Spring基础五:容器功能扩展

Spring容器不仅提供了强大的配置方法,还提供了容器功能扩展点,用户可以基于这些扩展点代码可以增强容器的功能。实际上,Spring很多模块,比如AOP,对注解的支持等,都是基于该机制实现的。

通过BeanPostProcessor定制Bean

BeanPostProcessor定义了一个能对Bean的初始化进行定制的接口。如果我们实现一个BeanPostProcessor,并注册到容器,就能对bean的初始化和配置过程进行干预。

先来看看实现BeanPostProcessor的简单例子:

public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {

    // simply return the instantiated bean as-is
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean; // we could potentially return any object reference here...
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("Bean '" + beanName + "' created : " + bean.toString());
        return bean;
    }
}

回调方法postProcessBeforeInitialization发生在bean的初始化回调被调用(见上一节)之前,该方法返回bean的实例,在这里我们可以给bean创建一个proxy返回给容器(这正是Spring AOP的实现方式)。回调方法postProcessAfterInitialization发生在bean的初始化方法被调用之后。

由于我们拿到了bean的引用,理论上,我们可以对bean做任何操作。

注册BeanPostProcessor

BeanPostProcessor本身就是一个bean,因此它的定义方法与其他的bean没有任何区别,一行简单的定义即可:<bean class="scripting.InstantiationTracingBeanPostProcessor"/>

容器如果发现某个bean是一个BeanPostProcessor,那么会提前初始化它。如果不是通过xml,而是通过java配置代码来定义bean,返回的类型(Type)必须实现了BeanPostProcessor,否则容器无法识别这是一个BeanPostProcessor:

public class ConfigurationClass {
	@Bean
	public Type postProcessor() {
		.....
	}
}

我们还可以调用发context.getBeanFactory().addBeanPostProcessor()动态地注册BeanPostProcessor。

BeanPostProcessor执行顺序

如果有多个BeanPostProcessor,那么可以通过实现Order接口来定义他们的相对执行顺序。

如果是context.getBeanFactory().addBeanPostProcessor()添加的BeanPostProcessor,那么那么即使实现了Order接口也会被忽略,而是按注册的顺序执行;而且他们的顺序在所有静态BeanPostProcessor之前。

由于BeanPostProcessor也是一个bean,它自然可以依赖其他bean,这样一来被依赖的bean将不会被任何BeanPostProcessor处理。这种情形是否有问题,取决于具体的业务场景,但还是避开微妙。Spring此时可能打印一条日志:“Bean xxxx is not eligible for getting processed by all BeanPostProcessor interfaces”。

BeanPostProcessor的作用域

BeanPostProcessor只作用于它所在所在的context,对父子Context都不会起作用。

通过BeanFactoryPostProcessor定制配置数据

BeanFactoryPostProcessor的原理和BeanPostProcessor有点类似,不同的是,BeanPostProcessor处理bean实例,而BeanFactoryPostProcessor处理bean的定义。

BeanFactoryPostProcessor自身也是以bean的形式被定义,多个BeanFactoryPostProcessor实例也通过Order接口来排序;BeanFactoryPostProcessor仅对本容器上下文自作用,不会跨越上下文父子层。

BeanFactoryPostProcessor在容器读取配置后自动执行,从而可以对容器配置做一些修改。Spring内部有很多此类处理器,比如PropertyOverrideConfigurer和PropertyPlaceholderConfigurer,前者执行bean属性的覆盖,后者负责将属性占位符替换成最终值。

PropertyPlaceholderConfigurer

这是大家用得最多的一个processor,用法如下:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>

<bean id="dataSource" destroy-method="close"
        class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

//jdbc.properties文件的内容
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

PropertyPlaceholderConfigurer检查所有bean的定义,将占位符(${jdbc.url})替换成属性文件里的值(jdbc.url=jdbc:hsqldb:hsql://production:9002)。如果属性文件里面没有定义该占位符,那么还会去系统属性(system properties)里面去找(可以通过设置PropertyPlaceholderConfigurer的systemPropertiesMode来改变这种行为)。

PropertyOverrideConfigurer

定义方式和上面类似,所使用属性文件的类似以下:

dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb

这个属性文件的意思是dataSource这个bean的driverClassName属性重写为com.mysql.jdbc.Driver

示例代码

定义一个Bean,有两个属性,一个有Setter方法,一个被@Tag(自定义注解)注释。

public class ExampleBean {
    @Tag
    private String tagA;
    private String tagB;
    
    public void print() {
        System.out.println("tagA = " + tagA);
        System.out.println("tagB = " + tagB);
    }

    public void setTagB(String tagB) {
        this.tagB = tagB;
    }

定义了一个BeanPostProcessor,它遍历bean的所有方法定义,如果发现某个属性有@Tag注解,则注入一个值:

 public class BeanPostProcessor implements org.springframework.beans.factory.config.BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field f : fields) {
            if (f.getAnnotation(Tag.class) != null) {
                f.setAccessible(true);
                try {
                    f.set(bean, "BeanPostProcessor赋值");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }
}

定义一个BeanFactoryProcessor,它定位ExampleBean的definition,并注入一个属性定义:

public class BeanFactoryPostProcessor implements org.springframework.beans.factory.config.BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("exampleBean");
        beanDefinition.getPropertyValues().addPropertyValue("tagB","BeanFactoryPostProcessor赋值");
    }
}
}

完整的示例代码在这里,请看子工程postprocessor_xml。

FactoryBean

如果bean的初始化过程比较复杂,很难用xml配置来表达,可以定义一个FactoryBean,顾名思义它自己也是一个bean,但是它的使命是创建另一个bean。

FactoryBean接口包含以下方法:
1、Object getObject() 返回工厂创建的bean实例;
2、boolean isSingleTon() bean是否单例;
3、Class getObjectType() 返回bean的类型;

如你提供一个名叫myBean的FactoryBean的xml定义,那么当调用容器的getBean("myBean")方法,返回的工厂创建的bean;如果调用getBean("&myBean"),返回的是工厂自身。

总结

BeanPostProcessor和BeanFactoryProcessor是我们扩展Spring容器功能的利器,前者干预bean的创建,后者干预bean的定义。Spring全家桶的各功能模块,大量使用了这两个机制,充分体现了Spring的优秀设计。有兴趣的人可以在Spring源码里找找BeanPostProcessor的实现类,会发现Spring那些给我们带来便利的功能背后,其实都是一个个BeanPostProcessor实现的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值