这里,我们发现这个bean class,竟然是一个BeanFactoryPostProcessor。这个接口有什么作用呢,大概就是,等所有的beanDefinition都装载了之后,会调用实现了BeanFactoryPostProcessor接口的bean,对beanDefinition进行处理。
如果对这块感兴趣,可以看博主之前的一篇文章,网上也很多解析,可自行搜索:
曹工杂谈:为什么很少需要改Spring源码,因为扩展点太多了,说说Spring的后置处理器
context:property-override 用法这个元素,一般比较少用,但今天查了一下,我觉得这个还比较有意思,而且很奇妙地和当前spring boot外部化配置的思想吻合。
它的用途说起来比较晦涩,我们看例子就知道了:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans "> <bean > <property value="Ram"/> <property value="20"/> <property value="Varanasi"/> </bean> </beans> package org.springframework.contextnamespace; import lombok.Data; @Data public class Person { private String name; private int age; private String location; }测试代码:
package org.springframework.contextnamespace; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.util.MyFastJson; import java.util.List; import java.util.Map; /** * desc: * * @author : caokunliang * creat_date: 2019/12/25 0025 * creat_time: 15:50 **/ @Slf4j public class TestPropertyOverride { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( new String[]{"classpath:context-namespace-test-property-override.xml"},false); context.refresh(); // 获取bean Object bean = context.getBean(Person.class); System.out.println("bean:" + bean); } }输出如下:
bean:Person(name=Ram, age=20, location=Varanasi)
这个应该大家都懂。
接下来,我们在xml里定义一个元素:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans "> // 配置了这个玩意 <context:property-override location="classpath:beanOverride.properties"/> <bean > <property value="Ram"/> <property value="20"/> <property value="Varanasi"/> </bean> </beans> #beanOverride.properties person.age=40 person.location=Delhi测试程序不变,这次的输出如下:
bean:Person(name=Ram, age=40, location=Delhi)
也就是说,外部配置文件:beanOverride.properties中的属性,覆盖了xml中的bean的属性。
而现在,spring boot的environment解析变量时,也是外部的配置文件、命令行参数、环境变量等,优先级高于jar包内的配置,是不是和我们这个元素的作用比较像呢?
等价用法如果不使用:,也可以像下面这样使用:
<bean> <property value="classpath:beanOverride.properties" /> </bean> 元素解析从ContextNamespaceHandler,我们可以找到该元素对应的parser:PropertyOverrideBeanDefinitionParser
public void init() { registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); }类实现也很简单,和前面的一样,都继承了同一个基类:AbstractPropertyLoadingBeanDefinitionParser。
简单看看其实现吧:
class PropertyOverrideBeanDefinitionParser extends AbstractPropertyLoadingBeanDefinitionParser { @Override protected Class getBeanClass(Element element) { return PropertyOverrideConfigurer.class; } @Override protected void doParse(Element element, BeanDefinitionBuilder builder) { super.doParse(element, builder); builder.addPropertyValue("ignoreInvalidKeys", Boolean.valueOf(element.getAttribute("ignore-unresolvable"))); } }这里,看看我们获得的bean class:
和前面讨论的一样,也是一个BeanFactoryPostProcessor。
总结又需要回答题目的问题了,从xml文件里,解析得到了什么呢,答案依然是beanDefinition。
不过呢,这次的beanClass,略有不同,因为他们是特殊的class,是可以参与beanDefinition生命周期的class,
因为他们实现了BeanFactoryPostProcessor。
大家可以再看看前面util命名空间,那些bean class呢,主要就是FactoryBean。
本篇源码位置:
https://gitee.com/ckl111/spring-boot-first-version-learn/tree/master/all-demo-in-spring-learning/spring-xml-demo/src/main/java/org/springframework/contextnamespace
由于context命名空间都是些大人物,所以本篇主要是先给大家热身,下一讲,我们讲讲这里面的:
annotation-config、component-scan我简单看了两眼,还挺有意思,欢迎大家和我一起学习。