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

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

文章目录

一、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 元素属性默认值使用场景
profilenull留空Spring Profiles 配置值Spring3.1之后才支持
default-lazy-initdefault当 outter beans “default-lazy-init” 属性存在时继承该值否则为“false”
default-mergedefault当 outter beans “default-merge” 属性存在时继承该值否则为“false”
default-autowiredefault当 outter beans “default-autowire” 属性存在时继承该值否则为“no”
default-autowire-candidatesnull留空默认 Spring Beans 名称 pattern
default-init-methodnull留空默认 Spring Beans 自定义初始化方法
default-destroy-methodnull留空默认 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="truefalses" />
@Primary替换 XML 元素 <bean primary="truefalse" />
@Role替换 XML 元素 <bean role=“…” />3.1
@Lookup替代 XML 属性 <bean lookup-method=“…”>4.1

3、Spring Bean 依赖注入注解

Spring 注解场景说明起始版本
@AutowiredBean 依赖注入支持多种依赖查找方式2.5
@Qualifier细粒度的 @Autowired 依赖查找2.5
Java 注解场景说明起始版本
@Resource类似于 @Autowired2.5
@Inject类似于 @Autowired2.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=“…” /> 或InitializingBean2.5
@PreDestroy替换 XML 元素 <bean destroy-method=“…” /> 或 DisposableBean2.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
beansspring-beanshttps://www.springframework.org/schema/beans/spring-beans.xsd
contextspring-contexthttps://www.springframework.org/schema/context/spring-context.xsd
aopspring-aophttps://www.springframework.org/schema/aop/spring-aop.xsd
txspring-txhttps://www.springframework.org/schema/tx/spring-tx.xsd
utilspring-beanshttps://www.springframework.org/schema/util/spring-util.xsd
toolspring-beanshttps://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 Class3.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 元素

比如 MyBatisDubbo 等整合 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 Schemaspring.handlersspring.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核心编程思想》

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Spring