Spring配置详解,Spring配置元信息详解,Spring配置大全及源码分析

文章目录

一、Spring都可以配置哪些元信息

  • Spring Bean 配置元信息 - BeanDefinition
  • Spring Bean 属性元信息 - PropertyValues
  • Spring 容器配置元信息
  • Spring 外部化配置元信息 - PropertySource
  • Spring Profile 元信息 - @Profile

二、Spring Bean 配置元信息

Spring Bean的配置元信息就是指的BeanDefinition接口。

BeanDefinition接口有三个基本的实现:

  • GenericBeanDefinition:通用型 BeanDefinition
  • RootBeanDefinition:无 Parent 的 BeanDefinition 或者合并后 BeanDefinition
  • AnnotatedBeanDefinition:注解标注的 BeanDefinition

1、GenericBeanDefinition

GenericBeanDefinition是标准的、通用的BeanDefinition,它继承了AbstractBeanDefinition,实现了parentName有关的方法,总体来说非常简单。

2、RootBeanDefinition

RootBeanDefinition是没有parent的BeanDefinition,或者有parent的BeanDefinition经过合并之后,会变成RootBeanDefinition。

关于Bean的合并我们介绍过(四、Spring BeanDefinition 合并阶段):
Spring Bean生命周期——从源码角度详解Spring Bean的生命周期(上)

RootBeanDefinition比GenericBeanDefinition属性更多、功能更强。里面包含了许多缓存相关的东西,可以提升一部分性能。

3、AnnotatedBeanDefinition

AnnotatedBeanDefinition实际上是一个接口,它用于处理关于注解的东西。

AnnotatedBeanDefinition中定义了可以获取AnnotationMetadata和MethodMetadata的方法。

AnnotationMetadata

AnnotationMetadata是一个接口,有两种实现,

一种是StandardAnnotationMetadata,基于java反射实现的。

另一种是AnnotationMetadataReadingVisitor,基于ASM的形式来操作的,就是相当于用字节码进行操作,通常来说这种方式性能更高一些,因为它不需要把整个类加载进来。不过在Spring5.2之后,这个类已经被弃用了,用SimpleAnnotationMetadataReadingVisitor来替代了,SimpleAnnotationMetadataReadingVisitor相对来说更加简单一点。

MethodMetadata

MethodMetadata里面的数据是可有可无的,里面保存着这个Bean定义的工厂方法的元数据

三、Spring Bean 属性元信息

1、Bean 属性元信息 - PropertyValues

Bean属性元信息,指的就是使用xml等配置的Bean的属性值,保存在MutablePropertyValues propertyValues中,在AbstractBeanDefinition中定义了propertyValues,并指定了set、get方法:

@Nullable
private MutablePropertyValues propertyValues;

public void setPropertyValues(MutablePropertyValues propertyValues) {
    
    
	this.propertyValues = propertyValues;
}

@Override
public MutablePropertyValues getPropertyValues() {
    
    
	if (this.propertyValues == null) {
    
    
		this.propertyValues = new MutablePropertyValues();
	}
	return this.propertyValues;
}

其中MutablePropertyValues实现了PropertyValues接口,支持迭代和stream。
MutablePropertyValues使用List存储了PropertyValue,用来保存元信息,我们可以称为这是一种组合模式,一个PropertyValues 中组合了多个PropertyValue,一个PropertyValues 对应了多个PropertyValue。

public class MutablePropertyValues implements PropertyValues, Serializable {
    
    

	private final List<PropertyValue> propertyValueList;

	// ...略

2、Bean 属性上下文存储 - AttributeAccessor

BeanDefinition继承了AttributeAccessor和BeanMetadataElement,所以BeanDefinition在此基础上做了扩展。

AttributeAccessor中定义了对attribute的增删改查操作,但是attribute对BeanDefinition是没有关联的。

我们上面了解过,BeanDefinition中存储了Bean的类型-className、Bean的属性PropertyValues,那么attribute是做什么的呢?

实际上,AttributeAccessor中的attribute是帮助我们存储一些附加属性的,比如说Bean的来源(xml、注解等)。

3、Bean 元信息元素 - BeanMetadataElement

BeanDefinition继承了AttributeAccessor和BeanMetadataElement,所以BeanDefinition在此基础上做了扩展。

BeanMetadataElement只有一个方法:

public interface BeanMetadataElement {
    
    

	@Nullable
	default Object getSource() {
    
    
		return null;
	}
}

getSource方法在BeanMetadataAttributeAccessor中进行了实现,而AbstractBeanDefinition又继承了BeanMetadataAttributeAccessor。

public class BeanMetadataAttributeAccessor extends AttributeAccessorSupport implements BeanMetadataElement {
    
    

	@Nullable
	private Object source;

	public void setSource(@Nullable Object source) {
    
    
		this.source = source;
	}

	@Override
	@Nullable
	public Object getSource() {
    
    
		return this.source;
	}

这里的Source就是为该元数据元素设置配置源Object。

4、代码实例

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.util.ObjectUtils;

/**
 * Bean 配置元信息示例
 */
public class BeanConfigurationMetadataDemo {
    
    

    public static void main(String[] args) {
    
    

        // BeanDefinition 的定义(声明)
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
        // 添加PropertyValues
        beanDefinitionBuilder.addPropertyValue("name", "李四");
        // 获取 AbstractBeanDefinition
        AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
        // 附加属性(不影响 Bean populate、initialize)
        beanDefinition.setAttribute("name", "张三");
        // 当前 BeanDefinition 来自于何方(辅助作用)
        beanDefinition.setSource(BeanConfigurationMetadataDemo.class);

        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        // 定义一个Bean初始化前的回调
        beanFactory.addBeanPostProcessor(new BeanPostProcessor() {
    
    
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
                if (ObjectUtils.nullSafeEquals("user", beanName) && User.class.equals(bean.getClass())) {
    
    
                    BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
                    if (BeanConfigurationMetadataDemo.class.equals(bd.getSource())) {
    
     // 通过 source 判断来
                        // 属性(存储)上下文
                        String name = (String) bd.getAttribute("name"); // 就是 "张三"
                        User user = (User) bean;
                        user.setName(name); // 手动设置为张三
                    }
                }
                return bean;
            }
        });

        // 注册 User 的 BeanDefinition
        beanFactory.registerBeanDefinition("user", beanDefinition);

        User user = beanFactory.getBean("user", User.class);

        // 如果没有上面的BeanPostProcessor,虽然定义了attribute,但是此处的name还是李四,并不是张三
        // 但是通过BeanPostProcessor处理过,此处就是张三了
        System.out.println(user);

    }
}

5、总结分析

Bean的PropertyValues是BeanDefinition中的非常重要的一个属性,它定义着Bean的配置属性值。

而AttributeAccessor和BeanMetadataElement,这两个其实只是用来辅助的帮助我们定义BeanDefinition,同时可以在一些Bean的生命周期阶段,辅助我们自定义BeanDefinition。

四、Spring 容器配置元信息

1、xml实例

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd"

        default-lazy-init="init">

    <context:annotation-config />
    <context:component-scan  base-package="com.test"/>
</beans>

2、Spring XML 配置元信息 - beans 元素相关(在<beans/>标签中配置的属性)

beans 元素属性 默认值 使用场景
profile null(留空) Spring Profiles 配置值(Spring3.1之后才支持)
default-lazy-init default 当 outter beans “default-lazy-init” 属性存在时,继承该值,否则为“false”
default-merge default 当 outter beans “default-merge” 属性存在时,继承该值,否则为“false”
default-autowire default 当 outter beans “default-autowire” 属性存在时,继承该值,否则为“no”
default-autowire-candidates null(留空) 默认 Spring Beans 名称 pattern
default-init-method null(留空) 默认 Spring Beans 自定义初始化方法
default-destroy-method null(留空) 默认 Spring Beans 自定义销毁方法

源码分析

Bean的解析代理器,就是BeanDefinitionParserDelegate类了。

它里面定义了大量的bean的xml属性定义,以及定义了beans的nameSpace:

public static final String BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans";

BeanDefinitionParserDelegate的populateDefaults方法就是给Bean中填充一些配置属性的值:

// org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#populateDefaults
protected void populateDefaults(DocumentDefaultsDefinition defaults, @Nullable DocumentDefaultsDefinition parentDefaults, Element root) {
    
    
	String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE);// default-lazy-init
	if (isDefaultValue(lazyInit)) {
    
    
		// Potentially inherited from outer <beans> sections, otherwise falling back to false.
		lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE);
	}
	defaults.setLazyInit(lazyInit);

	String merge = root.getAttribute(DEFAULT_MERGE_ATTRIBUTE); //default-merge
	if (isDefaultValue(merge)) {
    
    
		// Potentially inherited from outer <beans> sections, otherwise falling back to false.
		merge = (parentDefaults != null ? parentDefaults.getMerge() : FALSE_VALUE);
	}
	defaults.setMerge(merge);

	String autowire = root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE);
	if (isDefaultValue(autowire)) {
    
    
		// Potentially inherited from outer <beans> sections, otherwise falling back to 'no'.
		autowire = (parentDefaults != null ? parentDefaults.getAutowire() : AUTOWIRE_NO_VALUE);
	}
	defaults.setAutowire(autowire);

	if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) {
    
    
		defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE));
	}
	else if (parentDefaults != null) {
    
    
		defaults.setAutowireCandidates(parentDefaults.getAutowireCandidates());
	}

	if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) {
    
    
		defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE));
	}
	else if (parentDefaults != null) {
    
    
		defaults.setInitMethod(parentDefaults.getInitMethod());
	}

	if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) {
    
    
		defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE));
	}
	else if (parentDefaults != null) {
    
    
		defaults.setDestroyMethod(parentDefaults.getDestroyMethod());
	}

	defaults.setSource(this.readerContext.extractSource(root));
}

3、Spring XML 配置元信息 - 应用上下文相关

XML 元素 使用场景
<context:annotation-config /> 激活 Spring 注解驱动
<context:component-scan /> Spring @Component 以及自定义注解扫描
<context:load-time-weaver /> 激活 Spring LoadTimeWeaver
<context:mbean-export /> 暴露 Spring Beans 作为 JMX Beans
<context:mbean-server /> 将当前平台作为 MBeanServer
<context:property-placeholder /> 加载外部化配置资源作为 Spring 属性配置
<context:property-override /> 利用外部化配置资源覆盖 Spring 属性值

源码分析

xml对应xsd文件,XSD即XML结构定义, XML Schemas Definition。其本身就是用xml描述的, 且遵循xml语法规则。一份XML schema文件描述了XML文档的结构。

context相关的配置,都是基于https://www.springframework.org/schema/context/spring-context.xsd 这个xsd文件来定义的,但是现在Spring做了优化,可以不用访问远程文件,可以访问本地文件。

五、基于 XML 资源装载 Spring Bean 配置元信息

尽管现实中很少用到XML 配置Bean ,研究掌握原理还是有必要的。

Spring Bean 配置元信息:

XML 元素 使用场景
<beans:beans /> 单 XML 资源下的多个 Spring Beans 配置
<beans:bean /> 单个 Spring Bean 定义(BeanDefinition)配置
<beans:alias /> 为 Spring Bean 定义(BeanDefinition)映射别名
<beans:import /> 加载外部 Spring XML 配置资源

核心 API - XmlBeanDefinitionReader
• 资源 - Resource
• 底层 - BeanDefinitionDocumentReader
• XML 解析 - Java DOM Level 3 API
• BeanDefinition 解析 - BeanDefinitionParserDelegate
• BeanDefinition 注册 - BeanDefinitionRegistry

1、代码实例

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
String[] locations = {
    
    "META-INF/bean1.xml", "META-INF/bean2.xml"};
int beanNumbers = beanDefinitionReader.loadBeanDefinitions(locations);
System.out.println("已加载 BeanDefinition 数量:" + beanNumbers);
// 通过 Bean Id 和类型进行依赖查找
User user = beanFactory.getBean("user", User.class);
System.out.println(user);

2、源码分析

读取加载XML文件的底层实现就是XmlBeanDefinitionReader,我们一起来分析一下。

XmlBeanDefinitionReader实现了BeanDefinitionReader,而BeanDefinitionReader有多种实现(XmlBeanDefinitionReader、PropertiesBeanDefinitionReader、GroovyBeanDefinitionReader)。

XmlBeanDefinitionReader中对Bean的加载都是先读的先加载,按照读取的顺序进行加载的,也就是说bean的加载和xml中定义的顺序是一样的。但是bean是否先加载完成取决于依赖查找的时机和依赖注入的时机。

XmlBeanDefinitionReader有两种文本校验的方式,一种是DTD的方式,另一种是XSD的方式,默认是XSD的方式:

/**
 * Indicates that DTD validation should be used.
 */
public static final int VALIDATION_DTD = XmlValidationModeDetector.VALIDATION_DTD;

/**
 * Indicates that XSD validation should be used.
 */
public static final int VALIDATION_XSD = XmlValidationModeDetector.VALIDATION_XSD;

XmlBeanDefinitionReader只有一个构造方法,该构造方法需要传入一个BeanDefinitionRegistry:

public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
    
    
	super(registry);
}

我们可以先想一下,XmlBeanDefinitionReader处理xml文件肯定是先读,然后将xml文件转成Dom,然后再定义成BeanDefinition,然后注册到容器中。

// org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
		throws BeanDefinitionStoreException {
    
    

	try {
    
    
		Document doc = doLoadDocument(inputSource, resource);// Document 是w3c标准的,用于处理xml文件
		int count = registerBeanDefinitions(doc, resource);
		if (logger.isDebugEnabled()) {
    
    
			logger.debug("Loaded " + count + " bean definitions from " + resource);
		}
		return count;
	}
	// 。。。略
// org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    
    
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	int countBefore = getRegistry().getBeanDefinitionCount();
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 注册BeanDefinition
	return getRegistry().getBeanDefinitionCount() - countBefore; // 返回注册了多少个bean
}
// org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    
    
	this.readerContext = readerContext;
	doRegisterBeanDefinitions(doc.getDocumentElement());
}

此时来到了我们注册的主逻辑:

// org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
protected void doRegisterBeanDefinitions(Element root) {
    
    
	// Any nested <beans> elements will cause recursion in this method. In
	// order to propagate and preserve <beans> default-* attributes correctly,
	// keep track of the current (parent) delegate, which may be null. Create
	// the new (child) delegate with a reference to the parent for fallback purposes,
	// then ultimately reset this.delegate back to its original (parent) reference.
	// this behavior emulates a stack of delegates without actually necessitating one.
	BeanDefinitionParserDelegate parent = this.delegate; // 解析属性的默认值,上面我们介绍过
	this.delegate = createDelegate(getReaderContext(), root, parent);

	if (this.delegate.isDefaultNamespace(root)) {
    
    
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); // 获取profile,可以有多个值
		if (StringUtils.hasText(profileSpec)) {
    
    
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			// We cannot use Profiles.of(...) since profile expressions are not supported
			// in XML config. See SPR-12458 for details.
			if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
    
     // 与Environment进行比较
				if (logger.isDebugEnabled()) {
    
    
					logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
							"] not matching: " + getReaderContext().getResource());
				}
				return;
			}
		}
	}

	preProcessXml(root);
	parseBeanDefinitions(root, this.delegate); // 解析BeanDefinition
	postProcessXml(root);

	this.delegate = parent;
}

六、基于 Properties 资源装载 Spring Bean 配置元信息

Spring Bean 配置元信息:

Properties 属性名 使用场景
(class) Bean 类全称限定名
(abstract) 是否为抽象的 BeanDefinition
(parent) 指定 parent BeanDefinition 名称
(lazy-init) 是否为延迟初始化
(ref) 引用其他 Bean 的名称
(scope) 设置 Bean 的 scope 属性
${n} n 表示第 n+1 个构造器参数

Properties 方式与xml的方式是有很大差别的,Properties 方式是一种key-value的方式;xml方式是一种树形结构,可以通过层层嵌套的方式来更好的多元的组合。

Properties 方式官方并不推荐。

关于Properties定义bean的使用请移步(一、Spring Bean 元信息配置阶段):
Spring Bean生命周期——从源码角度详解Spring Bean的生命周期(上)

核心 API - PropertiesBeanDefinitionReader
• 资源
• 字节流 - Resource
• 字符流 - EncodedResouce
• 底层
• 存储 - java.util.Properties
• BeanDefinition 解析 - API 内部实现
• BeanDefinition 注册 - BeanDefinitionRegistry

1、代码实例

// 创建 IoC 底层容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 创建面向 Properties 资源的 BeanDefinitionReader 示例
PropertiesBeanDefinitionReader beanDefinitionReader = new PropertiesBeanDefinitionReader(beanFactory);
// Properties 资源加载默认通过 ISO-8859-1,实际存储 UTF-8
ResourceLoader resourceLoader = new DefaultResourceLoader();
// 通过指定的 ClassPath 获取 Resource 对象
Resource resource = resourceLoader.getResource("classpath:/META-INF/bean-definitions.properties");
// 转换成带有字符编码 EncodedResource 对象
EncodedResource encodedResource = new EncodedResource(resource, "UTF-8");
int beanDefinitionsCount = beanDefinitionReader.loadBeanDefinitions(encodedResource);
System.out.println(String.format("已记载 %d 个 BeanDefinition\n", beanDefinitionsCount));
// 通过依赖查找获取 User Bean
User user = beanFactory.getBean("user", User.class);
System.out.println(user);

2、源码分析

Properties的方式是由PropertiesBeanDefinitionReader进行解析的,Spring在5.3之后的版本,已经将Properties的方式弃用了。

关于如何配置,在PropertiesBeanDefinitionReader的doc中已经有了:


/**
 * Bean definition reader for a simple properties format.
 *
 * <p>Provides bean definition registration methods for Map/Properties and
 * ResourceBundle. Typically applied to a DefaultListableBeanFactory.
 *
 * <p><b>Example:</b>
 *
 * <pre class="code">
 * employee.(class)=MyClass       // bean is of class MyClass
 * employee.(abstract)=true       // this bean can't be instantiated directly
 * employee.group=Insurance       // real property
 * employee.usesDialUp=false      // real property (potentially overridden)
 *
 * salesrep.(parent)=employee     // derives from "employee" bean definition
 * salesrep.(lazy-init)=true      // lazily initialize this singleton bean
 * salesrep.manager(ref)=tony     // reference to another bean
 * salesrep.department=Sales      // real property
 *
 * techie.(parent)=employee       // derives from "employee" bean definition
 * techie.(scope)=prototype       // bean is a prototype (not a shared instance)
 * techie.manager(ref)=jeff       // reference to another bean
 * techie.department=Engineering  // real property
 * techie.usesDialUp=true         // real property (overriding parent value)
 *
 * ceo.$0(ref)=secretary          // inject 'secretary' bean as 0th constructor arg
 * ceo.$1=1000000                 // inject value '1000000' at 1st constructor arg
 * </pre>
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Rob Harrop
 * @since 26.11.2003
 * @see DefaultListableBeanFactory
 */
public class PropertiesBeanDefinitionReader extends AbstractBeanDefinitionReader {
    
    

这里就不做过多分析了。

七、基于Groovy资源装载Spring BeanDefinition

这种方式更不常见,后续有机会再出相关文章介绍这种方式。

八、基于 Java 注解装载 Spring Bean 配置元信息

Spring注解方式装配是Spring的重点,后续单独拿出来介绍。

1、Spring 模式注解

Spring 注解 场景说明 起始版本
@Repository 数据仓储模式注解 2.0
@Component 通用组件模式注解 2.5
@Service 服务模式注解 2.5
@Controller Web 控制器模式注解 2.5
@Configuration 配置类模式注解 3.0

2、Spring Bean 定义注解

Spring 注解 场景说明 起始版本
@Bean 替换 XML 元素<bean> 3.0
@DependsOn 替代 XML 属性 <bean depends-on=“…”/> 3.0
@Lazy 替代 XML 属性 <bean lazy-init="true falses" />
@Primary 替换 XML 元素 <bean primary="true false" />
@Role 替换 XML 元素 <bean role=“…” /> 3.1
@Lookup 替代 XML 属性 <bean lookup-method=“…”> 4.1

3、Spring Bean 依赖注入注解

Spring 注解 场景说明 起始版本
@Autowired Bean 依赖注入,支持多种依赖查找方式 2.5
@Qualifier 细粒度的 @Autowired 依赖查找 2.5
Java 注解 场景说明 起始版本
@Resource 类似于 @Autowired 2.5
@Inject 类似于 @Autowired 2.5

源码:AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor

4、Spring Bean 条件装配注解

Spring 注解 场景说明 起始版本
@Profile 配置化条件装配 3.1
@Conditional 编程条件装配 4.0

其中,@Profile在Spring4.0后做了一点变化,基于@Conditional进行实现了。

5、Spring Bean 生命周期回调注解

Spring 注解 场景说明 起始版本
@PostConstruct 替换 XML 元素 <bean init-method=“…” /> 或InitializingBean 2.5
@PreDestroy 替换 XML 元素 <bean destroy-method=“…” /> 或 DisposableBean 2.5

6、注解 注册 BeanDefinition 解析与注册源码分析

Java注解注册BeanDefinition的核心API接口就是AnnotatedBeanDefinitionReader,这个接口跟xml、properties加载BeanDefinition不同,并没有实现BeanDefinitionReader接口,因为它的资源并不是一个resource资源,它的资源其实是java.lang.Class。

底层:
• 条件评估 - ConditionEvaluator
• Bean 范围解析 - ScopeMetadataResolver
• BeanDefinition 解析 - 内部 API 实现
• BeanDefinition 处理 - AnnotationConfigUtils.processCommonDefinitionAnnotations
• BeanDefinition 注册 - BeanDefinitionRegistry

我们一起来看一下AnnotatedBeanDefinitionReader 的源码:

public class AnnotatedBeanDefinitionReader {
    
    
	// BeanDefinition注册工厂
	private final BeanDefinitionRegistry registry;
	// Bean命名的工厂,默认为AnnotationBeanNameGenerator
	private BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;
	// Scope处理
	private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
	// Condition评估,shouldSkip方法判断Condition条件是否予以跳过
	private ConditionEvaluator conditionEvaluator;

AnnotatedBeanDefinitionReader的构造方法允许传一个BeanDefinitionRegistry ,或者多传一个Environment:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
    
    
	this(registry, getOrCreateEnvironment(registry)); // 返回Environment对象
}

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    
    
	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
	Assert.notNull(environment, "Environment must not be null");
	this.registry = registry;
	this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
	AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

AnnotatedBeanDefinitionReader的register方法有很多重载方法,可以注册多个beanClass,最终执行的是doRegisterBean方法进行bean的注册:

// org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
		@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
		@Nullable BeanDefinitionCustomizer[] customizers) {
    
    

	// 创建基于注解的BeanDefinition
	AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
	// 条件评估是否需要跳过
	if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
    
    
		return;
	}

	abd.setInstanceSupplier(supplier);
	ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
	abd.setScope(scopeMetadata.getScopeName());
	String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

	// 对一些BeanDefinition的属性进行设置
	AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
	if (qualifiers != null) {
    
    
		for (Class<? extends Annotation> qualifier : qualifiers) {
    
    
			if (Primary.class == qualifier) {
    
    
				abd.setPrimary(true);
			}
			else if (Lazy.class == qualifier) {
    
    
				abd.setLazyInit(true);
			}
			else {
    
    
				abd.addQualifier(new AutowireCandidateQualifier(qualifier));
			}
		}
	}
	if (customizers != null) {
    
    
		for (BeanDefinitionCustomizer customizer : customizers) {
    
    
			customizer.customize(abd);
		}
	}

	BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
	// 选择代理方式
	definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
	BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); // 注册BeanDefinition
}

九、基于 XML 资源装载 Spring IoC 容器配置元信息

Spring IoC 容器相关 XML 配置:

命名空间 所属模块 Schema 资源 URL
beans spring-beans https://www.springframework.org/schema/beans/spring-beans.xsd
context spring-context https://www.springframework.org/schema/context/spring-context.xsd
aop spring-aop https://www.springframework.org/schema/aop/spring-aop.xsd
tx spring-tx https://www.springframework.org/schema/tx/spring-tx.xsd
util spring-beans https://www.springframework.org/schema/util/spring-util.xsd
tool spring-beans https://www.springframework.org/schema/tool/spring-tool.xsd

1、源码分析

(1)在spring-beans包的META-INF中有个spring.handlers文件,里面标注了schema与class的对应关系:

http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

我们一起分析一下UtilNamespaceHandler:

init方法里初始化的Parser就是用来解析xml的元素信息

// org.springframework.beans.factory.xml.UtilNamespaceHandler#init
@Override
public void init() {
    
    
	registerBeanDefinitionParser("constant", new ConstantBeanDefinitionParser());
	registerBeanDefinitionParser("property-path", new PropertyPathBeanDefinitionParser());
	registerBeanDefinitionParser("list", new ListBeanDefinitionParser());
	registerBeanDefinitionParser("set", new SetBeanDefinitionParser());
	registerBeanDefinitionParser("map", new MapBeanDefinitionParser());
	registerBeanDefinitionParser("properties", new PropertiesBeanDefinitionParser());
}

(2)在spring-beans包的META-INF中有个spring.schemas文件,里面存放着schemas与本地文件的关联关系:

http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-4.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-4.1.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-4.2.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans-4.3.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
http\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.1.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
http\://www.springframework.org/schema/tool/spring-tool-3.2.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
http\://www.springframework.org/schema/tool/spring-tool-4.0.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
http\://www.springframework.org/schema/tool/spring-tool-4.1.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
http\://www.springframework.org/schema/tool/spring-tool-4.2.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
http\://www.springframework.org/schema/tool/spring-tool-4.3.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util.xsd
http\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util.xsd
http\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util.xsd
http\://www.springframework.org/schema/util/spring-util-3.1.xsd=org/springframework/beans/factory/xml/spring-util.xsd
http\://www.springframework.org/schema/util/spring-util-3.2.xsd=org/springframework/beans/factory/xml/spring-util.xsd
http\://www.springframework.org/schema/util/spring-util-4.0.xsd=org/springframework/beans/factory/xml/spring-util.xsd
http\://www.springframework.org/schema/util/spring-util-4.1.xsd=org/springframework/beans/factory/xml/spring-util.xsd
http\://www.springframework.org/schema/util/spring-util-4.2.xsd=org/springframework/beans/factory/xml/spring-util.xsd
http\://www.springframework.org/schema/util/spring-util-4.3.xsd=org/springframework/beans/factory/xml/spring-util.xsd
http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util.xsd
https\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
https\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
https\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
https\://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
https\://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
https\://www.springframework.org/schema/beans/spring-beans-4.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
https\://www.springframework.org/schema/beans/spring-beans-4.1.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
https\://www.springframework.org/schema/beans/spring-beans-4.2.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
https\://www.springframework.org/schema/beans/spring-beans-4.3.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
https\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
https\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
https\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
https\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
https\://www.springframework.org/schema/tool/spring-tool-3.1.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
https\://www.springframework.org/schema/tool/spring-tool-3.2.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
https\://www.springframework.org/schema/tool/spring-tool-4.0.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
https\://www.springframework.org/schema/tool/spring-tool-4.1.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
https\://www.springframework.org/schema/tool/spring-tool-4.2.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
https\://www.springframework.org/schema/tool/spring-tool-4.3.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
https\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool.xsd
https\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util.xsd
https\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util.xsd
https\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util.xsd
https\://www.springframework.org/schema/util/spring-util-3.1.xsd=org/springframework/beans/factory/xml/spring-util.xsd
https\://www.springframework.org/schema/util/spring-util-3.2.xsd=org/springframework/beans/factory/xml/spring-util.xsd
https\://www.springframework.org/schema/util/spring-util-4.0.xsd=org/springframework/beans/factory/xml/spring-util.xsd
https\://www.springframework.org/schema/util/spring-util-4.1.xsd=org/springframework/beans/factory/xml/spring-util.xsd
https\://www.springframework.org/schema/util/spring-util-4.2.xsd=org/springframework/beans/factory/xml/spring-util.xsd
https\://www.springframework.org/schema/util/spring-util-4.3.xsd=org/springframework/beans/factory/xml/spring-util.xsd
https\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util.xsd

(3)类似的,在aop包、context包等中,也有以上这些文件,用于做映射的。

十、基于 Java 注解装载 Spring IoC 容器配置元信息

Spring IoC 容器装配注解:

Spring 注解 场景说明 起始版本
@ImportResource 替换 XML 元素 <import> 3.0
@Import 导入 Configuration Class 3.0
@ComponentScan 扫描指定 package 下标注 Spring 模式注解的类 3.1

Spring IoC 配属属性注解:

Spring 注解 场景说明 起始版本
@PropertySource 配置属性抽象 PropertySource 注解 3.1
@PropertySources @PropertySource 集合注解 4.0

1、代码实例


/**
 * 基于 Java 注解 Spring IoC 容器元信息配置示例
 */
// 将当前类作为 Configuration Class
@ImportResource("classpath:/META-INF/beans.xml")
@Import(User.class)
// @PropertyRecource 并非 BeanDefinition生命注解,它只是为 Spring ApplicationContext 提供配置来源,如帮助 @Value 提供属性来源,或者 XML 配置属性中占位符的替换。
@PropertySource("classpath:/META-INF/user-bean-definitions.properties") 
// Java 8+ @Repeatable 支持,可以使用多个@PropertySource
@PropertySource("classpath:/META-INF/user-bean-definitions2.properties")
// @PropertySources(@PropertySource(...))
public class AnnotatedSpringIoCContainerMetadataConfigurationDemo {
    
    

    @Bean
    public User configuredUser(@Value("${user.id}") Long id, @Value("${user.name}") String name) {
    
    
        User user = new User();
        user.setId(id);
        user.setName(name);
        return user;
    }

    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 注册当前类作为 Configuration Class
        context.register(AnnotatedSpringIoCContainerMetadataConfigurationDemo.class);
        // 启动 Spring 应用上下文
        context.refresh();
        // beanName 和 bean 映射
        Map<String, User> usersMap = context.getBeansOfType(User.class);
        for (Map.Entry<String, User> entry : usersMap.entrySet()) {
    
    
            System.out.printf("User Bean name : %s , content : %s \n", entry.getKey(), entry.getValue());
        }
        // 关闭 Spring 应用上下文
        context.close();
    }
}

十一、基于 Extensible XML authoring 扩展 Spring XML 元素

比如 MyBatis,Dubbo 等整合 Spring 的框架,需要对XML文件进行扩展,咱们一起聊聊如何进行扩展XML元素。

1、Spring XML扩展

(1)编写 XML Schema 文件:定义 XML 结构

spring是在类的同目录下定义的xsd文件,我们也这样做,在resources目录下定义同包名的xsd文件:

users.xsd:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://com.cxf.test/schema/users"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://com.cxf.test/schema/users">

    <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>

    <!-- 定义 User 类型(复杂类型) -->
    <xsd:complexType name="User">
        <xsd:attribute name="id" type="xsd:long" use="required"/>
        <xsd:attribute name="name" type="xsd:string" use="required"/>
        <xsd:attribute name="city" type="City"/>
    </xsd:complexType>

    <!-- 定义 City 类型(简单类型,枚举) -->
    <xsd:simpleType name="City">
        <xsd:restriction base="xsd:string">
            <xsd:enumeration value="BEIJING"/>
            <xsd:enumeration value="SHENZHEN"/>
            <xsd:enumeration value="SHANGHAI"/>
        </xsd:restriction>
    </xsd:simpleType>

    <!-- 定义 user 元素 -->
    <xsd:element name="user" type="User"/>
</xsd:schema>

定义users-context.xml文件,进行bean的定义:

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:users="http://com.cxf.test/schema/users"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://com.cxf.test/schema/users
        http://com.cxf.test/schema/users.xsd
">
    <users:user id="1" name="张三" city="BEIJING"/>
</beans>

(2)自定义 NamespaceHandler 实现:命名空间绑定

import org.springframework.beans.factory.xml.NamespaceHandler;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

/**
 * "users.xsd" {@link NamespaceHandler} 实现
 */
public class UsersNamespaceHandler extends NamespaceHandlerSupport {
    
    

    @Override
    public void init() {
    
    
        // 将 "user" 元素注册对应的 BeanDefinitionParser 实现
        registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
    }
}

在META-INF目录下定义spring.handlers:

## 定义 namespace 与 NamespaceHandler 的映射
http\://com.cxf.test/schema/users=com.cxf.test.config.UsersNamespaceHandler

(3)自定义 BeanDefinitionParser 实现:XML 元素与 BeanDefinition 解析

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

/**
 * "user" 元素的 {@link BeanDefinitionParser} 实现
 */
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
    
    

    @Override
    protected Class<?> getBeanClass(Element element) {
    
    
        return User.class;
    }

    @Override
    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
    
    
        setPropertyValue("id", element, builder);
        setPropertyValue("name", element, builder);
        setPropertyValue("city", element, builder);
    }

    private void setPropertyValue(String attributeName, Element element, BeanDefinitionBuilder builder) {
    
    
        String attributeValue = element.getAttribute(attributeName);
        if (StringUtils.hasText(attributeValue)) {
    
    
            builder.addPropertyValue(attributeName, attributeValue); // -> <property name="" value=""/>
        }
    }
}

(4)注册 XML 扩展:命名空间与 XML Schema 映射

在META-INF目录下定义spring.schemas:

http\://com.cxf.test/schema/users.xsd = com/cxf/test/config/users.xsd

(5)执行实例

// 创建 IoC 底层容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 创建 XML 资源的 BeanDefinitionReader
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// 记载 XML 资源
reader.loadBeanDefinitions("META-INF/users-context.xml");
// 获取 User Bean 对象
User user = beanFactory.getBean(User.class);
System.out.println(user);

2、源码分析

spring容器启动的时候会调用AbstractApplicationContext的refresh()方法,
再执行AbstractApplicationContext的obtainFreshBeanFactory方法,
再执行AbstractRefreshableApplicationContext的refreshBeanFactory方法,
再执行AbstractXmlApplicationContext的loadBeanDefinitions(DefaultListableBeanFactory)方法,
再执行AbstractXmlApplicationContext的loadBeanDefinitions(XmlBeanDefinitionReader)方法,
再执行AbstractBeanDefinitionReader的loadBeanDefinitions(java.lang.String…)方法,
再执行AbstractBeanDefinitionReader的loadBeanDefinitions(java.lang.String)方法,
再执行AbstractBeanDefinitionReader的loadBeanDefinitions(String, Set<Resource>)方法,
再执行AbstractBeanDefinitionReader的loadBeanDefinitions(org.springframework.core.io.Resource…)方法,
再执行XmlBeanDefinitionReader的loadBeanDefinitions(org.springframework.core.io.Resource)方法,
再执行XmlBeanDefinitionReader#loadBeanDefinitions(EncodedResource)方法,
再执行XmlBeanDefinitionReader的doLoadBeanDefinitions方法,
再执行XmlBeanDefinitionReader的registerBeanDefinitions方法,
再执行DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法,
再执行DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions方法,
再执行DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法:

// org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    
    
	if (delegate.isDefaultNamespace(root)) {
    
    
		NodeList nl = root.getChildNodes();
		for (int i = 0; i < nl.getLength(); i++) {
    
    
			Node node = nl.item(i);
			if (node instanceof Element) {
    
    
				Element ele = (Element) node;
				if (delegate.isDefaultNamespace(ele)) {
    
    
					parseDefaultElement(ele, delegate); // 解析默认元素(beans里面的)
				}
				else {
    
    
					delegate.parseCustomElement(ele); // 读取自定义的xml配置元素
				}
			}
		}
	}
	else {
    
    
		delegate.parseCustomElement(root);
	}
}

再执行BeanDefinitionParserDelegate的parseCustomElement(org.w3c.dom.Element)方法,
再执行BeanDefinitionParserDelegate的parseCustomElement(Element, BeanDefinition)方法,

// org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
    
    
	// 获取nameSpaceURI(通过user的前缀,找到namespaceURI)
	String namespaceUri = getNamespaceURI(ele);
	if (namespaceUri == null) {
    
    
		return null;
	}
	// 拿到我们的NamespaceHandler(通过namespaceURI和spring.handlers中的配置,找到NamespaceHandler)
	NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
	if (handler == null) {
    
    
		error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
		return null;
	}
	return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

这里最后会执行NamespaceHandlerSupport的parse方法,
再执行AbstractBeanDefinitionParser的parse方法,
再执行AbstractSingleBeanDefinitionParser的parseInternal方法:

// org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#parseInternal
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
    
    
	BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
	String parentName = getParentName(element);
	if (parentName != null) {
    
    
		builder.getRawBeanDefinition().setParentName(parentName);
	}
	Class<?> beanClass = getBeanClass(element);
	if (beanClass != null) {
    
    
		builder.getRawBeanDefinition().setBeanClass(beanClass);
	}
	else {
    
    
		String beanClassName = getBeanClassName(element);
		if (beanClassName != null) {
    
    
			builder.getRawBeanDefinition().setBeanClassName(beanClassName);
		}
	}
	builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
	BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
	if (containingBd != null) {
    
    
		// Inner bean definition must receive same scope as containing bean.
		builder.setScope(containingBd.getScope());
	}
	if (parserContext.isDefaultLazyInit()) {
    
    
		// Default-lazy-init applies to custom bean definitions as well.
		builder.setLazyInit(true);
	}
	// 执行我们自定义的Parser的doParse方法
	doParse(element, parserContext, builder);
	return builder.getBeanDefinition();
}

3、分析总结

核心流程:BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, BeanDefinition)
(1)获取 namespace
(2)通过 namespace 解析 NamespaceHandler
(3)构造 ParserContext
(4)解析元素,获取 BeanDefinintion

Extensible XML authoring 虽然扩展性较强,但是缺点也是很明显的:
• 高复杂度:开发人员需要熟悉 XML Schema,spring.handlers,spring.schemas 以及 Spring API
• 嵌套元素支持较弱:通常需要使用方法递归或者其嵌套解析的方式处理嵌套(子)元素
• XML 处理性能较差:Spring XML 基于 DOM Level 3 API 实现,该 API 便于理解,然而性能较差
• XML 框架移植性差:很难适配高性能和便利性的 XML 框架,如 JAXB

十二、基于 Properties 资源装载外部化配置

1、注解驱动

注解驱动涉及两个关键注解:
@org.springframework.context.annotation.PropertySource
@org.springframework.context.annotation.PropertySources

2、API编程

API编程涉及两个关键API:
org.springframework.core.env.PropertySource
org.springframework.core.env.PropertySources

3、代码实例

取properties中的配置,有可能会被Environment中的配置覆盖,每次加载Properties资源都会在Environment中的PropertySources中添加一个PropertySource。

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

// 扩展 Environment 中的 PropertySources
// 添加 PropertySource 操作必须在 refresh 方法之前完成
Map<String, Object> propertiesSource = new HashMap<>();
propertiesSource.put("user.name", "cxf");
org.springframework.core.env.PropertySource propertySource = new MapPropertySource("first-property-source", propertiesSource);
context.getEnvironment().getPropertySources().addFirst(propertySource);

// 注册当前类作为 Configuration Class
context.register(PropertySourceDemo.class);
// 启动 Spring 应用上下文
context.refresh();
// beanName 和 bean 映射
Map<String, User> usersMap = context.getBeansOfType(User.class);
for (Map.Entry<String, User> entry : usersMap.entrySet()) {
    
    
    System.out.printf("User Bean name : %s , content : %s \n", entry.getKey(), entry.getValue());
}
System.out.println(context.getEnvironment().getPropertySources());
// 关闭 Spring 应用上下文
context.close();

十三、基于 YAML 资源装载外部化配置

springframework中其实对YAML方式支持的并不是很完善,它只是针对YMAL的方式做了一个简单的支持。

但是在springboot中对YAML做了一些完善。

主要API:
org.springframework.beans.factory.config.YamlProcessor
• org.springframework.beans.factory.config.YamlMapFactoryBean
• org.springframework.beans.factory.config.YamlPropertiesFactoryBean

1、xml方式读取yaml实例

<bean id="yamlMap" class="com.cxf.test.config.YamlMapFactoryBean" >
   <!-- 关联 user.yaml 配置,配置user的信息 -->
   <property name="resources" value="classpath:/META-INF/user.yaml" />
</bean>
// 创建 IoC 底层容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 创建 XML 资源的 BeanDefinitionReader
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// 记载 XML 资源
reader.loadBeanDefinitions("classpath:/META-INF/yaml-property-source-context.xml");
// 获取 Map YAML 对象
Map<String, Object> yamlMap = beanFactory.getBean("yamlMap", Map.class);
System.out.println(yamlMap);

2、注解方式读取yaml实例

import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import java.io.IOException;
import java.util.Properties;

/**
 * YAML 格式的 {@link PropertySourceFactory} 实现
 */
public class YamlPropertySourceFactory implements PropertySourceFactory {
    
    
    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
    
    
        YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean();
        yamlPropertiesFactoryBean.setResources(resource.getResource());
        Properties yamlProperties = yamlPropertiesFactoryBean.getObject();
        return new PropertiesPropertySource(name, yamlProperties);
    }
}

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

/**
 * 基于 Java 注解的 YAML 外部化配置示例
 */
@PropertySource(
        name = "yamlPropertySource",
        value = "classpath:/META-INF/user.yaml",
        factory = YamlPropertySourceFactory.class)
public class AnnotatedYamlPropertySourceDemo {
    
    

    @Bean
    public User user(@Value("${user.id}") Long id, @Value("${user.name}") String name, @Value("${user.city}") City city) {
    
    
        User user = new User();
        user.setId(id);
        user.setName(name);
        user.setCity(city);
        return user;
    }

    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 注册当前类作为 Configuration Class
        context.register(AnnotatedYamlPropertySourceDemo.class);
        // 启动 Spring 应用上下文
        context.refresh();
        // 获取 Map YAML 对象
        User user = context.getBean("user", User.class);
        System.out.println(user);
        // 关闭 Spring 应用上下文
        context.close();
    }
}

参考资料

极客时间-小马哥《小马哥讲Spring核心编程思想》

猜你喜欢

转载自blog.csdn.net/A_art_xiang/article/details/128637493