Spring 扩展接口 InstantiationAwareBeanPostProcessor 实例化对象处理

上篇文章介绍了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配置

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- bean 配置 -->
    <bean id="oneBean" class="com.gysoft.spring.beanpost.OneBean"  >

        <!-- 构造方法 -->
        <constructor-arg name="name" value="配置文件赋予的名字"/>
    </bean>
    <!-- bean后处理器 类似于filter -->
    <bean id="myInstantiationAwareBeanPostProcessor" class="com.gysoft.spring.beanpost.MyInstantiationAwareBeanPostProcessor"/>

    
</beans>

 

基本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了一次,这个可以理解,但是多出来一次我始终不是就很理解,我推测可能是在代理里面执行的时候又创建了一次,这个问题不是很确定,有知道的可以告知一下。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值