06、IoCContainer-6 Bean定义的继承、容器扩展点

本文探讨了Spring框架中Bean定义的继承机制,如何利用BeanPostProcessor和BeanFactoryPostProcessor进行容器扩展,以及通过FactoryBean定制复杂的实例化逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Bean定义的继承、容器扩展点

  1. 定义继承

    <bean>元素中定义parent属性,使这个bean定义继承另一个bean定义。

     <bean id="inheritedTestBean" abstract="true"
        class="org.springframework.beans.TestBean">
        <property name="name" value="parent"/>
        <property name="age" value="1"/>
     </bean>
    
     <bean id="inheritsWithDifferentClass"
            class="org.springframework.beans.DerivedTestBean"
            parent="inheritedTestBean" init-method="initialize">  
        <property name="name" value="override"/>
        <!-- the age property value of 1 will be inherited from parent -->
     </bean>
    

    子定义可以继承父定义的作用域,构造器参数,属性值;也可以覆盖父定义的方法,比如初始话回调,销毁回调和静态工厂方法等。

    下面的设置总是取自子定义:依赖、自动装配模式、依赖检查和懒启动。

    如果在一个bean定义中abstract属性为true,那么这个bean将不会被实例化,它只会作为子定义的模板。如果一个bean定义没有指定class属性,一定要配置abstract属性。

  2. 使用BeanPostProcessor定制Bean

    2.1 BeanPostProcessor

    实现BeanPostProcessor接口定义的方法,我们可以定义自己的实例化逻辑和依赖解析逻辑等。也可以在容器完成实例化、配置、和初始化bean之后完成我们定义的一些逻辑。

    如果我们实现了多个BeanPostProcessor,我们还可以通过实现Ordered接口来定义这些工作逻辑的执行顺序。

    每个post-processor只在自己所在的容器中起作用。

    容器将自动搜寻并注册那些实现了BeanPostProcessor接口且在配置元数据中有定义的bean。下面是一个简单的例子,这个post-processor将在每个bean初始化后都打印结果。

    class InitialResultPostProcessor implements BeanPostProcessor, Ordered {
        public static Logger log = Logger.getLogger("com.heisenberg.spring");
        //post-processor的执行顺序
        @Override
        public int getOrder() {
            return 0;
        }
        //每一个bean初始化之前回调该方法
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
        //每一个bean初始化之后回调该方法
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            log.info(Utils.buildLogInfo(beanName + " has been initialed"));
            return bean;
        }
    }
    
    <bean id="postProcessor" class="com.heisenberg.spring.pojo.InitialResultPostProcessor"/>
    
  3. 使用BeanFactoryPostProcessor定制配置数据元

    从语义上看,BeanFactoryPostProcessorBeanPostProcessor相似,但是它们有一个主要的不同点:BeanFactoryPostProcessor操作bean的配置数据元。Spring IoC容器让一个BeanFactoryPostProcessor读取配置信息,也允许它在初始化这些bean(除BeanFactoryPostProcessorbean之外)之前修改他们的配置。

    如果我们实现了多个BeanFactoryPostProcessor,我们还可以通过实现Ordered接口来定义这些工作逻辑的执行顺序。

    每个bean factory post-processor只在自己所在的容器中起作用。

    Spring包含几个预定义的bean factory post processor,如PropertyOverrideConfigurerPropertySourcesPlaceholderConfigurer

    BeanPostProcessorBeanFactoryPostProcessor会预初始化,即使我们将这些bean设置为lazy-init=true,或者整个容器的bean都懒加载default-lazy-init=true

    public interface BeanFactoryPostProcessor {
        void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
    }
    

    3.1 PropertySourcesPlaceholderConfigurer

    org.springframework.context.support.PropertySourcesPlaceholderConfigurer 实现了BeanFactoryPostProcessor接口。这个类可以让我们在单独的文件中配置bean的属性值。

    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <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
    

    通过上面的配置,dataSourcebean的属性值在应用运行时被替换。

    我们也可以使用专门的元素<context>来配置property placeholder

    <!-- location的值可以是多个用,隔开的文件路径 -->
    <context:property-placeholder location="classpath:com/something/jdbc.properties"/>
    

    property placeholder不仅可以用来替换bean的基础类型值,还可以用来替换<bean>元素的class属性值。当我们在运行时必须要指定一个实现类型时,这个特性就十分的有用

    <bean class="org.springframework.beans.factory.config.PropertySourcesPlaceholderConfigurer">
        <property name="locations">
            <value>classpath:com/something/strategy.properties</value>
        </property>
        <property name="properties">
            <value>custom.strategy.class=com.something.DefaultStrategy</value>
        </property>
    </bean>
    
    <bean id="serviceStrategy" class="${custom.strategy.class}"/>
    

    3.2 PropertyOverrideConfigurer

    PropertySourcesPlaceholderConfigurer类似,但是PropertyOverrideConfigurer允许bean的属性有默认值或者没有值。如果一个Properties文件没有对应于这个bean某个属性的实体时,这个属性就使用某人值,否则默认值将被覆盖。Properties文件的格式为:

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

    上面的配置是dataSourcebean的driverClassNameurl属性的覆盖值。还支持符合属性名:

    aaa.bbb.ccc=ddd
    

    beanaaa的域bbbccc属性的覆盖值为:ddd

    简便的PropertyOverrideConfigurerbean配置:

    <context:property-override location="classpath:override.properties"/>
    
  4. 通过FactoryBean制定实例化逻辑(注意不是BeanFactory

    FactoryBean接口有3个方法:

    public interface FactoryBean<T> {
         String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
    
         @Nullable
         T getObject() throws Exception;
    
         @Nullable
         Class<?> getObjectType();
    
         default boolean isSingleton() {
             return true;
         }
     }
    

    在容器中注册一个FactoryBean,并为其设置一个id(这里假设为:factoryBean),那么调用ApplicationContextgetBean()方法,入参beanName的值若是"factoryBean",那么我们实现的getObject方法将会被调用。若入参beanName的值为:"&factoryBean"(多了&前缀),我们将的到factoryBeanbean本身的实例。

    若一个bean的实例化过程十分的复杂,使用FactoryBean是一个非常好的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值