• Spring中BeanPostProcessor


    Spring中BeanPostProcessor

    前言:

          本文旨在介绍Spring动态配置数据源的方式,即对一个DataSource的配置诸如jdbcUrl,user,password,driverClass都通过运行时指定,而非由xml静态配置定死。

          Spring构造Context的参数一般只包含配置文件路径和类加载器,如果需要达到动态传入配置参数的目的,需要Spring在初始化数据源相关bean的时候能够对原有配置执行修改或替换,为方便处理,本文将定义一个名为DynamicDataSourceConfigHolder的公共类提供配置数据存储。

           本文替换数据源为c3p0配置。

    BeanPostProcessor简介:

          Spring BeanPostProcesssor通常被称为Spring Bean回调处理器,它一般用于在实例化一个bean的前后增加一些附加操作,它会对全局的Spring bean配置生效。

    Spring Bean的生命周期处理:

          Spring Bean生命周期通常对应两种处理方式,一种是init-method &destroy-method, 另一种是InitializingBean的afterPropertiesSet()方法和DisposeBean的destroy()方法,BeanPostProcessor的出现使得批处理Spring bean定义有了可能。

    BeanPostProcessor定义:

     1 /**
     2  * Factory hook that allows for custom modification of new bean instances,
     3  * e.g. checking for marker interfaces or wrapping them with proxies.
     4  *
     5  * <p>ApplicationContexts can autodetect BeanPostProcessor beans in their
     6  * bean definitions and apply them to any beans subsequently created.
     7  * Plain bean factories allow for programmatic registration of post-processors,
     8  * applying to all beans created through this factory.
     9  *
    10  * <p>Typically, post-processors that populate beans via marker interfaces
    11  * or the like will implement {@link #postProcessBeforeInitialization},
    12  * while post-processors that wrap beans with proxies will normally
    13  * implement {@link #postProcessAfterInitialization}.
    14  *
    15  * @author Juergen Hoeller
    16  * @since 10.10.2003
    17  * @see InstantiationAwareBeanPostProcessor
    18  * @see DestructionAwareBeanPostProcessor
    19  * @see ConfigurableBeanFactory#addBeanPostProcessor
    20  * @see BeanFactoryPostProcessor
    21  */
    22 public interface BeanPostProcessor {
    23 
    24     /**
    25      * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
    26      * initialization callbacks (like InitializingBean's <code>afterPropertiesSet</code>
    27      * or a custom init-method). The bean will already be populated with property values.
    28      * The returned bean instance may be a wrapper around the original.
    29      * @param bean the new bean instance
    30      * @param beanName the name of the bean
    31      * @return the bean instance to use, either the original or a wrapped one; if
    32      * <code>null</code>, no subsequent BeanPostProcessors will be invoked
    33      * @throws org.springframework.beans.BeansException in case of errors
    34      * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
    35      */
    36     Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    37 
    38     /**
    39      * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
    40      * initialization callbacks (like InitializingBean's <code>afterPropertiesSet</code>
    41      * or a custom init-method). The bean will already be populated with property values.
    42      * The returned bean instance may be a wrapper around the original.
    43      * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
    44      * instance and the objects created by the FactoryBean (as of Spring 2.0). The
    45      * post-processor can decide whether to apply to either the FactoryBean or created
    46      * objects or both through corresponding <code>bean instanceof FactoryBean</code> checks.
    47      * <p>This callback will also be invoked after a short-circuiting triggered by a
    48      * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
    49      * in contrast to all other BeanPostProcessor callbacks.
    50      * @param bean the new bean instance
    51      * @param beanName the name of the bean
    52      * @return the bean instance to use, either the original or a wrapped one; if
    53      * <code>null</code>, no subsequent BeanPostProcessors will be invoked
    54      * @throws org.springframework.beans.BeansException in case of errors
    55      * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
    56      * @see org.springframework.beans.factory.FactoryBean
    57      */
    58     Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
    59 
    60 }
    View Code

    以上为Spring源代码,我们重点关注它和Spring bean初始化的关系,即postProcessBeforeInitialization将会在Spring 执行bean初始化钩子(init-method或者afterPropertiesSet)之前被调用。

    DynamicDataSourceConfigHolder:

     1 package org.wit.ff;
     2 
     3 import java.util.Map;
     4 
     5 /**
     6  * 动态数据源配置存储.
     7  * @author ff
     8  *
     9  */
    10 public class DynamicDataSourceConfigHolder {
    11     
    12     /**
    13      * 定义本地变量,加入存在多个Spring Context连接多个不同的数据源时,可以共用此类。
    14      */
    15     private static final ThreadLocal<Map<String,String>> dynamicDataSourceConfigHolder = new ThreadLocal<Map<String,String>>();
    16     
    17     public static void setDynamicConfig(Map<String,String> dynamicDataSourceConfig) {
    18         dynamicDataSourceConfigHolder.set(dynamicDataSourceConfig);
    19     }
    20 
    21     public static Map<String,String> getDynamicDataSourceConfig() {
    22         return (dynamicDataSourceConfigHolder.get());
    23     }
    24 
    25     public static void clear() {
    26         dynamicDataSourceConfigHolder.remove();
    27     }
    28 
    29 }
    View Code

    数据源配置文件:

    1 db.driverClass=****
    2 db.jdbcUrl=****
    3 db.user=****
    4 db.password=****

    自定义bean回调处理器:
     1 package org.wit.ff;
     2 
     3 import java.lang.reflect.Method;
     4 import java.util.HashMap;
     5 import java.util.Map;
     6 
     7 import org.springframework.beans.BeansException;
     8 import org.springframework.beans.factory.config.BeanPostProcessor;
     9 import org.springframework.util.ReflectionUtils;
    10 
    11 import com.mchange.v2.c3p0.ComboPooledDataSource;
    12 
    13 /**
    14  * Bean回调处理器.
    15  * @author ff
    16  *
    17  */
    18 public class ComboPooledDataSourceBeanPostProcessor implements BeanPostProcessor {
    19 
    20     private String dataSourceName;
    21 
    22     @Override
    23     public Object postProcessAfterInitialization(Object bean, String paramString) throws BeansException {
    24         return bean;
    25     }
    26 
    27     @Override
    28     public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
    29         // 限制数据源名称和类型.
    30         if (bean instanceof ComboPooledDataSource && dataSourceName.equals(beanName)) {
    31             final Map<String,String> methodMatchField = new HashMap<String,String>();
    32             methodMatchField.put("setDriverClass", "db.driverClass");
    33             methodMatchField.put("setJdbcUrl", "db.jdbcUrl");
    34             methodMatchField.put("setUser", "db.user");
    35             methodMatchField.put("setPassword", "db.password");
    36             // 从公共存储区中加载.
    37             final Map<String, String> config = DynamicDataSourceConfigHolder.getDynamicDataSourceConfig();
    38             ReflectionUtils.doWithMethods(bean.getClass(), new ReflectionUtils.MethodCallback() {
    39                 @Override
    40                 public void doWith(Method paramMethod) throws IllegalArgumentException, IllegalAccessException {
    41                     if(methodMatchField.containsKey(paramMethod.getName())){
    42                         ReflectionUtils.invokeMethod(paramMethod, bean, config.get(methodMatchField.get(paramMethod.getName())));
    43                     }
    44                 }
    45             });
    46         }
    47         return bean;
    48     }
    49 
    50     public void setDataSourceName(String dataSourceName) {
    51         this.dataSourceName = dataSourceName;
    52     }
    53 
    54 }
    View Code

    Spring 配置文件dynamicDatasource/applicationContext.xml:

     1 <!-- 加载properties配置文件 -->
     2     <bean id="propertyConfigurer"
     3         class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
     4         <property name="locations">
     5             <list>
     6                 <!-- 这里支持多种寻址方式:classpath和file -->
     7                 <value>classpath:dynamicDatasource/dbconfig.properties</value>
     8             </list>
     9         </property>
    10     </bean>
    11     <!-- 回调处理器.-->
    12     <bean id="dynamicDataSourceBp" class="org.wit.ff.ComboPooledDataSourceBeanPostProcessor" >
    13         <property name="dataSourceName" value="dataSource" />
    14     </bean>
    15 
    16     <!-- 数据库连接池 -->
    17     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    18         destroy-method="close">
    19         <property name="driverClass" value="${db.driverClass}" />
    20         <property name="jdbcUrl" value="${db.jdbcUrl}" />
    21         <property name="user" value="${db.user}" />
    22         <property name="password" value="${db.password}" />
    23     </bean>
    View Code

    测试示例:

     1 Map<String,String> dynamicDataSourceConfig = new HashMap<String,String>();
     2 dynamicDataSourceConfig.put("db.driverClass", "com.mysql.jdbc.Driver");
     3 dynamicDataSourceConfig.put("db.jdbcUrl", "jdbc:mysql://127.0.0.1:3306/menlo3?autoReconnect=true&amp;characterEncoding=utf-8");
     4 dynamicDataSourceConfig.put("db.user", "root");
     5 dynamicDataSourceConfig.put("db.password", "root");
     6 DynamicDataSourceConfigHolder.setDynamicConfig(dynamicDataSourceConfig);
     7 ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[]{"classpath:dynamicDatasource/applicationContext.xml"});
     8 
     9 //执行一段操作数据库的逻辑验证即可.
    10 
    11 assertNotNull(applicationContext);
    View Code

    Spring提供了很多扩展接口,BeanPostProcessor接口和InstantiationAwareBeanPostProcessor接口就是其中两个。

    BeanPostProcessor

    BeanPostProcessor接口作用是:如果我们需要在Spring容器完成Bean的实例化、配置和其他的初始化前后添加一些自己的逻辑处理,我们就可以定义一个或者多个BeanPostProcessor接口的实现,然后注册到容器中。

    Spring中Bean的实例化过程图示:

    由上图可以看到,Spring中的BeanPostProcessor在实例化过程处于的位置,BeanPostProcessor接口有两个方法需要实现:postProcessBeforeInitialization和postProcessAfterInitialization,

     1 import org.springframework.beans.factory.config.BeanPostProcessor;  
     2 
     3 public class MyBeanPostProcessor implements BeanPostProcessor {  
     4    
     5      public MyBeanPostProcessor() {  
     6         super();  
     7         System.out.println("这是BeanPostProcessor实现类构造器!!");          
     8      }  
     9    
    10      @Override  
    11      public Object postProcessAfterInitialization(Object bean, String arg1)  
    12              throws BeansException {  
    13          System.out.println("bean处理器:bean创建之后..");  
    14          return bean;  
    15      }  
    16    
    17      @Override  
    18      public Object postProcessBeforeInitialization(Object bean, String arg1)  
    19              throws BeansException {  
    20          System.out.println("bean处理器:bean创建之前..");  
    21        
    22          return bean;  
    23      }  
    24  }
    View Code

    由方法名字也可以看出,前者在实例化及依赖注入完成后、在任何初始化代码(比如配置文件中的init-method)调用之前调用;后者在初始化代码调用之后调用。

    注意:

    1、接口中的两个方法都要将传入的bean返回,而不能返回null,如果返回的是null那么我们通过getBean方法将得不到目标。

    2、BeanFactory和ApplicationContext对待bean后置处理器稍有不同。ApplicationContext会自动检测在配置文件中实现了BeanPostProcessor接口的所有bean,并把它们注册为后置处理器,然后在容器创建bean的适当时候调用它,因此部署一个后置处理器同部署其他的bean并没有什么区别。而使用BeanFactory实现的时候,bean 后置处理器必须通过代码显式地去注册,在IoC容器继承体系中的ConfigurableBeanFactory接口中定义了注册方法

     1 /**  
     2  * Add a new BeanPostProcessor that will get applied to beans created  
     3  * by this factory. To be invoked during factory configuration.  
     4  * <p>Note: Post-processors submitted here will be applied in the order of  
     5  * registration; any ordering semantics expressed through implementing the  
     6  * {@link org.springframework.core.Ordered} interface will be ignored. Note  
     7  * that autodetected post-processors (e.g. as beans in an ApplicationContext)  
     8  * will always be applied after programmatically registered ones.  
     9  * @param beanPostProcessor the post-processor to register  
    10  */    
    11 void addBeanPostProcessor(BeanPostProcessor beanPostProcessor); 
    View Code

    另外,不要将BeanPostProcessor标记为延迟初始化。因为如果这样做,Spring容器将不会注册它们,自定义逻辑也就无法得到应用。假如你在<beans />元素的定义中使用了'default-lazy-init'属性,请确信你的各个BeanPostProcessor标记为'lazy-init="false"'。

  • 相关阅读:
    二叉树前序中序遍历求后序遍历
    二叉树的遍历
    Codeforces Round #381 (Div. 2)
    Codeforces Round #380 (Div. 2, Rated, Based on Technocup 2017
    Codeforces Round #379 (Div. 2)
    HDU 2896 ac自动机裸题
    RabbitMQ镜像模式双节点部署时故障转移过程中队列中消息的状态
    Maven搭建Spring MVC时使用jstl无效
    在使用HttpClient做客户端调用一个API时 模拟并发调用时发生“死锁"?
    使用mongodb提供的dotnet core sdk进行地理位置运算
  • 原文地址:https://www.cnblogs.com/jiaozi-li/p/5696191.html
Copyright © 2020-2023  润新知