1.8。容器扩大点

通常,应用程序开发人员不须要对ApplicationContext 实现类进行子类化。相同,能够通过插入非凡集成接口的实现来扩大Spring IoC容器。接下来的几节形容了这些集成接口。

1.8.1。自定义bean实现BeanBeanPostProcessor接口

BeanPostProcessor接口定义了回调办法,您能够实现这些回调办法来批改默认的bean实例化的逻辑,依赖关系解析逻辑等。
如果您想在Spring容器实现实例化,配置和初始化bean之后实现一些自定义逻辑,则能够插入一个或多个自定义BeanPostProcessor。

您能够配置多个BeanPostProcessor实例,并且能够BeanPostProcessor通过实现Ordered 接口设置order属性来管制这些实例的运行程序。

@Componentpublic class MyBeanPostProcessor implements BeanPostProcessor, Ordered {    @Override    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {        return bean;    }    @Override    public int getOrder() {        return 0;    }}
BeanPostProcessor实例操作的是bean的实例。也就是说,Spring IoC容器实例化一个bean实例,而后应用BeanPostProcessor对这些实例进行解决加工。BeanPostProcessor实例是按容器划分作用域的。  仅在应用容器层次结构时,这才有意义。如果BeanPostProcessor在一个容器中定义一个,它将仅对该容器中的bean进行后处理。换句话说,一个容器中定义的bean不会被BeanPostProcessor另一个容器中的定义进行后处理,即便这两个容器是同一层次结构的一部分也是如此。BeanPostProcessor批改的是bean实例化之后的内容,如果要更改理论的bean定义(即bean definition)您须要应用 BeanFactoryPostProcessor接口.

org.springframework.beans.factory.config.BeanPostProcessor接口恰好由两个回调办法组成。
当此类被注册为容器的post-processor时,对于容器创立的每个bean实例,post-processor都会在任何bean实例化之后并且在容器初始化办法(例如InitializingBean.afterPropertiesSet()或任何申明的init办法)被应用之前调用。
post-processor能够对bean实例执行任何操作,也能够齐全疏忽回调。
post-processor通常查看回调接口,或者能够用代理包装Bean。
一些Spring AOP根底构造类被实现为post-processor,以提供代理包装逻辑。

ApplicationContext自动检测实现BeanPostProcessor接口所有bean,留神是要注册成bean,仅仅实现接口是不能够的。

请留神,通过应用@Bean工厂办法申明BeanPostProcessor时,工厂办法的返回类型应该是实现类自身或至多是org.springframework.beans.factory.config.BeanPostProcessor 接口,以分明地表明该bean的post-processor性质。
否则,ApplicationContext无奈在齐全创立之前按类型自动检测它。
因为BeanPostProcessor须要提前实例化以便利用于上下文中其余bean的初始化,因而这种晚期类型检测至关重要。

    @Bean    public BeanPostProcessor myBeanPostProcessor(){        return new MyBeanPostProcessor();    } 
以编程形式注册BeanPostProcessor实例尽管举荐的BeanPostProcessor注册办法是通过ApplicationContext自动检测,然而您能够ConfigurableBeanFactory应用addBeanPostProcessor办法通过编程形式对它们进行注册。当您须要在注册之前评估条件逻辑(比方利用场景是xxx条件才注册,xxx条件不注册时),甚至须要跨层次结构的上下文复制Bean post-processor时,这将十分有用。然而请留神,以BeanPostProcessor编程形式增加的实例不恪守该Ordered接口。在这里,注册的程序决定了执行的程序。还要留神,以BeanPostProcessor编程形式注册的实例总是在通过自动检测注册的实例之前进行解决,而不思考任何明确的程序。
BeanPostProcessor 实例和AOP主动代理实现BeanPostProcessor接口的类是非凡的,并且容器对它们的解决形式有所不同。BeanPostProcessor它们间接援用的所有实例和bean在启动时都会实例化,作为ApplicationContext的非凡启动阶段的一部分。接下来,BeanPostProcessor以排序形式注册所有实例,并将其利用于容器中的所有其余bean。然而因为AOP主动代理的实现是通过BeanPostProcessor接口,所以在AOP的BeanPostProcessor接口实例化之前的BeanPostProcessor实例或BeanPostProcessor实例间接援用的bean都没有资格进行主动代理。并且对于任何此类bean都没有任何解决切面的BeanPostProcessor指向他们。您应该看到一条参考性日志音讯:Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying)。这条音讯的意思大略就是说这个bean没有失去所有BeanPostProcessor的解决上面剖析一下这条日志的逻辑:咱们不必AOP的BeanPostProcessor用AutowiredAnnotationBeanPostProcessor来看这个状况首先这条日志是在BeanPostProcessorChecker类中打印的,这个类自身就实现了BeanPostProcessor,Spring容器减少这个processor的代码如下:            //获取所有的BeanPostProcessor类型的bean         //第一个true示意包含非单例的bean        //第二个false示意仅查找曾经实例化实现的bean,如果是factory-bean则不算入内        String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);            //以后beanFactory内的所有post-processor数 +  1 + postBeanNames的数量        //这个数量在后续有个判断        //beanFactory.getBeanPostProcessorCount() 零碎内置processor        //1 就是BeanPostProcessorChecker        //postProcessorNames.length 就是能扫描到的processor        //这个数量之和就是目前零碎能看到的所有processor        //还有的就可能是解析完了某些bean又新增了processor那个不算在内         int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;                //add BeanPostProcessorChecker 进入beanPostProcessor链        beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));BeanPostProcessorChecker中判断并打印上边那条日志的办法如下:        @Override        public Object postProcessAfterInitialization(Object bean, String beanName) {            //如果以后bean不是postProcessor的实例            //并且不是外部应用的bean            //并且this.beanFactory.getBeanPostProcessorCount()小于方才相加的值            //三个都满足才会打印那行日志            if (!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) &&                    this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) {                if (logger.isInfoEnabled()) {                    logger.info("Bean '" + beanName + "' of type [" + bean.getClass().getName() +                            "] is not eligible for getting processed by all BeanPostProcessors " +                            "(for example: not eligible for auto-proxying)");                }            }            return bean;        }        //以后beanName不为空,并且对应的bean是容器外部应用的bean则返回true            private boolean isInfrastructureBean(@Nullable String beanName) {            if (beanName != null && this.beanFactory.containsBeanDefinition(beanName)) {                BeanDefinition bd = this.beanFactory.getBeanDefinition(beanName);                return (bd.getRole() == RootBeanDefinition.ROLE_INFRASTRUCTURE);            }            return false;        }        在看Spring createBean时遍历postProcessor的代码    @Override    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)            throws BeansException {        Object result = existingBean;        for (BeanPostProcessor processor : getBeanPostProcessors()) {            Object current = processor.postProcessAfterInitialization(result, beanName);            if (current == null) {                return result;            }            result = current;        }        return result;    }就是通过这么一个循环来执行后置办法applyBeanPostProcessorsAfterInitialization,前置办法也是这样的当初假如咱们有一个自定义的beanPostProcessor外面须要注入一个咱们自定义的beanA,那么在beanPostProcessor被实例化的时候必定会要求注入咱们自定义的beanA,那么当初就有多种状况了:    1.咱们用的set或者结构器注入那beanA会被实例化并注入    2.如果咱们用的@Autowired,当咱们自定义的beanPostProcessor实例化    在AutowiredAnnotationBeanPostProcessor实例化之前,那么beanA都无奈被注入值    如果在之后,则还是能够被注入值    然而这两种状况都会打印这行日志Bean 'beanA' of type [org.springframework.beanA] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)

以下示例显示了如何在ApplicationContext中编写,注册和应用BeanPostProcessor实例。

示例:Hello World,BeanPostProcessor-style

第一个示例演示了根本用法。示例展现了一个自定义BeanPostProcessor实现,它在容器创立每个bean时调用该bean的toString()办法,并将后果字符串打印到零碎控制台。

上面的清单显示了自定义的BeanPostProcessor实现类定义:

package scripting;import org.springframework.beans.factory.config.BeanPostProcessor;public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {    // 只需按原样返回实例化的bean    public Object postProcessBeforeInitialization(Object bean, String beanName) {        return bean; // 咱们能够返回任何对象援用    }    public Object postProcessAfterInitialization(Object bean, String beanName) {        System.out.println("Bean '" + beanName + "' created : " + bean.toString());        return bean;    }}

以下beans元素应用InstantiationTracingBeanPostProcessor:

<?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:lang="http://www.springframework.org/schema/lang"    xsi:schemaLocation="http://www.springframework.org/schema/beans        https://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/lang        https://www.springframework.org/schema/lang/spring-lang.xsd">    <lang:groovy id="messenger"            script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">        <lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>    </lang:groovy>    <!--   当上述bean (messenger)被实例化时,这个自定义的BeanPostProcessor实现将事实输入到零碎控制台   -->    <bean class="scripting.InstantiationTracingBeanPostProcessor"/></beans>

请留神实例化tracingbeanpostprocessor是如何定义的。它甚至没有名称,而且,因为它是一个bean,所以能够像其余bean一样进行依赖注入。

上面的Java利用程序运行后面的代码和配置:

import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframework.scripting.Messenger;public final class Boot {    public static void main(final String[] args) throws Exception {        ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");        Messenger messenger = ctx.getBean("messenger", Messenger.class);        System.out.println(messenger);    }}

后面的应用程序的输入相似于以下内容:

Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961org.springframework.scripting.groovy.GroovyMessenger@272961
示例: RequiredAnnotationBeanPostProcessor

将回调接口或注解与自定义BeanPostProcessor实现联合应用是扩大Spring IoC容器的一种常见办法。

一个例子是Spring的AutowiredAnnotationBeanPostProcessor——一个随Spring发行版附带的BeanPostProcessor实现,它确保被注解(@Autowired,@Value, @Inject等注解)正文的属性会被注入一个bean实例。

1.8.2。自定义配置元数据BeanFactoryPostProcessor

咱们要看的下一个扩大点是 org.springframework.beans.factory.config.BeanFactoryPostProcessor。

该接口与BeanPostProcessor次要区别在于:BeanFactoryPostProcessor对Bean配置元数据进行操作。
也就是说,Spring IoC容器容许BeanFactoryPostProcessor读取配置元数据,并有可能在容器实例化实例任何bean之前更改元数据。

您能够配置多个BeanFactoryPostProcessor实例,并且能够BeanFactoryPostProcessor通过设置order属性来管制这些实例的运行程序。然而,仅当BeanFactoryPostProcessor实现 Ordered接口时能力设置此属性。

如果心愿更改理论bean实例(从配置元数据创立的对象),则须要应用BeanPostProcessor。只管在BeanFactoryPostProcessor中应用bean实例在技术上是可行的(例如,通过应用BeanFactory.getBean()),然而这样做会导致过早的bean实例化,违反规范的容器生命周期。这可能会导致负面的副作用,比方绕过bean的后处理。另外,BeanFactoryPostProcessor实例的作用域为每个容器。这只有在应用容器层次结构时才有用。如果您在一个容器中定义了BeanFactoryPostProcessor,那么它只利用于该容器中的bean定义。一个容器中的Bean定义不会被另一个容器中的BeanFactoryPostProcessor实例进行后处理,即便这两个容器属于同一层次结构。

当BeanFactoryPostProcessor在ApplicationContext中申明时,它将主动运行,以便对定义容器的配置元数据利用更改。
Spring包含许多预约义的bean工厂后处理器,如PropertyOverrideConfigurer和PropertySourcesPlaceholderConfigurer。
您还能够应用自定义BeanFactoryPostProcessor例如,用于注册自定义属性编辑器。

ApplicationContext自动检测部署其中实现BeanFactoryPostProcessor接口的任何bean。在适当的时候,这些bean会被bean factory post-processors来应用。

你也能够像部署任何其余bean一样部署这些自定义的bean factory post-processors。

示例:PropertySourcesPlaceholderConfigurer

您能够应用PropertySourcesPlaceholderConfigurer应用规范的Java属性格局将bean定义中的属性值内部化到独自的文件中。这样,部署应用程序的人员就能够自定义特定于环境的属性,比方数据库url和明码,而无需批改主XML定义文件或容器文件的复杂性或危险。

思考以下基于xml的配置元数据片段,其中定义了具备占位符值的数据源:

<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>

该示例显示了从内部Properties文件配置的属性。
在运行时,将 PropertySourcesPlaceholderConfigurer利用于替换数据源的某些属性的元数据。将要替换的值指定为模式的占位符,该模式${property-name}遵循Ant和log4j和JSP EL款式。

理论值来自规范Java Properties格局的另一个文件:

jdbc.driverClassName = org.hsqldb.jdbcDriverjdbc.url = jdbc:hsqldb:hsql://production:9002jdbc.username = sajdbc.password = root

因而,${jdbc.username}在运行时将字符串替换为值“sa”,并且其余与属性文件中的键匹配的占位符值也实用。
在PropertySourcesPlaceholderConfigurer为大多数属性和bean定义的属性占位符查看。此外,您能够自定义占位符前缀和后缀。

    <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">        <property name="locations" value="classpath:jdbc.properties"/>        //自定义前缀后缀        <property name="placeholderPrefix" value="${"/>        <property name="placeholderSuffix" value="}"/>    </bean>
1.8.3。自定义实例化逻辑FactoryBean

您能够org.springframework.beans.factory.FactoryBean为自身就是工厂的对象实现接口。

该FactoryBean接口是可插入Spring IoC容器的实例化逻辑的一点。
如果您有简单的初始化代码,而不是(可能)简短的XML,能够用Java更好地表白,则以创立本人的代码 FactoryBean,
在该类中编写简单的初始化,而后将自定义FactoryBean插入容器。

该FactoryBean界面提供了三种办法:

  • Object getObject():返回此工厂创立的对象的实例。实例能够共享,具体取决于该工厂是否返回单例或原型。
  • boolean isSingleton():true如果FactoryBean返回单例或false其余则返回 。
  • Class getObjectType():返回getObject()办法返回的对象类型,或者null如果类型未知,则返回该对象类型。

FactoryBeanSpring框架中的许多中央都应用了该概念和接口。Spring附带了50多种FactoryBean接口实现。Spring中的理解的少,然而Mybatis的MybatisSqlSessionFactoryBean很闻名。

当您须要向容器询问FactoryBean自身而不是由它产生的bean的理论实例时,请在调用的办法时在该bean的id后面加上“&”符号(&)。
因而,对于给定id为myBean的一个FactoryBean ,调用getBean("myBean")返回的是FactoryBean生成的实例,getBean("&myBean")返回的是FactoryBean自身。

public class MyFactoryBean implements FactoryBean<MyBean> {    @Override    public MyBean getObject() throws Exception {        return new MyBean();    }    @Override    public Class<?> getObjectType() {        return MyBean.class;    }}<bean id="myFactoryBean" class="org.springframework.example.factoryBean.MyFactoryBean"/>getBean("myFactoryBean")  返回的是MyBean实例getBean("&myFactoryBean")  返回的是MyFactoryBean实例