4、Container 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接口,那么容器就不会去进行依赖注入)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值