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定义:

 /**
* Factory hook that allows for custom modification of new bean instances,
* e.g. checking for marker interfaces or wrapping them with proxies.
*
* <p>ApplicationContexts can autodetect BeanPostProcessor beans in their
* bean definitions and apply them to any beans subsequently created.
* Plain bean factories allow for programmatic registration of post-processors,
* applying to all beans created through this factory.
*
* <p>Typically, post-processors that populate beans via marker interfaces
* or the like will implement {@link #postProcessBeforeInitialization},
* while post-processors that wrap beans with proxies will normally
* implement {@link #postProcessAfterInitialization}.
*
* @author Juergen Hoeller
* @since 10.10.2003
* @see InstantiationAwareBeanPostProcessor
* @see DestructionAwareBeanPostProcessor
* @see ConfigurableBeanFactory#addBeanPostProcessor
* @see BeanFactoryPostProcessor
*/
public interface BeanPostProcessor { /**
* Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
* initialization callbacks (like InitializingBean's <code>afterPropertiesSet</code>
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one; if
* <code>null</code>, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; /**
* Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
* initialization callbacks (like InitializingBean's <code>afterPropertiesSet</code>
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
* instance and the objects created by the FactoryBean (as of Spring 2.0). The
* post-processor can decide whether to apply to either the FactoryBean or created
* objects or both through corresponding <code>bean instanceof FactoryBean</code> checks.
* <p>This callback will also be invoked after a short-circuiting triggered by a
* {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
* in contrast to all other BeanPostProcessor callbacks.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one; if
* <code>null</code>, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
* @see org.springframework.beans.factory.FactoryBean
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; }

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

DynamicDataSourceConfigHolder:

 package org.wit.ff;

 import java.util.Map;

 /**
* 动态数据源配置存储.
* @author ff
*
*/
public class DynamicDataSourceConfigHolder { /**
* 定义本地变量,加入存在多个Spring Context连接多个不同的数据源时,可以共用此类。
*/
private static final ThreadLocal<Map<String,String>> dynamicDataSourceConfigHolder = new ThreadLocal<Map<String,String>>(); public static void setDynamicConfig(Map<String,String> dynamicDataSourceConfig) {
dynamicDataSourceConfigHolder.set(dynamicDataSourceConfig);
} public static Map<String,String> getDynamicDataSourceConfig() {
return (dynamicDataSourceConfigHolder.get());
} public static void clear() {
dynamicDataSourceConfigHolder.remove();
} }

数据源配置文件:

1 db.driverClass=****
2 db.jdbcUrl=****
3 db.user=****
4 db.password=**** 自定义bean回调处理器:
 package org.wit.ff;

 import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.util.ReflectionUtils; import com.mchange.v2.c3p0.ComboPooledDataSource; /**
* Bean回调处理器.
* @author ff
*
*/
public class ComboPooledDataSourceBeanPostProcessor implements BeanPostProcessor { private String dataSourceName; @Override
public Object postProcessAfterInitialization(Object bean, String paramString) throws BeansException {
return bean;
} @Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
// 限制数据源名称和类型.
if (bean instanceof ComboPooledDataSource && dataSourceName.equals(beanName)) {
final Map<String,String> methodMatchField = new HashMap<String,String>();
methodMatchField.put("setDriverClass", "db.driverClass");
methodMatchField.put("setJdbcUrl", "db.jdbcUrl");
methodMatchField.put("setUser", "db.user");
methodMatchField.put("setPassword", "db.password");
// 从公共存储区中加载.
final Map<String, String> config = DynamicDataSourceConfigHolder.getDynamicDataSourceConfig();
ReflectionUtils.doWithMethods(bean.getClass(), new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method paramMethod) throws IllegalArgumentException, IllegalAccessException {
if(methodMatchField.containsKey(paramMethod.getName())){
ReflectionUtils.invokeMethod(paramMethod, bean, config.get(methodMatchField.get(paramMethod.getName())));
}
}
});
}
return bean;
} public void setDataSourceName(String dataSourceName) {
this.dataSourceName = dataSourceName;
} }

Spring 配置文件dynamicDatasource/applicationContext.xml:

 <!-- 加载properties配置文件 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!-- 这里支持多种寻址方式:classpath和file -->
<value>classpath:dynamicDatasource/dbconfig.properties</value>
</list>
</property>
</bean>
<!-- 回调处理器.-->
<bean id="dynamicDataSourceBp" class="org.wit.ff.ComboPooledDataSourceBeanPostProcessor" >
<property name="dataSourceName" value="dataSource" />
</bean> <!-- 数据库连接池 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${db.driverClass}" />
<property name="jdbcUrl" value="${db.jdbcUrl}" />
<property name="user" value="${db.user}" />
<property name="password" value="${db.password}" />
</bean>

测试示例:

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

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

BeanPostProcessor

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

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

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

 import org.springframework.beans.factory.config.BeanPostProcessor;  

 public class MyBeanPostProcessor implements BeanPostProcessor {  

      public MyBeanPostProcessor() {
super();
System.out.println("这是BeanPostProcessor实现类构造器!!");
} @Override
public Object postProcessAfterInitialization(Object bean, String arg1)
throws BeansException {
System.out.println("bean处理器:bean创建之后..");
return bean;
} @Override
public Object postProcessBeforeInitialization(Object bean, String arg1)
throws BeansException {
System.out.println("bean处理器:bean创建之前.."); return bean;
}
}

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

注意:

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

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

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

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

Spring中BeanPostProcessor的更多相关文章

  1. spring中BeanPostProcessor之一:InstantiationAwareBeanPostProcessor(01)

    在spring中beanPostProcessor绝对是开天辟地的产物,给了程序员很多自主权,beanPostProcessor即常说的bean后置处理器. 一.概览 先来说下Instantiatio ...

  2. spring中BeanPostProcessor之三:InitDestroyAnnotationBeanPostProcessor(01)

    在<spring中BeanPostProcessor之二:CommonAnnotationBeanPostProcessor(01)>一文中,分析到在调用CommonAnnotationB ...

  3. spring中BeanPostProcessor之四:AutowiredAnnotationBeanPostProcessor(01)

    在<spring中BeanPostProcessor之二:CommonAnnotationBeanPostProcessor(01)>中分析了CommonAnnotationBeanPos ...

  4. spring(三):spring中BeanPostProcessor的使用

    spring中实现BeanPostProcessor的后置处理器 ApplicationContextAwareProcessor 进入该实现类内部 可以看到:该类帮我们组建IOC容器,判断我们的be ...

  5. spring中BeanPostProcessor之二:CommonAnnotationBeanPostProcessor(01)

    在上篇博客中分享了InstantiationAwareBeanPostProcessor接口中的四个方法,分别对其进行了详细的介绍,在文末留下了一个问题,那就是postProcessPropertie ...

  6. spring中BeanPostProcessor之一:InstantiationAwareBeanPostProcessor(03)

    前面介绍了InstantiationAwareBeanPostProcessor后置处理器的postProcessBeforeInstantiation和postProcessAfterInstant ...

  7. spring中BeanPostProcessor之一:InstantiationAwareBeanPostProcessor(02)

    在上篇博客中写道了bean后置处理器InstantiationAwareBeanPostProcessor,只介绍了其中一个方法的作用及用法,现在来看postProcessBeforeInstanti ...

  8. 通过BeanPostProcessor理解Spring中Bean的生命周期

    通过BeanPostProcessor理解Spring中Bean的生命周期及AOP原理 Spring源码解析(十一)Spring扩展接口InstantiationAwareBeanPostProces ...

  9. Spring点滴十一:Spring中BeanFactoryPostProcessor和BeanPostProcessor区别

    Spring中BeanFactoryPostProcessor和BeanPostProcessor都是Spring初始化bean时对外暴露的扩展点.两个接口从名字看起来很相似,但是作用及使用场景却不同 ...

随机推荐

  1. BlockingQueue汇总

    前言: 在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题.通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便 ...

  2. WebDriver 运行模式下使用rc 代码

    selenium2 对之前的rc 代码提供了兼容性接口,如果你之前的code 都是用rc 写,而现在又想摆脱要每次启动server,你只需要 略做修改即可.代码如下: public class Tes ...

  3. Android_1_渐变背景色

    首先创建一个渐变背景色文件drawable-mdpi/bg_color.xml <?xml version="1.0" encoding="utf-8"? ...

  4. MVC框架模式技术实例(用到隐藏帧、json、仿Ajax、Dom4j、jstl、el等)

    前言: 刚刚学完了MVC,根据自己的感悟和理解写了一个小项目. 完全按照MVC模式,后面有一个MVC的理解示意图. 用MVC模式重新完成了联系人的管理系统: 用户需求: 多用户系统,提供用户注册.登录 ...

  5. javscript面试题(一)

    你如何理解HTML结构的语意化? 1.去掉或样式丢失的时候能让页面呈现清晰的结构:2.屏幕阅读器(如果访客有视障)会完全根据你的标记来“读”你的网页:3.PDA.手机等设备可能无法像普通电脑的浏览器一 ...

  6. C#进程同名的问题

    当一个进程中,判断另一个进程存在还是不存在可以使用Process.GetProcessesByName()方法来判断.但是仅仅使用Name来做区分的话,是有问题的.如何能保证这个名称的进程就是所希望的 ...

  7. SQL Server 用表中已有数据造数据

    从表中选择数据再插入到表中(select XXX into 与insert into XXX select的结合) 在做性能测试时需要大量的业务数据.完全从画面造数据比较费时间,使用SQL文批量插入数 ...

  8. C#基础系列(一)

    1.vs中F5(调试)和Ctrl + F5(直接运行不调试)的区别: Ctrl+F5是直接运行生成的程序,不进行重新编绎,所以运行起来比较快F5是重新编绎后再运行,这样可以在程序代码中设置断点跟踪来调 ...

  9. Oracle- 分页例子

    Oracle分页的写法,比MSSQLSERVER还简单. 记录如下: select t.*, rownum r from (select su02, wi00, wi01, wi02, wi05, t ...

  10. 如何在64位系统上安装SQL Server 2000

    如何在64位系统上安装SQL Server 2000? 现在用SQL Server 2000数据库的人少了吧?大都是SQL Server 2005/2008了.不过还是有需求的,今天一朋友就让我在他的 ...