1.SPRING扩展机制
- 通过基于XML和基于Java的配置扩展,可以使用户通过Spring使用我们研发的组件,提供很好的易用性。
- 通过Spring容器最常用的两个扩展点:
BeanFactoryPostProcessor
和BeanPostProcessor
,可以使我们的程序逻辑和Spring容器紧密合作,无缝插入到用户bean的生命周期中,发挥更强大的作用。
2.spring的扩展接口
- 1. FactroyBean 我们熟悉的AOP基础bean
- 2. BeanPostProcess 在每个bena初始化成前后做操作。
- 3. InstantiationAwareBeanPostProcessor 在Bean实例化前后做一些操作。
- 4. BeanNameAware、ApplicationContextAware 和 BeanFactoryAware 针对bean工厂,可以获取上下文,可以获取当前bena的id。
- 5. BeanFactoryPostProcessor Spring允许在Bean创建之前,读取Bean的元属性,并根据自己的需求对元属性进行改变,比如将Bean的scope从singleton改变为prototype。
- 6. InitialingBean 在属性设置完毕后做一些自定义操作 DisposableBean 在关闭容器前做一些操作。
相关知识
一扩展方式
1。基于XML配置的扩展
1.首先需要定义一套XML Schema来描述组件所提供的功能。schema中就需要描述我们期望用户提供的namespace以及namespace之间的排序等元数据。
2.除了XML Schema,我们还需要创建一个自定义的NamespaceHandler来负责解析用户在XML中的配置。
- 为了简化代码,我们一般会继承一个helper类:
NamespaceHandlerSupport
,然后在init
方法中注册处理我们自定义节点的BeanDefinitionParser - 自定义的BeanDefinitionParser负责解析xml中的
config
节点信息,记录用户的配置信息,为后面和Spring整合做好铺垫。
3.注册Spring handler和Spring schema,让Spring解析xml配置文件的过程中识别我们的自定义节点,并且转交到我们的NamespaceHandler
处理。
- 1)首先需要在META-INF目录下创建一个spring.handlers文件,来配置我们自定义的XML Schema Namespace到我们自定义的NamespaceHandler映射关系。
- 2)在META-INF目录下创建一个spring.schemas,来配置我们自定义的XML Schema地址到实际Jar包中的classpath映射关系(避免Spring真的去服务器上下载不存在的文件)
2.基于Java配置的扩展
从Spring 3.0开始,一种新的基于Java的配置方式出现了。
通过这种方式,我们在开发Spring项目的过程中再也不需要去配置繁琐的xml文件了,只需要在Configuration类中配置就可以了,大大的简化了Spring的使用。
另外,这也是Spring Boot默认的配置方式,所以建议也支持这一特性。
2.1 @Import注解
支持Java配置扩展的关键点就是@Import
注解,Spring 3.0提供了这个注解用来支持在Configuration类中引入其它的配置类,包括Configuration类, ImportSelector和ImportBeanDefinitionRegistrar的实现类。
我们可以通过这个注解来引入自定义的扩展Bean。
2.2 自定义注解
和基于XML配置类似的,我们需要提供给用户一个注解来配置需要注入到Spring Property Sources的namespaces和order。
2.3 自定义ImportBeanDefinitionRegistrar实现
ImportBeanDefinitionRegistrar
接口定义了registerBeanDefinitions
方法,从而允许我们向Spring注册必要的Bean。
- 记录用户配置的参数
- 向Spring注册Bean:PropertySourcesProcessor,这个bean后面会实际处理用户配置的参数,从而完成配置注入到Spring中的功能
三扩展点
Spring容器最常用的两个扩展点:BeanFactoryPostProcessor
和BeanPostProcessor
。
3.1 BeanFactoryPostProcessor
BeanFactoryPostProcessor
提供了一个方法:postProcessBeanFactory
。
这个方法会被Spring在容器初始化过程中调用,调用时机是所有bean的定义信息都已经初始化好,但是这些bean还没有实例化。
实现该接口,可以在spring的bean创建之前,修改bean的定义属性。也就是说,Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,并可以根据需要进行修改,例如可以把bean的scope从singleton改为prototype,也可以把property的值给修改掉。可以同时配置多个BeanFactoryPostProcessor,并通过设置'order'属性来控制各个BeanFactoryPostProcessor的执行次序。
注意:BeanFactoryPostProcessor是在spring容器加载了bean的定义文件之后,在bean实例化之前执行的。接口方法的入参是ConfigurrableListableBeanFactory,使用该参数,可以获取到相关bean的定义信息,
<?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"
xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
default-autowire="byName">
<bean id="myJavaBean" class="com.ali.caihj.postprocessor.MyJavaBean">
<property name="desc" value="测试一下啦" />
<property name="remark" value="这是备注信息啦啦啦" />
</bean>
<bean id="myBeanFactoryPostProcessor" class="com.ali.caihj.postprocessor.MyBeanFactoryPostProcessor" />
</beans>
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("调用MyBeanFactoryPostProcessor的postProcessBeanFactory");
BeanDefinition bd = beanFactory.getBeanDefinition("myJavaBean");
System.out.println("属性值============" + bd.getPropertyValues().toString());
MutablePropertyValues pv = bd.getPropertyValues();
if (pv.contains("remark")) {
pv.addPropertyValue("remark", "把备注信息修改一下");
}
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
}
}
实际应用:
3.2 BeanPostProcessor
BeanPostProcessor
提供了两个方法:postProcessBeforeInitialization
和postProcessAfterInitialization
,主要针对bean初始化提供扩展。
postProcessBeforeInitialization
会在每一个bean实例化之后、初始化(如afterPropertiesSet方法)之前被调用。postProcessAfterInitialization
则在每一个bean初始化之后被调用。
我们常用的@Autowired
注解就是通过postProcessBeforeInitialization
实现的(AutowiredAnnotationBeanPostProcessor)。
BeanFactoryPostProcessor在bean实例化之前执行,之后实例化bean(调用构造函数,并调用set方法注入属性值),然后在调用两个初始化方法前后,执行了BeanPostProcessor。初始化方法的执行顺序是,先执行afterPropertiesSet,再执行init-method。如下
<?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"
xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
default-autowire="byName">
<bean id="myJavaBean" class="com.ali.caihj.postprocessor.MyJavaBean" init-method="initMethod">
<property name="desc" value="原始的描述信息" />
<property name="remark" value="原始的备注信息" />
</bean>
<bean id="myBeanPostProcessor" class="com.ali.caihj.postprocessor.MyBeanPostProcessor" />
<bean id="myBeanFactoryPostProcessor" class="com.ali.caihj.postprocessor.MyBeanFactoryPostProcessor" />
</beans>
public class MyJavaBean implements InitializingBean {
private String desc;
private String remark;
public MyJavaBean() {
System.out.println("MyJavaBean的构造函数被执行啦");
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
System.out.println("调用setDesc方法");
this.desc = desc;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
System.out.println("调用setRemark方法");
this.remark = remark;
}
public void afterPropertiesSet() throws Exception {
System.out.println("调用afterPropertiesSet方法");
this.desc = "在初始化方法中修改之后的描述信息";
}
public void initMethod() {
System.out.println("调用initMethod方法");
}
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("[描述:").append(desc);
builder.append(", 备注:").append(remark).append("]");
return builder.toString();
}
}
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("调用MyBeanFactoryPostProcessor的postProcessBeanFactory");
BeanDefinition bd = beanFactory.getBeanDefinition("myJavaBean");
MutablePropertyValues pv = bd.getPropertyValues();
if (pv.contains("remark")) {
pv.addPropertyValue("remark", "在BeanFactoryPostProcessor中修改之后的备忘信息");
}
}
}
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor,对象" + beanName + "调用初始化方法之前的数据: " + bean.toString());
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor,对象" + beanName + "调用初始化方法之后的数据:" + bean.toString());
return bean;
}
}
BeanPostProcessor的执行,取决于配置文件中bean的定义,如果定义的bean是singleton并且不是抽象类,也不延迟初始化,则BeanPostProcessor是在第11步中执行;而对于prototype的bean,BeanPostProcessor是在程序getBean的时候执行的。在第6步中,调用registerBeanPostProcessors方法,注册所有实现BeanPostProcessor接口的bean
参考
https://juejin.im/post/5ba45a94f265da0aa94a0d71
https://nobodyiam.com/2017/02/26/several-ways-to-extend-spring/
https://blog.youkuaiyun.com/qq_38182963/article/details/78795058
https://blog.youkuaiyun.com/caihaijiang/article/details/35552859