Bean的继承关系
bean的定义包括许多配置信息,例如构造参数、属性值、静态工厂方法名等,子bean定义从父bean定义继承配置数据,子bean可以覆盖或是添加某些值,子bean的定义由ChildBeanDefinition类决定,如果我们使用XML作为配置,方式如下:
<bean id="inheritedTestBean" abstract="true"
class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithDifferentClass"
class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean" init-method="initialize">
<property name="name" value="override"/>
<!-- the age property value of 1 will be inherited from parent -->
</bean>
通过parent属性指定父bean,子bean会继承父bean的scope、父类构造函数参数值、属性值、方法等,子类指定的scope、初始化方法、销毁方法、静态工厂方法等将会覆盖父类中的相应方法或属性(若不覆盖,则默认使用父类的相应方法),依赖、自动装配、依赖检查、懒加载将直接从子bean中获取
我们也可以将上述例子改为:
<bean id="inheritedTestBeanWithoutClass" abstract="true">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBeanWithoutClass" init-method="initialize">
<property name="name" value="override"/>
<!-- age will inherit the value of 1 from the parent bean definition-->
</bean>
此时spring应该会默认组织一个类名,如果我们将abstract=true的类作为某个bean的依赖,或是使用getBean()显示获得abstract=true的bean,则会出现错误提示,容器内部的preInstantiateSingletons()方法会忽略abstract为true的类的定义,abstract为true的类只作为模板(用于继承),不会初始化为bean
Spring IOC容器的扩展
BeanPostProcessor
BeanPostProcessor接口定义了回调方法,我们可以实现这些方法来提供自己的(或覆盖容器的默认)实例化逻辑、依赖解析逻辑等等,我们可以为一个bean定义多个BeanPostProcessor实例,为了控制多个实例执行的顺序,需要继承Ordered接口,接口中的order属性决定了实例执行的顺序,BeanPostProcessor只在一个容器内中有效,
BeanPostProcessor定义了两个回调方法:
public interface BeanPostProcessor {
//bean初始化方法调用前被调用,bean为当前初始化完毕的bean,beanName为bean的名字
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
//bean初始化方法(不是构造函数)调用后被调用
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
BeanPostProcessor接口的实现类必须比其他bean优先实例化,实例化后将作用于所有的Bean,ApplicationContext自动检测继承了BeanPostProcessor接口的类,如果我们用工厂方法返回BeanPostProcessor,那么返回的类型应该是BeanPostProcessor或是其实现类,不论如何,必须让springIOC意识到这是一个继承了BeanPostProcessor的类
除了上述自动检测的方式外,我们也可以通过硬编码的方式显式注册BeanPostProcessor:使用ConfigurableBeanFactory的addBeanPostProcessor方法,用这种方式不会遵循Order接口的order属性,显式注册的顺序就是执行的顺序,硬编码方式比自动检测优先级高
举个例子:
package scripting;
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;
}
}
<?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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang
http://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>
<!--
when the above bean (messenger) is instantiated, this custom
BeanPostProcessor implementation will output the fact to the system console
-->
<bean class="scripting.InstantiationTracingBeanPostProcessor"/>
</beans>
注意到InstantiationTracingBeanPostProcessor没有定义id、name等,但是它仍然可以作为其他bean的依赖,这里简单起见就没写,上面用到了Groovy(官方文档这么写的),暂时不用管
启动类:
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 = (Messenger) ctx.getBean("messenger");
System.out.println(messenger);
}
}
执行结果:
Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961
另外一个例子是Spring已经实现的RequiredAnnotationBeanPostProcessor类,这个类负责保证用@Required注释的属性一定获得了值,否则会抛出异常
使用BeanFactoryPostProcessor自定义配置元数据
在IOC容器初始化任何bean之前(除了BeanFactoryPostProcessor),org.springframework.beans.factory.config.BeanFactoryPostProcessor可以读取配置元数据,并且可以更改其中的值,如果我们想更改bean的实例,那最好使用BeanPostProcessor,因为BeanFactoryPostProcessor比任何Bean都要早实例化(包括BeanPostProcessor),这将导致bean过早实例化,会产生一些负面影响,比如绕过了BeanPostProcessor的处理(此时BeanPostProcessor还没有实例化),BeanFactoryPostProcessor作用的范围为单个容器,spring提供了预先定义好的BeanFactoryPostProcessor的实现类,例如PropertyOverrideConfigurer、PropertyPlaceholderConfigurer,任何BeanFactoryProcessor的实现类都会被spring自动检测
public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
懒加载对于BeanFactoryPostProcessor和BeanPostProcessor的实现类来说是无效的
例子:PropertyPlaceholderConfigurer
PropertyPlaceholderConfigurer可以在运行前将占位符改为实际值:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/foo/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>
实际值来自其他文件夹:
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
在运行的时候,PropertyPlaceholderConfigurer会在创建任何实例之前将占位符变为实际值
PropertyPlaceholderConfigurer除了检查命名空间指定的文件外,还可以指定在文件没有匹配值的情况下是否查看系统属性,通过设置systemPropertiesMode属性来控制该行为,该属性有三种可选值:
1、never(0):不查询系统属性
2、fallback(1):在文件没有匹配值的情况下查找系统属性,这是默认值
3、override(2):首先检查系统属性,然后检查文件指定的值,此时系统属性优先级比文件指定的值高
PropertyPlaceholderConfigurer也可以用来替换类名
例子:PropertyOverrideConfigurer
不同于通过占位符工作的PropertyPlaceholderConfigurer,PropertyOverrideConfigurer是通过覆盖机制工作的,如果配置文件没有对应bean的属性的值,则使用配置(注解、xml、java配置)中指定的值,否则覆盖配置的值,如果有多个PropertyPlaceholderConfigurer实例,基于覆盖机制,最后进行匹配的PropertyPlaceholderConfigurer将获得胜利,PropertyPlaceholderConfigurer的属性文件中的配置格式如下:
beanName.property=value
复合属性名也可以支持,但是属性的每个组成部分(除了最后一部分)都必须非空,例如:
foo.fred.bob.sammy=123
foo、fred、bob都不允许为空,使用PropertyOverrideConfigurer,在xml文件的配置如下:
<bean id="propertyConfigure" class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
<property name="locations" value="classpath:Bean/propertytwo/person.properties">
</property>
</bean>
使用FactoryBean定制实例化逻辑
该接口是一个泛型接口:
org.springframework.beans.factory.FactoryBean接口提供了三种方法:
1、Object getObject():返回该工厂生产的产品
2、boolean isSingleton():判断工厂生产的产品是否为单例(文档说有,但实测没有)
3、Class getObjectType():返回工厂生产的产品的类型
假设有一个实现了org.springframework.beans.factory.FactoryBean接口的类myBean,当我们调用getBean("myBean")方法时,返回的是getObject()返回的对象,如果调用getBean("&myBean"),则会返回myBean