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