spring之扩展点

来源:互联网 发布:eai是什么软件 编辑:程序博客网 时间:2024/05/16 23:54

上篇文章,介绍了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的功能,他们是很好的方式。并且它们可以像插件一样很好的工作。本篇演示完整代码见底下评论。

3 1
原创粉丝点击