关于spring:一起来读官方文档SpringIOC07

1.8。容器扩大点

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

1.8.1。自定义bean实现BeanBeanPostProcessor接口

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

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

@Component
public 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@272961
org.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.jdbcDriver
jdbc.url = jdbc:hsqldb:hsql://production:9002
jdbc.username = sa
jdbc.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实例

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理