Spring FrameWork Core Tech

The IoC Container 1.8

2019-02-17  本文已影响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初始化前和初始化后调用的方法:

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

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到底是如何实现相应功能的。

上一篇下一篇

猜你喜欢

热点阅读