Spring配置详解,Spring配置元信息详解,Spring配置大全及源码分析
文章目录
- 一、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配置大全及源码分析相关推荐
- Spring Ioc 源码分析(一)--Spring Ioc容器的加载
1.目标:熟练使用spring,并分析其源码,了解其中的思想.这篇主要介绍spring ioc 容器的加载 2.前提条件:会使用debug 3.源码分析方法:Intellj idea debug 模式 ...
- 【Swagger】配置信息详解(涉及源码分析)
先来说说 Swagger 有什么用,相较于使用 markdown 或者 word 写接口文档,Swagger 自动生成 API 文档,然后在 web 端暴露,并且 API 文档与 API 定义同步更新 ...
- Spring源码分析之Bean的创建过程详解
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...
- beaninfo详解源码解析 java_【Spring源码分析】Bean加载流程概览
代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...
- Cubemx与HAL库系列教程|系统时钟配置详解及源码分析
STM32时钟系统简介 STM32种类繁多,时钟系统也不尽相同,但基本的还是大差不差,今日小飞哥就F1系列的MCU简单聊一聊STM32的时钟系统 1.时钟种类介绍: 先来看一看时钟树图,包含了整个系统 ...
- 【spring源码】源码分析
[spring源码]源码分析 (一)mac版idea引入spring源码 (二)spring的学习流程 (三)spring源码分析 [1]refresh()方法概览(AbstractApplicati ...
- Spring Security 源码分析:Spring Security 授权过程
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring I ...
- Spring Cloud部分源码分析Eureka,Ribbon,Feign,Zuul
Eureka SpringCloud Eureka使用NetFlix Eureka来实现的,它包括了服务端组件和客户端组件,并且都是用java 编写的. Eureka服务端就是服务注册中心, Eure ...
- Spring Security(四) —— 核心过滤器源码分析
摘要: 原创出处 https://www.cnkirito.moe/spring-security-4/ 「老徐」欢迎转载,保留摘要,谢谢! 4 过滤器详解 前面的部分,我们关注了Spring Sec ...
最新文章
- UnicodeDecodeError: ‘utf-8‘ codec can‘t decode byte 0xb0 in position 0: invalid start byte
- Flex DataGrid设置不同行高度和自动换行
- 建立稳定安全的SSH隧道
- spring集成 JedisCluster 连接 redis3.0 集群
- Flask之threading.loacl方法
- MVC传递Model
- Array.forEach
- 图嵌入综述 (arxiv 1709.07604) 译文第三章
- Java继承,接口,抽象类
- 20201219:力扣219周周赛题解
- 浅谈Event Loop
- camtasia喀秋莎2022(屏幕录像课件制作工具)
- arcgis python计算面积_如何在ARCMAP里面计算面积
- iOS 热敏打印机打印位图
- Windows蓝屏代码集合
- python编程软件手机版下载_清辅音p的发音方法
- 用HTML+CSS+JS做一个漂亮的个人网页
- win10环境下Android SDK下载安装及配置教程----Android SDK安装
- vue中在一个函数中调用另外一个函数
- 用python画小星星
热门文章
- 32位和64位与虚拟地址之间和字节数的问题
- SE11 创建搜索帮助
- 计算机休眠快还是关机快,你应该关机,睡眠还是休眠你的笔记本电脑? | MOS86...
- 美国大学生数学建模竞赛数据常用网站-数学建模(十九)
- cocos2d-x中的Jni使用(C++与Andriod方法互调)
- 64位系统运行报错:1%不是有效的win32应用程序解决
- 大宇资讯java游戏_30年后重新出发:大宇单机游戏《轩辕剑柒》试玩
- 调用caffe2中pre-trained model(squeezenet)
- 【人工智能】专家系统简答
- python通过SMIELS查询CAS号