上篇文章介绍了spring的后置处理器,然后查了相关资料,发现它还有一个子接口
InstantiationAwareBeanPostProcessor,后置处理器主要负责对象的初始化,因为对象肯定是先要实例化再去初始化,这个类就变得额外的重要了,负责对象的创建。InstantiationAwareBeanPostProcessor的
public Object postProcessBeforeInstantiation(Class aClass, String s)方法,这个方法是这个实例化的核心他决定实例化哪一个,根据我们返回的值,它实例化的对象不同,如果返回的对象它本身,那么会后面的方法初始化会继续初始化它对象本身,如果返回的是一个代理对象,那么后续的实例化方法不会被执行,只会继续执行初始化方法,去初始化这个代理对象。详细的解释再下面的类里面的注释里。
这里的代理方式我选用了Cglib的代理方式
基本实现如下:
package com.gysoft.spring.beanpost.proxy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @Description
* @Author DJZ-WWS
* @Date 2019/4/8 16:02
*/
public class CglibProxy implements MethodInterceptor {
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("### before invocation");
Object result = method.invoke(target, objects);
System.out.println("### end invocation");
return result;
}
public static Object getProxy(Object target) {
Enhancer enhancer = new Enhancer();
// 设置需要代理的对象
enhancer.setSuperclass(target.getClass());
// 设置代理人
enhancer.setCallback(new CglibProxy(target));
return enhancer.create();
}
}
这个代理产生类使用起来很简单,
只需要如下操作:
Object proxy = CglibProxy.getProxy(new OneBean());
核心代码如下:
依赖的xml配置
基本bean
package com.gysoft.spring.beanpost;
import lombok.Data;
/**
* @Description
* @Author DJZ-WWS
* @Date 2019/5/6 20:52
*/
@Data
public class OneBean {
private String name;
private int age;
private String email;
public OneBean() {
System.out.println("调用了无参构造器");
}
public OneBean(String name) {
this.name = name;
System.out.println("调用了有参构造器");
}
public void doSomeThing(){
System.out.println("OneBean的 doSomeThing方法");
}
}
实例化的接口实现
package com.gysoft.spring.beanpost;
import com.gysoft.spring.beanpost.proxy.CglibProxy;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import java.beans.PropertyDescriptor;
/**
* @Description 这个借口主要作用在于目标对象的实例化过程中需要处理的事情,包括实例化前后过程中以及实例属性的设置
*
* 1、实例化—-实例化的过程是一个创建Bean的过程,即调用Bean的构造函数,单例的Bean放入单例池中
*
* 2、初始化—-初始化的过程是一个赋值的过程,即调用Bean的setter,设置Bean的属性
*
* @Author DJZ-WWS
* @Date 2019/5/7 11:37
*/
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInstantiation(Class aClass, String s) throws BeansException {
/**
* 这个方法是最先执行的,它在目标对象实例化之前调用,返回的值是Object,我们可以返回任何类型的值。
* 这个时候目标对象还没有被实例化,所以这个返回值可以用来代替原本该生成的目标对象的实例(比如代理对象)。
* 如果该方法的返回值代替原本该生成的目标对象,后续只有postProcessorAfterInitialization方法会调用,其他方法不会再调用,否则按照正常的流程走
*/
//在这里返回一个代理对象测试 使用cglib代理
/*if(aClass== OneBean.class){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(aClass);
enhancer.setCallback(new MyMethodInterceptor());
OneBean proxy = (OneBean)enhancer.create();
System.out.print("返回动态代理\n");
return proxy;
}*/
/**
* 当返回的是代理对象的时候,最后获得是代理对象,所以后面的对原本对象的实例化就没有必要执行,后面的对象的初始化就是对代理对象的初始化。
*/
if(aClass==OneBean.class){
Object proxy = CglibProxy.getProxy(new OneBean());
return proxy;
}
System.out.println("实例化之前 postProcessBeforeInstantiation 方法调用"+s);
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object o, String s) throws BeansException {
/**
* 这个方法在目标方法实例化之后调用,这个时候目标对象已经被实例化,但是该实例的属性还没有被设置,都是null.
* 它的返回值是决定要不要调用postProcessValues方法的其中一个因素(因为还有一个因素是mbd.getDependencyCheck());
* 如果该方法返回false,并且不需要check,那么postProcessPropertyValues就不会被忽略不执行,;如果返回true,postProcessPropertyValues就会被执行
*/
System.out.println("实例化之后 postProcessAfterInstantiation 方法调用"+s);
return false;
}
@Override
public PropertyValues postProcessPropertyValues(PropertyValues propertyValues, PropertyDescriptor[] propertyDescriptors, Object o, String s) throws BeansException {
/**
* 这个方法对属性值的修改(这个时候属性值没有被设置,但是我们可以修改原本该设置进去的属性值)
* 如果postProcessAfterInstantiation方法返回false,该方法可能不会被调用。可以在该方法内对属性值进行修改
*/
System.out.println("调用 InstantiationAwareBeanPostProcessor 的 postProcessPropertyValues 方法");
return null;
}
@Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
System.out.println("初始化之前 postProcessBeforeInitialization 调用"+s);
return o;
}
@Override
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
System.out.println("初始化之后 postProcessAfterInitialization 方法调用"+s);
return o;
}
}
测试方法如下:
package com.gysoft.spring.beanpost;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Description
* @Author DJZ-WWS
* @Date 2019/5/7 14:02
*/
public class InstantionAwaredBeanPostTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("instantionAwareBeanPostProcessor.xml");
// OneBean oneBean = app.getBean("$oneBean", OneBean.class);
OneBean oneBean = app.getBean(OneBean.class);
oneBean.doSomeThing();
System.out.println(oneBean); //由于打印对象的时候会默认走toString方法,所以也会走那个方法拦截器。
}
}
对测试里面的一些关键的东西进行简单说明:
1.
postProcessBeforeInstantiation方法里返回的如果是代理对象那么这个对象就是代理对象,如果是正常返回,那么返回的就是spring管理的正常bean,代理对象也会被spring管理。
2.在
oneBean.doSomeThing();里面通过代理对方法进行了增强,
3.打印对象的时候发现只要对象一旦被打印就会调用增强的方法,这个问题也困扰了自己一段时间,其实很简单,
MethodInterceptor类似方法拦截器,看名也会看出来,结合结果可知,我推测应该是调用了对象的toString方法。才会有这个效果。
结果:
调用了无参构造器
调用了无参构造器
初始化之后 postProcessAfterInitialization 方法调用oneBean
### before invocation
OneBean的 doSomeThing方法
### end invocation
### before invocation
### end invocation
OneBean(name=null, age=0, email=null)
在这里我还有个小小的疑问不是很确定
无参构造器被调用了两次,我在创建代理类的时候是new了一次,这个可以理解,但是多出来一次我始终不是就很理解,我推测可能是在代理里面执行的时候又创建了一次,这个问题不是很确定,有知道的可以告知一下。