专栏首页程序那些事Spring5参考指南:容器扩展

Spring5参考指南:容器扩展

Spring提供了一系列的接口来提供对Spring容器的扩展功能。下面我们一一介绍。

BeanPostProcessor自定义bean

前面一篇文章我们在自定义bean中提到,可以实现Spring的InitializingBean和DisposableBean接口来实现自定义bean的生命周期。如果是容器级别的,Spring提供了更加强大的BeanPostProcessor,来实现在容器级对Bean的扩展。

BeanPostProcessor接口定义了两个方法:

    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

该方法在调用容器初始化方法(如InitializingBean.afterPropertiesSet()或任何声明的init方法)之前,以及在任何bean初始化之后,被调用。

    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

该方法在容器初始化方法之后被调用。

BeanPostProcessor可以配置多个,如果想控制多个BeanPostProcessor的顺序,可以实现Ordered接口,来定义他们的顺序。

虽然BeanPostProcessor是通过ApplicationContext自动检测的,你也可通过ConfigurableBeanFactory的addBeanPostProcessor来手动注册。手动注册则其Ordered失效,以手动注册的先后为准。

还要注意,以编程方式注册的BeanPostProcessor实例总是在注册为自动检测的实例之前进行处理,而不接收任何显式排序。

所有BeanPostProcessor实例和这些实例直接引用的bean都在启动时实例化,因为AOP自动代理是作为BeanPostProcessor本身实现的,所以BeanPostProcessor实例和它们直接引用的bean都不符合自动代理的条件。

下面是一个调用的例子:

    <bean id="beanA" class="com.flydean.beans.BeanA"/>
    <bean id="beanB" class="com.flydean.beans.BeanB"/>

    <bean class="com.flydean.beans.InstantiationTracingBeanPostProcessor"/>

调用实现:

    public static void main(final String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-post-processor.xml");
        BeanA beanA = (BeanA) ctx.getBean("beanA");
        System.out.println(beanA);
    }

BeanFactoryPostProcessor自定义配置元数据

BeanFactoryPostProcessor接口的语义与BeanPostProcessor的语义相似,但有一个主要区别:BeanFactoryPostProcessor对Bean配置元数据进行操作。也就是说,Spring IOC容器允许BeanFactoryPostProcessor读取配置元数据,并可能在容器实例化BeanFactoryPostProcessor实例以外的任何bean之前对其进行更改。

BeanFactoryPostProcessor也可以配置多个,并通过实现Ordered接口来确定执行顺序。BeanFactoryPostProcessor定义了一个方法:

    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

通过该方法可以获取到可配置的beanFactory从而对bean定义进行修改。

Spring提供了很多预定义的bean工厂后处理器,例如PropertyOverrideConfigurer和PropertyPlaceholderConfigurer。下面我们通过例子来说明怎么使用。

PropertyOverrideConfigurer类名替换

PropertyPlaceholderConfigurer主要用于从外部的Property文件读取属性,用来替换定义好的配置,这样做可以使部署应用程序的人员自定义特定于环境的属性,如数据库URL和密码,而不必为容器修改一个或多个XML定义主文件从而增加复杂性或风险。

下面是配置的XML文件:

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

    <bean id="dataSource" destroy-method="close"
          class="com.flydean.beans.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>

这个例子展示了属性被配置在外部的Properties文件中。在运行时,使用PropertyPlaceholderConfigurer将元数据替换成DataSource中的某些属性。要替换的值被指定为${property-name}格式的占位符,该格式遵循ant和log4j以及JSP EL样式。

真实的值取自外部的Java Properties格式的文件:

jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

PropertyOverrideConfigurer属性覆盖

PropertyOverrideConfigurer可以用来覆盖Bean属性的默认值,或者设置新的值。我们看一个例子:

    <bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
        <property name="locations" value="classpath:override.properties"/>
        <property name="properties">
            <value>beanOverride.url=com.something.DefaultStrategy</value>
        </property>
    </bean>
    <bean name="beanOverride" class="com.flydean.beans.BeanOverride"/>

对应的类是:

@Data
public class BeanOverride {

    private String name="beanA";
    private String url="http://www.163.com";

}

它的默认属性会被覆盖。

使用FactoryBean自定义实例化逻辑

FactoryBean接口提供3个方法:

  • Object getObject(): 返回工厂创建的实例,该实例可能是被共享的, 取决于该实例是单例还是多例模式。
  • boolean isSingleton():判断FactoryBean返回的是单例还是多例。
  • Class getObjectType():返回getObject() 方法返回的类型,如果提前不知道类型,那么返回null。

我们可以实现FactoryBean接口来自定义Bean的实现逻辑。

public class BeanFactoryBean implements FactoryBean {

    @Resource
    private BeanA beanA;

    @Override
    public Object getObject() throws Exception {
        return beanA;
    }

    @Override
    public Class<?> getObjectType() {
        return BeanA.class;
    }
}

下面是其配置:

    <context:annotation-config/>
    <bean id="beanA" class="com.flydean.beans.BeanA"/>

    <bean id="beanFactoryBean" class="com.flydean.beans.BeanFactoryBean"/>

如何使用?

    public static void main(final String[] args) throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean-factory.xml");
        BeanFactoryBean beanFactoryBean = (BeanFactoryBean) ctx.getBean("&beanFactoryBean");
        System.out.println(beanFactoryBean.getObject());
        System.out.println(beanFactoryBean.getObjectType());

        BeanA beanA=(BeanA)ctx.getBean("beanFactoryBean");
        System.out.println(beanA);
    }

当需要向容器请求实际的FactoryBean实例本身而不是它生成的bean时,在调用ApplicationContext的getbean()方法时,在bean的ID前面加上符号(&)。因此,对于ID为beanFactoryBean的给定FactoryBean,在容器上调用getBean(“beanFactoryBean”)返回FactoryBean生成的bean,而调用getBean(“&beanFactoryBean”)则返回FactoryBean实例本身。

本节的例子可以参考:ioc-extend

本文分享自微信公众号 - 程序那些事(flydean-tech),作者:flydean

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spring5参考指南:依赖注入

    依赖注入就是在Spring创建Bean的时候,去实例化该Bean构造函数所需的参数,或者通过Setter方法去设置该Bean的属性。

    程序那些事
  • Spring5参考指南:Bean作用域

    Bean是Spring的根本,Spring本身就是一个一个的bean组成的,bean托管在Spring容器中,那么这些bean的作用域范围是怎么样的呢?

    程序那些事
  • Spring5参考指南:Bean的创建

    Bean在Spring中就是一个业务组件,我们通过创建各种Bean来完成最终的业务逻辑功能。

    程序那些事
  • Heritrix3.x自定义扩展Extractor

      Heritrix3.x与Heritrix1.x版本差异比较大,全新配置模式的引入+扩展接口的变化,同时由于说明文档的匮乏,给Heritrix的开发者带来困惑...

    数据饕餮
  • 真没想到!三十步才能完成bean实例的创建

    本文已收录至我的GitHub 在容器启动快完成时,会把所有的单例bean进行实例化,也可以叫做预先实例化。 这样做的好处之一是,可以及早地发现问题,及早的抛出...

    Java3y
  • Spring Bean的解析和加载详细解释

    Core Container 包含 Core,Beans,Context,Expression Language 模块 Core 和 Beans 提供 IOC...

    黑白格
  • spring中bean的细节 三种创建bean对象的方法 bean作用范围 bean对象的生命周期

    模拟一个工厂类(该类可能时存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数)

    韦恩少爷的背
  • 快速学习-Spring(Bean的生命周期)

    cwl_java
  • Spring 循环依赖及三级缓存

    Spring启动过程大致如下: 1.加载配置文件 2.解析配置文件转化beanDefination,获取到bean的所有属性、依赖及初始化用到的各类处理...

  • spring中Bean的作用域

    在Spring中,那些组成你应用程序的主体(backbone)及由Spring IoC容器所管理的对象,被称之为bean。 简单地讲,bean就是由Spri...

    MickyInvQ

扫码关注云+社区

领取腾讯云代金券