spring之扩展点
上篇文章,介绍了spring中bean的生命周期,并且在文章末尾提到,spring使用BeanPostProcessor接口来处理生命周期的回调。我们可以在初始化函数(init())中定制化一些逻辑。上述BeanPostProcessor就是spring扩展点(extension points)。Spring及其灵活,一般情况下我们并不需要去继承ApplicationContext 去扩展功能,只需要使用spring提供的扩展接口,就可以刻spring无缝集成。
Spring主要提供了两类扩展点BeanPostProcessor和BeanFactoryPostProcessor。前者是操作bean的实例,后者使对bean的元数据定义进行扩展。
BeanPostProcessor
接口说明
BeanPostProcessor提供对bean实例的操作扩展,在spring容器对bean实例化和设置依赖之后,其回调开始执行。BeanPostProcessor接口定义的两个方法,分别在bean的初始化方法(InitializingBean接口,或者init-method定义)执行的前后执行:
public interface BeanPostProcessor{ /** * 在bean的初始化方法执行后执行 */ public Object postProcessAfterInitialization(Object arg0, String arg1) throws BeansException ; /** * 在bean的初始化方法执行前执行 */ public Object postProcessBeforeInitialization(Object arg0, String arg1) throws BeansException ; }
以上两个方法的参数列表中分别是spring容器创建的对象和beanName,返回值是经过定制化的对象。
BeanPostProcessor对bean实例进行操作,其在bean被实例化和后开始被执行相关回调。它是与容器相关的,它只对其所在容器中的bean有影响,对其父容器没有影响。
BeanPostProcessor也是一个bean,只不过它对其它bean进行后续的扩展处理。Spring容器可以自动的发现实现这个接口的bean。所以如果我们要使我们自定义的BeanPostProcessor起作用,可以像配置其他bean一样在配置文件中进行配置(当然也可以编码实现,向容器注册一个)。Spring容器对BeanPostProcessor类型的bean专门处理,所有的BeanPostProcessor及其引用的依赖在spring容器启动的时候实例化,作为spring容器启动的一个阶段。
Spring容器中内置很多BeanPostProcessor的实现,如上篇文章中使用JSR注解@PostConstruct,对它的处理就是使用BeanPostProcessor。它的另外一哥较常见的作用是spring-aop动态代理。
另外我们可以定义多个BeanPostProcessor,他们执行的顺序可以通过实现Ordered接口来控制。
一个示例
代码说明
这个示例演示有两个点:
1. BeanPostProcessor的在bean的生命周期中执行时机。
2. 模拟实现一个aop的事务处理代理。
这个示例使用的代码基本上还是上篇文章的代码,增加了一个BeanPostProcessor的一个实现类、一个自定义的注解等在UserService的方法上添加事务的功能,其基本结构如下:
代码和注释
首先我们创建一个注解来标识某个方法,说明其需要事务处理,如下所示:
/** *一个标志的注解,只有被这个注解标识的方法才需要增加事务。 * */ @Retention(RetentionPolicy.RUNTIME) @Target(value={ElementType.METHOD}) public @interface MyTransaction { }
然后采用在采用JDK动态代理的机制创建代理前,我们需要一个自定义的InvocationHandler,它对仅对上述注解标识的方法进行事务处理,其代码和注释如下:
package com.test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 自定义代理,JDK动态代理 * */ public class MyCustomProxy implements InvocationHandler{ private Object target; public MyCustomProxy(Object target) { super(); this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (isNeed(method)) {//只有被@MyTransaction注解标识的方法才执行开启事务操作。 beginTransaction(proxy); } Object res = method.invoke(target, args); if (isNeed(method)) { endTransaction(); } return res; } /** *模拟开启事务,假如通用点,就很类似AOP中的 Before advice */ private void beginTransaction(Object o){ System.out.println("-----开始事务--------"); } /** *模拟结束事务,假如通用点,就很类似AOP中的 After advice */ private void endTransaction(){ System.out.println("-----结束事务--------"); } /** * 这个实现也可以更加的通用,基于不同的对象不同方法不同的规则 */ private boolean isNeed(Method method){ if (method.getAnnotation(MyTransaction.class) != null) { return true; } return false; } }
接下来我们需要在合适的时机创建JDK动态代理,以下代码仅对UserService创建代理,当然这里可以更加通用一些,如对某些包的某些类、被某个注解标识等。下面的代码是自定义的BeanPostProcessor,我们在postProcessAfterInitialization方法中创建代理对象并返回给Spring容器:
package com.test; import java.lang.reflect.Proxy; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import com.test.service.UserService; /** * 注意打印的文字,观察其生命周期 * 这里只对UserService创建代理 * */ public class MyCustomPostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object arg0, String arg1) throws BeansException { if (arg0 instanceof UserService) {//为UserService创建代理,实现事务,如果这里的可以是基于某种规则,如@ASPECTJ的规则 System.out .println(" i am postProcessAfterInitialization beanname :" + arg1 + " BEAN type:" + arg0); System.out.println("创建代理,实现事务"); //创建代理,这里可以通过一个代理工厂,根据不同的规则采用不同的InvokeHandler Object res = Proxy.newProxyInstance(getClass().getClassLoader(), arg0.getClass().getInterfaces(), new MyCustomProxy(arg0)); return res; } return arg0; } @Override public Object postProcessBeforeInitialization(Object arg0, String arg1) throws BeansException { if (arg0 instanceof UserService) { System.out .println(" i am postProcessBeforeInitialization beanname :" + arg1 + " BEAN type:" + arg0); } return arg0; } }
最后我们需要上述的BeanPostProcessor生效,为此我们在xml配置文件中增加如下代码:
<bean class="com.test.MyCustomPostProcessor"/>
然后测试代码如下,和之前一样采用main方法模拟:
UserService userService0 = context.getBean("user0", UserService.class); System.out.println(userService0.getUser());
程序运行结果,从以下程序运行结果可以看出我们提出的两点:
1. BeanPostProcessor的两个回调一个在依赖注入和初始化函数执行前执行,一个在初始化函数执行后执行。
2. 我们模拟AOP事务处理的代码成功执行。实际上Spring中AOP自动代理就是使用BeanPostProcessor实现的。
BeanFactoryPostProcessor
接口说明
这是Spring容器的另外一个扩展点,和BeanPostProcessor不同的地方在于,它是对beanDefiniton进行操作。
其接口定义如下所示:
public interface BeanFactoryPostProcessor { /** * Modify the application context's internal bean factory after its standard * initialization. All bean definitions will have been loaded, but no beans * will have been instantiated yet. This allows for overriding or adding * properties even to eager-initializing beans. * @param beanFactory the bean factory used by the application context * @throws org.springframework.beans.BeansException in case of errors */ void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; }
实现该接口,可以在spring的bean创建之前,修改bean的定义属性。也就是说,Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,并可以根据需要进行修改,例如可以把bean的scope从singleton改为prototype,也可以把property的值给修改掉。可以同时配置多个BeanFactoryPostProcessor,并通过设置‘order‘属性或实现order接口来控制各个BeanFactoryPostProcessor的执行次序,这些和BeanPostProcessor很类似,并且其启用方式和容器相关性也与之一致。
注意:BeanFactoryPostProcessor是在spring容器加载了bean的定义文件之后,在bean实例化之前执行的。接口方法的入参是ConfigurrableListableBeanFactory,使用该参数,可以获取到相关bean的定义信息:
BeanDefinition obj = arg0.getBeanDefinition("sumBean");
Spring内置实现了很多的BeanFactoryPostProcessor实现,例如:
常用的有:
- org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
- org.springframework.beans.factory.config.PropertyOverrideConfigurer
- org.springframework.beans.factory.config.CustomEditorConfigurer:用来注册自定义的属性编辑器。
一个示例
这个示例我们来演示下BeanFactoryPostProcessor的简单用法,我们在UserServiceIml.Java中新增加一个field 名字是testValue(假设这是个String类型的),但是不设置初值,然后我们在BeanFactoryPostProcessor中进行设置。
我们自定义的BeanFactoryPostProcessor如下:
public class MyCostumFactoryProcessor implements BeanFactoryPostProcessor{ @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory arg0) throws BeansException { BeanDefinition obj = arg0.getBeanDefinition("user0"); MutablePropertyValues pv = obj.getPropertyValues(); pv.add("testValue", "这是新增加的测试值"); } }
最后在配置文件中注册它:
<bean class="com.test.MyCostumFactoryProcessor"/>
结束语
Spring扩展点在Spring核心中是很重要的概念,spring本身就内置了很多实现。如果我们需要扩展Spring的功能,他们是很好的方式。并且它们可以像插件一样很好的工作。本篇演示完整代码见底下评论。