4Container Extension Points(spring容器的扩展)
Posted yuanGrowing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4Container Extension Points(spring容器的扩展)相关的知识,希望对你有一定的参考价值。
4.1 BeanPostProcessor和BeanFactoryPostProcessor接口
4.1.1 BeanPostProcessor接口
文档中有一个对于BeanPostProcessor的描述:
BeanPostProcessors operate on bean (or object) instances; that is to say, the Spring IoC container instantiates a bean instance and then BeanPostProcessors do their work.
springioc容器实例化一个bean,然后BeanPostProcessor执行操作,它的操作有两个,postProcessBeforeInitialization和postProcessAfterInitialization。
从字面上来看,这个接口的两个方法分别是在bean实例化之前和实例化之后执行的,但是从文档的描述上来看,是spring容器先实例化bean然后 才执行BeanPostProcessor的操作。至于到底是怎么样的,我们就来试一试吧。
先实现一个BeanPostProcessor
public class MyBeanPostProcessor implements BeanPostProcessor
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException
System.out.println(o.hashCode());
System.out.println("a bean named "+s+" will be created!");
return o ;
public Object postProcessAfterInitialization(Object o, String s) throws BeansException
System.out.println(o.hashCode());
System.out.println("a bean named "+s+" has bean created");
return o;
我们可以看到,这个接口的两个方法有一样的参数,你也许会猜测Object参数是实例化好的bean,String参数是bean的id(yes,就是这样)。
写一个Student类来进行测试
public class Student
private String name ;
private int age ;
private String school ;
public void init()
System.out.println("do init");
//get/set方法
spring配置文件
<bean id="mypostprocessor" class="com.example.iocContainer.MyBeanPostProcessor"></bean>
<bean id="student" class="com.example.iocContainer.Student" init-method="init">
<property name="name" value="mj"/>
<property name="age" value="23"/>
<property name="school" value="njust"/>
</bean>
测试代码
ApplicationContext app = new ClassPathXmlApplicationContext("spring-main.xml");
Student student = (Student) app.getBean("student");
System.out.println(student.getName()+" "+student.getAge()+" "+student.getSchool());
System.out.println(student.hashCode());
结果:
390689829
a bean named student will be created!
do init
390689829
a bean named student has bean created
mj 23 njust
390689829
从结果我们看到,有三个一样的hashcode,说明BeanPostProcessor的确是在bean实例化好之后才执行的,而且会将实例化好的bean作为参数传入接口的两个方法中,方法中的另一个参数也的确是bean的名称。postProcessBeforeInitialization函数是在bean实例化之后init函数之前执行,postProcessAfterInitialization是在init函数之后执行。
思考
我觉得这个BeanPostProcessor实现了一种切面编程,bean里面的init被做成了一个切面。在实例化bean的时候,容器应该使用了一种动态的代理,这样就能实现这样的切面编程。等下次开始看源码的时候应该就能发现其中的奥秘了。
文档中有这么一段描述:
AOP auto-proxying is implemented as a BeanPostProcessor itself, neither BeanPostProcessors nor the beans they reference directly are eligible for auto-proxying, and thus do not have aspects woven into them.
aop自动代理功能里面就实现了诸如BeanPostProcessor的接口,所以我们不需要对BeanPostProcessor或者bean做切面就能实现切面编程。
4.1.2 BeanFactoryPostProcessor接口
这个接口有一个方法,postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory);实现这个方法的类将在spring容器读取玩xml配置数据以后实例化bean之前执行,它提供了一种动态改变配置元数据的方法。configurableListableBeanFactory这个参数可以获取到spring容器中的很多很多的信息,我只尝试用了一个getBeanDefinition方法。用这个方法可以得到或者改变对于某个bean的定义。
下面就来实验一下
两个测试用的bean:(省略get、set方法)
public class Teacher
private String name ;
private int age ;
private String school;
private String role ;
public class Student
private String name ;
private int age ;
private String school ;
private String role ;
实现BeanFactoryPostProcessor接口
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException
BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("teacher");
System.out.println("beanclassname before change:" + beanDefinition.getBeanClassName());
beanDefinition.setBeanClassName("com.example.iocContainer.Student");
System.out.println("beanclassname after change:"+beanDefinition.getBeanClassName());
在这个方法里面,我取到teacher的bean,然后将teacher的bean改为Student。
配置xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="mybeanfactorypostprocessor" class="com.example.iocContainer.MyBeanFactoryPostProcessor"></bean>
<bean id="student" class="com.example.iocContainer.Student">
<property name="name" value="studenta"/>
<property name="age" value="23"/>
<property name="school" value="njust"/>
<property name="role" value="student"/>
</bean>
<bean id="teacher" class="com.example.iocContainer.Teacher">
<property name="name" value="teachera"/>
<property name="age" value="41"/>
<property name="role" value="teacher"/>
<property name="school" value="njust"/>
</bean>
</beans>
测试代码:
ApplicationContext app = new ClassPathXmlApplicationContext("spring-main.xml");
Student student = (Student) app.getBean("teacher");
System.out.println(student.getName()+" "+student.getAge()+" "+student.getSchool()+" "+student.getRole());
System.out.println(student.hashCode());
结果:
信息: Loading XML bean definitions from class path resource [spring-main.xml]
beanclassname before change:com.example.iocContainer.Teacher
beanclassname after change:com.example.iocContainer.Student
三月 29, 2016 11:27:31 上午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@71e9ddb4: defining beans [mybeanfactorypostprocessor,student,teacher]; root of factory hierarchy
teachera 41 njust teacher
854487022
从结果的第二、三行和倒数两行中我们看到,的确完成了一个bean的class类型的转换。
思考:
从上面的两个接口使用中,对于bean的产生有了更深的了解,spring容器启动的时候,首先是读取xml中的数据,然后如果有BeanFactoryPostProcessor,那么就执行BeanFactoryPostProcessor.postProcessBeanFactory方法,修改或者获取元数据。然后spring就开始实例化,每一次实例化都会涉及到BeanPostProcessor(当然,如果没有配置的话那就没有关系了)。实例化完了之后就是依赖注入了。
4.2 PropertyPlaceholderConfigurer和PropertyOverrideConfigurer
这是在spring中的两个类,用来配置xml文件中的数据
4.2.1 PropertyPlaceholderConfigurer
简单的说,这就是一个用$符来配置xml文件数据的类。直接看例子就会了。
写一个test.properties文件:
mj.name=mj
mj.age=23
mj.school=njust
mj.role=student
然后在xml文件中配置:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/example/iocContainer/test.properties"/>
</bean>
<bean id="student" class="com.example.iocContainer.Student">
<property name="name" value="$mj.name"/>
<property name="age" value="$mj.age"/>
<property name="school" value="$mj.school"/>
<property name="role" value="$mj.role"/>
</bean>
(这里的student和之前的student一样)
测试代码:
ApplicationContext app = new ClassPathXmlApplicationContext("spring-main.xml");
Student student = (Student) app.getBean("student");
System.out.println(student.getName()+" "+student.getAge()+" "+student.getSchool()+" "+student.getRole());
结果:
mj 23 njust student
这个的使用很简单,就是在其他文件里面配置好数据,然后利用$符去取这些数据就行了。这里文件的名称可以任意,不一定要.properties结尾,亲测txt、txtas(随便什么结尾)等都可行。
4.2.2 PropertyOverrideConfigurer
这个可以配置将xml文件中的数据进行覆盖重写,直接上例子
同样先新建一个override.properties文件,这里面存的是用来覆盖的数据,格式是beanname.property=value
teacher.name=teacher
teacher.age=42
teacher.school=njust
teacher.role=teacher
xml文件:
<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
<property name="locations" value="classpath:com/example/iocContainer/override.properties"/>
</bean>
<bean id="teacher" class="com.example.iocContainer.Teacher">
<property name="age" value="32"/>
<property name="name" value="hello"/>
<property name="role" value="math teacher"/>
<property name="school" value="njust"/>
</bean>
(这里的teacher和上面的teacher一样)
测试代码
ApplicationContext app = new ClassPathXmlApplicationContext("spring-main.xml");
Teacher teacher = (Teacher) app.getBean("teacher");
System.out.println(teacher.getName()+" "+teacher.getAge()+" "+teacher.getSchool()+" "+teacher.getRole());
结果:
teacher 42 njust teacher
从结果可以看到,bean里的property被覆盖了
4.3 FactoryBean 让类被容器管理,但又能使用自己的依赖注入。实现FactoryBean接口就需要实现三个函数。
在文档中的描述如下:
Object getObject(): returns an instance of the object this factory creates. The instance can possibly be shared, depending on whether this factory returns singletons or prototypes.
返回工厂创建的类,在这个函数里面返回你创建的bean
boolean isSingleton(): returns true if this FactoryBean returns singletons, false otherwise.
返回true说明是单例模式
Class getObjectType(): returns the object type returned by the getObject() method or null if the type is not known in advance.
返回从getObject()函数返回的对象的类型
列子:
Student实现FactoryBean
public class Student implements FactoryBean
static int a = 0;
private String name ;
private int age ;
private String school ;
private String role ;
public Object getObject() throws Exception
System.out.println("factory bean do create studetn"+a);
a++;
Student student = new Student();
student.setAge(18);
student.setName("张三");
student.setRole("student");
student.setSchool("衢州二中");
return student ;
public Class<?> getObjectType()
return Student.class;
public boolean isSingleton()
return true;
//get、set函数
spring文件的配置沿用上面的xml配置,
测试代码:
ApplicationContext app = new ClassPathXmlApplicationContext("spring-main.xml");
Student student = (Student) app.getBean("student");
System.out.println(student.getName()+" "+student.getAge()+" "+student.getSchool()+" "+student.getRole());
结果:
factory bean do create studetn0
张三 18 衢州二中 student
(注意,这里不是将xml文件中配置的数据覆盖了,而是一旦设置了FactoryBean接口,那么容器就不会去进行依赖注入)
以上是关于4Container Extension Points(spring容器的扩展)的主要内容,如果未能解决你的问题,请参考以下文章