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 }

以上为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 }

数据源配置文件:

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 }

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>

测试示例:

 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);

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  }

由方法名字也可以看出,前者在实例化及依赖注入完成后、在任何初始化代码(比如配置文件中的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); 

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

时间: 2024-09-28 16:43:52

Spring中BeanPostProcessor的相关文章

spring中BeanPostProcessor之一:InstantiationAwareBeanPostProcessor(01)

在spring中beanPostProcessor绝对是开天辟地的产物,给了程序员很多自主权,beanPostProcessor即常说的bean后置处理器. 一.概览 先来说下InstantiationAwareBeanPostProcessor,这个后置处理器是BeanPostProcessor的子接口,继承自BeanPostProcessor,先看下BeanPostProcessor中的方法, 再看下InstantiationAwareBeanPostProcessor中的方法, 可见Ins

spring中BeanPostProcessor之一:InstantiationAwareBeanPostProcessor(03)

前面介绍了InstantiationAwareBeanPostProcessor后置处理器的postProcessBeforeInstantiation和postProcessAfterInstantiation两个方法的用法和使用场景等.在InstantiationAwareBeanPostProcessor中还有两个方法,分别是postProcessProperties和postProcessPropertyValues,从这两个方法的名称上来看,它们完成的功能似乎很相近,下面来看具体的方法

Spring中基于Java的容器配置(二)

使用@Configuration注解 @Configuration注解是一个类级别的注解,表明该对象是用来指定Bean的定义的.@Configuration注解的类通过@Bean注解的方法来声明Bean.通过调用注解了@Bean方法的返回的Bean可以用来构建Bean之间的相互依赖关系,可以通过前文来了解其基本概念. 注入inter-bean依赖 当@Bean方法依赖于其他的Bean的时候,可以通过在另一个方法中调用即可. @Configuration public class AppConfi

Spring中@Autowired注解与自动装配

1 使用配置文件的方法来完成自动装配我们编写spring 框架的代码时候.一直遵循是这样一个规则:所有在spring中注入的bean 都建议定义成私有的域变量.并且要配套写上 get 和 set方法.比如:Boss 拥有 Office 和 Car 类型的两个属性:public class Boss { private Car car; private Office office; // 省略 get/setter @Override public String toString() { retu

Spring 的 BeanPostProcessor接口实现

今天学习了一下Spring的BeanPostProcessor接口,该接口作用是:如果我们需要在Spring容器完成Bean的实例化,配置和其他的初始化后添加一些自己的逻辑处理,我们就可以定义一个或者多个BeanPostProcessor接口的实现. 下面我们来看一个简单的例子 package com.spring.test.di; import org.springframework.beans.BeansException; import org.springframework.beans.

Spring中ApplicationContext和beanfactory区别

BeanFacotry是spring中比较原始的Factory.如XMLBeanFactory就是一种典型的BeanFactory.原始的BeanFactory无法支持spring的许多插件,如AOP功能.Web应用等.   ApplicationContext接口,它由BeanFactory接口派生而来,因而提供BeanFactory所有的功能.ApplicationContext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承,ApplicationContext包还提供了以下的功

JAVA面试题:Spring中bean的生命周期

Spring 中bean 的生命周期短暂吗? 在spring中,从BeanFactory或ApplicationContext取得的实例为Singleton,也就是预设为每一个Bean的别名只能维持一个实例,而不是每次都产生一个新的对象使用Singleton模式产生单一实例,对单线程的程序说并不会有什么问题,但对于多线程的程序,就必须注意安全(Thread-safe)的议题,防止多个线程同时存取共享资源所引发的数据不同步问题. 然而在spring中 可以设定每次从BeanFactory或Appl

深入理解spring中的各种注解

Spring中的注解大概可以分为两大类: 1)spring的bean容器相关的注解,或者说bean工厂相关的注解: 2)springmvc相关的注解. spring的bean容器相关的注解,先后有:@Required, @Autowired, @PostConstruct, @PreDestory,还有Spring3.0开始支持的JSR-330标准javax.inject.*中的注解(@Inject, @Named, @Qualifier, @Provider, @Scope, @Singlet

Spring中property-placeholder的使用与解析

Spring中property-placeholder的使用与解析 我们在基于spring开发应用的时候,一般都会将数据库的配置放置在properties文件中. 代码分析的时候,涉及的知识点概要: NamespaceHandler 解析xml配置文件中的自定义命名空间 ContextNamespaceHandler 上下文相关的解析器,这边定义了具体如何解析property-placeholder的解析器 BeanDefinitionParser 解析bean definition的接口 Be