文章目录

  • 一、Spring都可以配置哪些元信息
  • 二、Spring Bean 配置元信息
    • 1、GenericBeanDefinition
    • 2、RootBeanDefinition
    • 3、AnnotatedBeanDefinition
      • AnnotationMetadata
      • MethodMetadata
  • 三、Spring Bean 属性元信息
    • 1、Bean 属性元信息 - PropertyValues
    • 2、Bean 属性上下文存储 - AttributeAccessor
    • 3、Bean 元信息元素 - BeanMetadataElement
    • 4、代码实例
    • 5、总结分析
  • 四、Spring 容器配置元信息
    • 1、xml实例
    • 2、Spring XML 配置元信息 - beans 元素相关(在\标签中配置的属性)
      • 源码分析
    • 3、Spring XML 配置元信息 - 应用上下文相关
      • 源码分析
  • 五、基于 XML 资源装载 Spring Bean 配置元信息
    • 1、代码实例
    • 2、源码分析
  • 六、基于 Properties 资源装载 Spring Bean 配置元信息
    • 1、代码实例
    • 2、源码分析
  • 七、基于Groovy资源装载Spring BeanDefinition
  • 八、基于 Java 注解装载 Spring Bean 配置元信息
    • 1、Spring 模式注解
    • 2、Spring Bean 定义注解
    • 3、Spring Bean 依赖注入注解
    • 4、Spring Bean 条件装配注解
    • 5、Spring Bean 生命周期回调注解
    • 6、注解 注册 BeanDefinition 解析与注册源码分析
  • 九、基于 XML 资源装载 Spring IoC 容器配置元信息
    • 1、源码分析
  • 十、基于 Java 注解装载 Spring IoC 容器配置元信息
    • 1、代码实例
  • 十一、基于 Extensible XML authoring 扩展 Spring XML 元素
    • 1、Spring XML扩展
      • (1)编写 XML Schema 文件:定义 XML 结构
      • (2)自定义 NamespaceHandler 实现:命名空间绑定
      • (3)自定义 BeanDefinitionParser 实现:XML 元素与 BeanDefinition 解析
      • (4)注册 XML 扩展:命名空间与 XML Schema 映射
      • (5)执行实例
    • 2、源码分析
    • 3、分析总结
  • 十二、基于 Properties 资源装载外部化配置
    • 1、注解驱动
    • 2、API编程
    • 3、代码实例
  • 十三、基于 YAML 资源装载外部化配置
    • 1、xml方式读取yaml实例
    • 2、注解方式读取yaml实例
  • 参考资料

一、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 {@Nullabledefault Object getSource() {return null;}
}

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

public class BeanMetadataAttributeAccessor extends AttributeAccessorSupport implements BeanMetadataElement {@Nullableprivate Object source;public void setSource(@Nullable Object source) {this.source = source;}@Override@Nullablepublic 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);// 添加PropertyValuesbeanDefinitionBuilder.addPropertyValue("name", "李四");// 获取 AbstractBeanDefinitionAbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();// 附加属性(不影响 Bean populate、initialize)beanDefinition.setAttribute("name", "张三");// 当前 BeanDefinition 来自于何方(辅助作用)beanDefinition.setSource(BeanConfigurationMetadataDemo.class);DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();// 定义一个Bean初始化前的回调beanFactory.addBeanPostProcessor(new BeanPostProcessor() {@Overridepublic 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 的 BeanDefinitionbeanFactory.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"?>
<beansxmlns="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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://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-initif (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-mergeif (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)); // 注册BeanDefinitionreturn 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); // 解析BeanDefinitionpostProcessXml(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命名的工厂,默认为AnnotationBeanNameGeneratorprivate 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) {// 创建基于注解的BeanDefinitionAnnotatedGenericBeanDefinition 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 {@Beanpublic 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 Classcontext.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"?>
<beansxmlns="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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://com.cxf.test/schema/usershttp://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 {@Overridepublic 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 {@Overrideprotected Class<?> getBeanClass(Element element) {return User.class;}@Overrideprotected 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 {@Overridepublic 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 {@Beanpublic 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 Classcontext.register(AnnotatedYamlPropertySourceDemo.class);// 启动 Spring 应用上下文context.refresh();// 获取 Map YAML 对象User user = context.getBean("user", User.class);System.out.println(user);// 关闭 Spring 应用上下文context.close();}
}

参考资料

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

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

  1. Spring Ioc 源码分析(一)--Spring Ioc容器的加载

    1.目标:熟练使用spring,并分析其源码,了解其中的思想.这篇主要介绍spring ioc 容器的加载 2.前提条件:会使用debug 3.源码分析方法:Intellj idea debug 模式 ...

  2. 【Swagger】配置信息详解(涉及源码分析)

    先来说说 Swagger 有什么用,相较于使用 markdown 或者 word 写接口文档,Swagger 自动生成 API 文档,然后在 web 端暴露,并且 API 文档与 API 定义同步更新 ...

  3. Spring源码分析之Bean的创建过程详解

    前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...

  4. beaninfo详解源码解析 java_【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  5. Cubemx与HAL库系列教程|系统时钟配置详解及源码分析

    STM32时钟系统简介 STM32种类繁多,时钟系统也不尽相同,但基本的还是大差不差,今日小飞哥就F1系列的MCU简单聊一聊STM32的时钟系统 1.时钟种类介绍: 先来看一看时钟树图,包含了整个系统 ...

  6. 【spring源码】源码分析

    [spring源码]源码分析 (一)mac版idea引入spring源码 (二)spring的学习流程 (三)spring源码分析 [1]refresh()方法概览(AbstractApplicati ...

  7. Spring Security 源码分析:Spring Security 授权过程

    Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring I ...

  8. Spring Cloud部分源码分析Eureka,Ribbon,Feign,Zuul

    Eureka SpringCloud Eureka使用NetFlix Eureka来实现的,它包括了服务端组件和客户端组件,并且都是用java 编写的. Eureka服务端就是服务注册中心, Eure ...

  9. Spring Security(四) —— 核心过滤器源码分析

    摘要: 原创出处 https://www.cnkirito.moe/spring-security-4/ 「老徐」欢迎转载,保留摘要,谢谢! 4 过滤器详解 前面的部分,我们关注了Spring Sec ...

最新文章

  1. UnicodeDecodeError: ‘utf-8‘ codec can‘t decode byte 0xb0 in position 0: invalid start byte
  2. Flex DataGrid设置不同行高度和自动换行
  3. 建立稳定安全的SSH隧道
  4. spring集成 JedisCluster 连接 redis3.0 集群
  5. Flask之threading.loacl方法
  6. MVC传递Model
  7. Array.forEach
  8. 图嵌入综述 (arxiv 1709.07604) 译文第三章
  9. Java继承,接口,抽象类
  10. 20201219:力扣219周周赛题解
  11. 浅谈Event Loop
  12. camtasia喀秋莎2022(屏幕录像课件制作工具)
  13. arcgis python计算面积_如何在ARCMAP里面计算面积
  14. iOS 热敏打印机打印位图
  15. Windows蓝屏代码集合
  16. python编程软件手机版下载_清辅音p的发音方法
  17. 用HTML+CSS+JS做一个漂亮的个人网页
  18. win10环境下Android SDK下载安装及配置教程----Android SDK安装
  19. vue中在一个函数中调用另外一个函数
  20. 用python画小星星

热门文章

  1. 32位和64位与虚拟地址之间和字节数的问题
  2. SE11 创建搜索帮助
  3. 计算机休眠快还是关机快,你应该关机,睡眠还是休眠你的笔记本电脑? | MOS86...
  4. 美国大学生数学建模竞赛数据常用网站-数学建模(十九)
  5. cocos2d-x中的Jni使用(C++与Andriod方法互调)
  6. 64位系统运行报错:1%不是有效的win32应用程序解决
  7. 大宇资讯java游戏_30年后重新出发:大宇单机游戏《轩辕剑柒》试玩
  8. 调用caffe2中pre-trained model(squeezenet)
  9. 【人工智能】专家系统简答
  10. python通过SMIELS查询CAS号