04Spring_bean 后处理器(后处理Bean),BeanPostProcessor ,bean创建时序,动态代理

这篇文章很重要,讲解的是动态代理,以及bean创建前后的所发生的事情。介绍一个接口:在Spring构造Bean对象过程中,有一个环节对Bean对象进行 后处理操作 (钩子函数) ----- Spring 提供 BeanPostProcessor 接口。我们可以自定义类,实现 BeanPostProcessor 接口,配置到Spring容器中,在构造对象时,spring容器会调用接口中方法。

这个接口两个方法public Object postProcessAfterInitialization(Object bean, String beanName) 方法以及public Object postProcessBeforeInitialization(final Object bean, String beanName) 这两个方法里面的bean就是spring IO容器创建对象,beanName就是Sring配置文件applicationContext.xml文件里面对应的bean id。

为了说明postProcessBeforeInitialization和postProcessAfterInitialization的具体调用顺序,举一个例子说明。

//applicationContext.xml
<bean id="teacherService" class="cn.csdn.cyclelife.TeacherService"  init-method="init" destroy-method="destroy">
 <constructor-arg type="java.lang.Integer" index="0">
 <value>20</value>
  </constructor-arg>
 <property name="name">
 <value>Longmanfei</value>
 </property>
 </bean>
<bean id="postService"class="cn.csdn.cyclelife.PostService"></bean>
//TeacherService bean
public class TeacherService {

    private String name;
 
    private Integer age;
	
	
	public void setName(String name){
		System.out.println("----这是teacherservice的set方法----");
		this.name=name;
	}
	
	public TeacherService(Integer age){
		this.age=age;
	}
	

	public void init(){
		System.out.println("--------这是teacherservice的init的方法-------------");
	}
	
	public void destroy(){
		System.out.println("---------这是销毁(destroy)方法----------");
	}
	
	public void display(){
		System.out.println(this.name+"-----------------"+this.age);
	}
 }
// 实现接口的BeanPostProcessor bean 
 public class PostService implements BeanPostProcessor{

	
	/**在初始化之后调用这个方法*/
	@Override
	public Object postProcessAfterInitialization(Object bean, String arg1)
			throws BeansException {
		System.out.println("----这是init之后执行的方法postProcessAfterInitialization----");
		return bean;
	}

	/**在初始bean之前调用的这个方法 在init方法之前执行,在set方法之后*/
	@Override
	public Object postProcessBeforeInitialization(Object bean, String arg1)
			throws BeansException {
		/**instanceof 判断前者是否是后者的一个实例*/
		if(bean instanceof TeacherService){
			System.out.println("--这是在init之前进行修改bean的属性值--");
			/*这里我们不能直接new一个对象 因为bean本身就是一个对象,直接转换就可以了*/
			((TeacherService)bean).setName("Longmanfei");
		}
	    System.out.println("---这是init之前执行的方法postProcessBeforeInitialization---");
		return bean;
	}
}
//Junit 测试方法
public class App {
	
	
	@Test
	public void test1(){
		/**加载容器*/
		ApplicationContext ac = new ClassPathXmlApplicationContext(new String[]{"applic*.xml"});
		/**调用getbean方法*/
		TeacherService ts = (TeacherService) ac.getBean("teacherService");
		
		
		ts.display();
		
		/**调用close方法关闭bean*/
		AbstractApplicationContext aac =(AbstractApplicationContext) ac;
		aac.close();
	}

}
//这是执行结果(当加载容器的时候会判断是否有实现接口的BeanPostProcessor bean,如果有其他Bean就会按照特定的顺序去执行,并且执行实现接口的bean里的方法)
----这是teacherservice的set方法----
--这是在init之前进行修改bean的属性值--
----这是teacherservice的set方法----
---这是init之前执行的方法postProcessBeforeInitialization---
--------这是teacherservice的init的方法-------------
----这是init之后执行的方法postProcessAfterInitialization----
Longmanfei-----------------20
---------这是销毁(destroy)方法----------

 

这个例子里面可以看到Spring先会创造bean的实例对象,然后调用postProcessBeforeInitialization,然后再调用init-method="init"方法,然后再调用postProcessAfterInitialization方法。

这里我给一个bean的创建流程图

这个图对上面的例子的代码运行结果给出了很好的解释。(这个博客非常不错http://uule.iteye.com/blog/2094609)

换句话说我们在执行public Object postProcessBeforeInitialization(final Object bean, String beanName)方法时,传入的值bean就是IOC容器已经给我们创建好的对象了,

那么我们可以拿这个对象做什么呢?很重要的一点就是动态代理,我们可以给这个bean对象做个动态代理。

先给出具体代码我在做分析:

做动态代理必须要接口,所以我先给出抽象角色(接口)

package cn.itcast.spring.d_lifecycle;

public interface IHello {
    public void sayHello();

    public void setup();

    public void teardown();
}

 

再给出真实角色:

package cn.itcast.spring.d_lifecycle;

/**
 * Bean对象,初始化和销毁方法 (无返回值、无参数、非静态)
 * 
 * @author seawind
 * 
 */
public class LifeCycleBean implements IHello {
    public LifeCycleBean() {
        System.out.println("LifeCycleBean 构造...");
    }
//配置文件中init-method="setup"
    public void setup() {
        System.out.println("LifeCycleBean 初始化...");
    }
//配置文件中destroy-method="teardown"
public void teardown() {
System.out.println(
"LifeCycleBean 销毁...");
}
//被代理的方法
@Override
public void sayHello()
{

System.out.println(
"hello ,itcast...");

}
}

再给出实现了BeanPostProcessor接口的方法:

package cn.itcast.spring.d_lifecycle;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * 后处理器
 * 
 * @author seawind
 * 
 */
public class MyBeanPostProcessor implements BeanPostProcessor {
    static int a=0;
    @Override
    /**
     * bean 代表Spring容器创建对象
     * beanName 代表配置对象对应 id属性
     */
    
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean")) {
            System.out.println("后处理器 初始化后执行...");
        }
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
        // 针对bean id 为 lifeCycleBean的对象 进行代理,配置文件中bean的id为lifeCycleBean就做一下处理
        if (beanName.equals("lifeCycleBean")) {
            System.out.println("后处理器 初始化前执行...");
/*给传进来的bean对象做一个动态代理.bean.getClass().getClassLoader表示要被执行代理的类,也就是我们的IOC容器创建的bean对象。
bean.getClass().getInterfaces()表示我们的要代理的类所实现
的所有的而接口,我们最后new出来的代理类会按照这个参数实现这些所有的接口。这也是为什么动态代理模式必须要用接口的原因了。
new InvocationHandler() {}表示真正要执行的方法。
最后用的是return 就是把生成出来的的代理类返回了。所以执行好这个方法后其实的返回的是new 出来的的代理类,而不是之前的bean对象了。(这句话非常重要)
*/
return Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //模拟代理方法(额外要执行的方法)
System.out.println("执行代理.....");
//执行要真正的方法 return method.invoke(bean, args); } }); }
return bean; } }

再给出Spring的配置文件(applicationContext.xml)

<bean id="lifeCycleBean" class="cn.itcast.spring.d_lifecycle.LifeCycleBean" 
        init-method="setup" destroy-method="teardown" />

最后给出Junit的测试代码

package cn.itcast.spring.d_lifecycle;

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class LifeCycleTest {
    @Test
    public void testInitDestroy() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        IHello lifeCycleBean = (IHello) applicationContext.getBean("lifeCycleBean");
        System.out.println(lifeCycleBean);
        lifeCycleBean.sayHello();

        // 必须手动调用 容器销毁的方法 --- web服务器tomcat,自动调用容器销毁
        applicationContext.close();
    }
}

 

对上面代码的分析:经过public class MyBeanPostProcessor implements BeanPostProcessor里面的postProcessBeforeInitialization方法后,就是给IOC创建的bean对象进行了动态代理,在Junit的测试代码中。我们执行了 lifeCycleBean.sayHello();就会被动态代理给拦截,执行 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {......}里面的方法.这里的lifeCycleBean已经不是lifeCycleBean.class类型了,而是com.sun.proxy.$Proxy4类型了,要验证这个观点很简单,只要在public class LifeCycleTest 方法中的 lifeCycleBean.sayHello();之前加一句System.out.println(lifeCycleBean);就可以得到验证。

最后给出上述代码的是运行结果:

后处理器 初始化前执行...
执行代理.....
被代理的方法是public abstract void cn.itcast.spring.d_lifecycle.IHello.setup()a的值是1aaaaacn.itcast.spring.d_lifecycle.LifeCycleBeanLifeCycleBean 初始化...
后处理器 初始化后执行...
执行代理.....
被代理的方法是public java.lang.String  java.lang.Object.toString()

aaaaacn.itcast.spring.d_lifecycle.LifeCycleBeancn.itcast.spring.d_lifecycle.LifeCycleBean@a0430b6
测试方法中lifeCycleBean的真相是com.sun.proxy.$Proxy4执行代理.....

被代理的方法是public abstract void cn.itcast.spring.d_lifecycle.IHello.sayHello()a的值是3aaaaacn.itcast.spring.d_lifecycle.LifeCycleBeanhello ,itcast...

这里很奇怪,为什么执行了这么多的“执行代理” 原因很简单,因为setup() System.out.println(lifeCycleBean)和 lifeCycleBean.sayHello();每一次执行方法时都会被动态代理所拦截,从而执行了三次动态代理。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值