spring之扩展点

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

时间: 2024-08-06 18:43:44

spring之扩展点的相关文章

spring mvc 提供的几个常用的扩展点

Spring3 MVC结构简单,应了那句话简单就是美,而且他强大不失灵活,易于扩展,性能也很优秀.他和Struts2的思想还是一样的都有一个前端拦截器进行请求转发,只不过Struts2是filter,spring mvc是servlet,但spring mvc非常简单不论是学习还是使用. 这是spring3 mvc的核心流程图: SpirngMVC的第一个扩展点  HandlerMapping接口 -- 处理请求的映射 保存请求url到具体的方法的映射关系,,我们可以编写任意的HandlerMa

Spring开闭原则的表现-BeanPostProcessor扩展点-2

上接Spring提供的BeanPostProcessor的扩展点-1继续分析. 四.BeanPostProcessor接口及回调方法图  从图中我们可以看出一共五个接口,共十个回调方法,即十个扩展点,但我们之前的文章只分析了其中八个,另外两个稍候也会解析一下是干什么的. =================================================================== ==============================================

Spring源码系列 — BeanDefinition扩展点

前言 前文介绍了Spring Bean的生命周期,也算是XML IOC系列的完结.但是Spring的博大精深,还有很多盲点需要摸索.整合前面的系列文章,从Resource到BeanDefinition,再到容器扩展点,最后到Bean创键,这个过程中无处不存在Spring预留的扩展口. 本篇文章介绍Spring的另一种扩展点:BeanDefinition扩展点,该扩展点是为处理BeanDefinition而设计.本文主要从以下几点分析: BeanDefinition扩展点的几种方式 BeanDef

玩转Asp.net MVC 的八个扩展点

MVC模型以低耦合.可重用.可维护性高等众多优点已逐渐代替了WebForm模型.能够灵活使用MVC提供的扩展点可以达到事半功倍的效果,另一方面Asp.net MVC优秀的设计和高质量的代码也值得我们去阅读和学习. 本文将介绍Asp.net MVC中常用的八个扩展点并举例说明. 一.ActionResult ActionResult代表了每个Action的返回结果.asp.net mvc提供了众多内置的ActionResult类型,如:ContentResult,ViewResult,JsonRe

17+个ASP.NET MVC扩展点,含源码{转}

1.自定义一个HttpModule,并将其中的方法添加到HttpApplication相应的事件中!即:创建一个实现了IHttpmodule接口的类,并将配置WebConfig.在自定义的HttpModule中,可以将一个方法注册到HttpApplication的任意一个事件中,在之后执行HttpApplication一些列事件时,按照事件的顺序(事件又按照添加方法先后的顺序)执行注册在事件中的方法! namespace MvcStore.Models { public class Excute

Eclipse平台SDK支持的扩展点

org.eclipse.ant.core.antTasks:将Ant的任务与你的插件中的类关联起来,以扩展Ant在Eclipse中运行时的能力.org.eclipse.ant.core.antTypes: 将Ant的数据类型与你插件中的类关联起来.org.eclipse.ant.core.extraClasspathEntries :提供额外的类型库(JAR 文件)给Ant使用.org.eclipse.compare.contentMergeViewers:为一种或多种文件类型提供一个比较/合并

eclipse插件开发菜单(Menu)功能及其扩展点

Eclipse 具有丰富的菜单功能,给开发人员提供了很好的用户体验.总体而言,Eclipse 菜单种类包括视图 / 编辑器菜单,主菜单(Main Menu),视图 / 编辑器菜单(ViewPart/EditorPart Menu)和上下文菜单(Context Menu).插件开发人员通过灵活应用这些菜单,可以给用户提供很好的体验.由于视图和编辑器菜单功能类似,因此本文重点讲述视图菜单(视图下拉菜单及其工具栏菜单),除此之外,还将讲述主菜单和上下文菜单. 如图 1 所示为 Project Expl

[转] ASP.NET MVC 中你必须知道的 13 个扩展点

ScottGu 在其 最新的博文 中推荐了 Simone Chiaretta 的文章 13 ASP.NET MVC extensibility points you have to know,该文章为我们简单介绍了  ASP.NET MVC  中的 13 个扩展点.Keyvan Nayyeri(与Simone合著了 Beginning ASP.NET MVC 1.0 一书)又陆续发表了一些文章,对这13个扩展点分别进行深入的讨论.我将在以后的随笔中对这些文章逐一进行翻译,希望能对大家有所帮助.

关于常用插件的扩展点

plugin.xml是插件和Eclipse内核的接口,Eclipse就像一所大宅子.它的外墙(plugin.xml)有很多门(扩展点), 要熟练进入这座大宅子,就得先搞清楚它有那些门(扩展点). 插件的扩展点非常多,但是很多扩展点都用不到,只需要熟悉一些主要的扩展点就可以了. 加入透视图(perspectives) 开发一个插件最常用的就是新增一个属于本插件专有的透视图.然后在此透视图基础上来展开一些功能,本书 也采用这种方式.