The IoC Container 1.8

作者: 小鲍比大爷 | 来源:发表于2019-02-17 19:27 被阅读0次

    1.8. Container Extension Points

    通常来讲,程序员不需要继承ApplicationContext去定制化Container的功能。Spring通过提供接口的方式给程序员,让程序员实现这些接口来定制化Container的功能,这些接口实现可以插入(plug)到Container中,Container以回调方式调用这些自定义接口,来改变或者增强Container的行为。继承接口比较轻量级,也可以更快速的定制相关功能。本节针对如何扩展Spring容器功能,介绍一些Spring提供的接口。

    1.8.1. Customizing Beans by Using a BeanPostProcessor

    可以通过继承BeanPostProcessor实现自定义BeanPostProcessor,并将实现插入到Spring container中,来实现对bean的自定义初始化逻辑、自定义解析等等。
    示例代码:

    package examples;
    
    import org.springframework.beans.factory.config.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;
        }
    }
    

    Spring container支持一个或多个自定义BeanPostProcessor,假设自定义了多个BeanPostProcessor,那就存在哪个BeanPostProcessor先调用的问题,如果有调用顺序要求,那么需要再继承Ordered接口,优先级仍然是数字越小优先级越高,最高优先级和最低优先级分别定义为int类型的最小值和最大值:

    public interface Ordered {
        int HIGHEST_PRECEDENCE = -2147483648;
        int LOWEST_PRECEDENCE = 2147483647;
    
        int getOrder();
    }
    

    BeanPostProcessor回调方法的调用时机
    顾名思义,分别为bean初始化前和初始化后调用的方法:

    • postProcessBeforeInitialization
    • postProcessAfterInitialization

    BeanPostProcessor,从名称看,主要就是处理bean的,Spring中典型的一种BeanPostProcessor应用场景就是Spring AOP,通过提供专门将bean包装成proxy的BeanPostProcessor,Spring就实现了AOP功能。由于BeanPostProcessor是用来处理Container中其他bean的,所以Spring Container一定会优先实例化BeanPostProcessor类型的bean,那么这些BeanPostProcessor才能在之后处理普通的bean。

    继承BeanPostProcessor是用来扩展Spring IoC container能力的常用方式之一:

    Using callback interfaces or annotations in conjunction with a custom BeanPostProcessor implementation is a common means of extending the Spring IoC container

    1.8.2. Customizing Configuration Metadata with a BeanFactoryPostProcessor

    BeanFactoryPostProcessor和BeanPostProcessor命名有点相似,但是作用不同,BeanPostProcessor针对bean的实例做操作,而BeanFactoryPostProcessor是针对bean的定义做操作,很明显,BeanFactoryPostProcessor是在BeanPostProcessor操作之前的,因为此时还未对bean进行实例化。
    同样,Spring也支持注入多个BeanFactoryPostProcessor,多个BeanFactoryPostProcessor运行的优先级也由Ordered接口控制。

    BeanFactoryPostProcessor的主要作用:

    A bean factory post-processor is automatically executed when it is declared inside an ApplicationContext, in order to apply changes to the configuration metadata that define the container.

    Spring本身有多达50多种内置的BeanFactoryPostProcessor。比如PropertyOverrideConfigurer and PropertyPlaceholderConfigurer。

    Example: The Class Name Substitution PropertyPlaceholderConfigurer
    PropertyPlaceholderConfigurer可以通过读取外部配置数据,动态替换bean定义中的属性:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-2.5.xsd">
        <!--bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations" value="classpath:jdbc.properties"/>
        </bean-->
    
        <context:property-placeholder location="classpath:jdbc.properties" system-properties-mode="ENVIRONMENT"/>
    
        <bean id="dataSource" destroy-method="close"
              class="examples.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>
    
    </beans>
    

    jdbc.properties:

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

    指定class名称PropertyPlaceholderConfigurer初始化bean的配置方法是老版本的配置方式(上述配置中被注掉的bean定义),新的Spring配置方式使用context:property-placeholder指定。通过指定jdbc.properties,继而读取其中配置,然后将bean定义中的以$符号开头的属性值全部替换成具体的值。
    BasicDataSource:

    package examples;
    
    import lombok.Data;
    
    @Data
    public class BasicDataSource {
    
        private String driverClassName;
        private String url;
        private String username;
        private String password;
    
        public void close() {
    
        }
    
        @Override
        public String toString() {
            return String.format("%s=[%s,%s,%s,%s].", BasicDataSource.class, driverClassName, url, username, password);
        }
    }
    

    执行如下代码:

            BasicDataSource dataSource = context.getBean("dataSource", BasicDataSource.class);
            System.out.println(dataSource);
    

    执行结果:

    class examples.BasicDataSource=[org.hsqldb.jdbcDriver,jdbc:hsqldb:hsql://production:9002,sa,root].
    

    通过执行结果可见,bean实例化后,具体属性就是jdbc.properties中配置的值。

    context:property-placeholder支持配置systemPropertiesMode的值和代表的功能:

    • "ENVIRONMENT" indicates placeholders should be resolved against the current Environment and against any local properties;
    • "NEVER" indicates placeholders should be resolved only against local properties and never against system properties;
    • "FALLBACK" indicates placeholders should be resolved against any local properties and then against system properties;
    • "OVERRIDE" indicates placeholders should be resolved first against system properties and then against any local properties;

    ENVIRONMENT是现版本默认值,其他三种都是为了前向兼容老版本的,这块一般用默认值即可,所以大多数时候不需要设置。

    PropertyPlaceholderConfigurer甚至可以支持启动时再指定执行的类:

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

    PropertyPlaceholderConfigurer基本上可以用来替换bean定义所有支持的属性,具体大家根据源码可以深入研究。

    Example: The PropertyOverrideConfigurer
    PropertyOverrideConfigurer也是BeanFactoryPostProcessor的实现,它用来覆盖bean的成员变量。下面给出具体示例。
    xml配置方式如下:

    <context:property-override location="classpath:override.properties"/>
    <bean id="overrideBean" class="examples.OverrideBean" autowire="byName"/>
    <bean id="dependencyBean" class="examples.DependencyBean" autowire-candidate="true"/>
    

    OverrideBean实现:

    package examples;
    
    import lombok.Data;
    
    @Data
    public class OverrideBean {
        private String name;
        private DependencyBean dependencyBean;
    }
    

    DependencyBean实现:

    package examples;
    
    import lombok.Data;
    
    @Data
    public class DependencyBean {
        private String name;
    }
    

    override.properties:

    overrideBean.name="override"
    dependencyBean.name="dependency"
    

    上述OverrideBean实例化之后,name的值是override,DependencyBean实例化过后,name的值是dependency。可见配置文件中的值已成功覆盖了bean原有的成员变量内容。override.properties配置文件中的配置方式为:

    beanName.property
    

    1.8.3. Customizing Instantiation Logic with a FactoryBean

    官方同样提供了用户自定义FactoryBean来定制实例化逻辑,不过我估计这节是很多年以前写的(本节描述FactoryBean只有三个接口,实际上远远不止),现在FactoryBean的接口已经非常复杂,使用官方的默认实现已经够用。这节本身也很短,没有什么干货,就此略过。如果需要自己实现FactoryBean的可以自行翻看源码实现。

    总结:
    本节讲述了如何通过实现Spring提供的接口(BeanPostProcessor、BeanFactoryPostProcessor)来改变或者增强Spring Container的功能。这节比较偏Spring源码实现(讲述到的几个类实现实际上都是以接口形式注入到Spring Container中来改变或者增强功能)。比如PropertyPlaceholderConfigurer,建议通过分析源码看看Spring到底是如何实现相应功能的。

    相关文章

      网友评论

        本文标题:The IoC Container 1.8

        本文链接:https://www.haomeiwen.com/subject/drhneqtx.html