自定义BeanPostProcessor实现对象的代理

本文详细介绍了如何在Spring中利用BeanPostProcessor实现对象的代理,并解决了在代理过程中可能遇到的循环依赖问题。通过自定义BeanPostProcessor和SmartInstantiationAwareBeanPostProcessor,针对Girl和Boy对象的相互依赖进行了增强。文章提供了具体代码示例,展示了如何在postProcessBeforeInitialization或postProcessAfterInitialization方法中进行代理操作,并通过调整配置文件来控制是否启用代理。

Spring源码导读

 

演示如何使用BeanPostProcessor对我们的bean进行代理。并解决循环依赖。

前言:

在前面getSingleton链路跟踪里面,我梳理了,doCreatBean中穿插的post-processor的调用。

用户可以自定义BeanPostProcessor,在Spring容器启动过程中,对Bean进行修改。但是要注意动态代理的循环依赖问题

 

伏笔:

1、需要自定义BeanPostProcessor#postProcessBeforeInitialization (在bean实例化完成之后对bean进行修改)

2、需要自定义 SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference (出现循环依赖时会被调用)

 

场景:

有Girl和Boy对象相互依赖,需要对二者生成的对象进行增强(代理)。

public class Boy {
    private Girl girl;
    public Girl getGirl() {
        return girl;
    }
    public void setGirl(Girl girl) {
        this.girl = girl;
    }
    @BoyAndGirlMethodInterceptor.SHOW
    public void show() {
        System.out.println("I am a boy");
    }
}
public class Girl {
    private Boy boy;
    public Boy getBoy() {
        return boy;
    }
    public void setBoy(Boy boy) {
        this.boy = boy;
    }
    @BoyAndGirlMethodInterceptor.SHOW
    public void show() {
        System.out.println("I am a girl");
    }
}

 

在前面循环依赖问题中我们知道如果只是简单的boy 和 girl 上面的情况是不会有问题的。如果要进行代理,那就需要注意:

public class BoyAndGirlPostProcessor implements SmartInstantiationAwareBeanPostProcessor{
    private final Set<Object> earlyProxyReferences = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        if (bean instanceof Boy || bean instanceof Girl) {
            if (earlyProxyReferences.contains(beanName)) {
                return bean;
            }
            System.out.println("=========================================================================");
            System.out.println("|              getEarlyBeanReference 对" + beanName + "代理                  |");
            System.out.println("=========================================================================");
            BoyAndGirlMethodInterceptor interceptor = new BoyAndGirlMethodInterceptor();
            Object proxyBean = interceptor.warpBean( bean, beanName);
            earlyProxyReferences.add(beanName);
            return proxyBean;
        }
        return bean;
    }
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Boy || bean instanceof Girl) {
            if (earlyProxyReferences.contains(beanName)) {
                return bean;
            }
            System.out.println("=========================================================================");
            System.out.println("|              postProcessBeforeInitialization 对" + beanName + "代理                  |");
            System.out.println("=========================================================================");
            BoyAndGirlMethodInterceptor interceptor = new BoyAndGirlMethodInterceptor();
            Object proxyBean = interceptor.warpBean( bean, beanName);
            earlyProxyReferences.add(beanName);
            return proxyBean;
        }
        return bean;
    }
}

我的BoyAndGirlPostProcessor 实现了SmartInstantiationAwareBeanPostProcessor,SmartInstantiationAwareBeanPostProcessor 是继承BeanPostProcessor的,它是一个聪明的BeanPostProcessor 提供  方法getEarlyBeanReference() 解决循环依赖问题 .

代码中对对象进行代理之前会判断earlyProxyReferences里有没有代理对象,如果有的化,就不再进行代理,返回原来的引用(为了  exposedObject == bean)。

后面附上BoyAndGirlMethodInterceptor的代码,这里的写的比较简单,我只是重点BeanPostProcessor 循环依赖问题

public class BoyAndGirlMethodInterceptor implements MethodInterceptor {
    private Object bean;
    public Object warpBean(Object bean, String beanName) {
        Enhancer enhancer = new Enhancer();
        this.bean = bean;
        enhancer.setSuperclass(bean.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object ret = null;
        if (method.getAnnotation(SHOW.class) != null) {
            if (o instanceof Boy) {
                ret = methodProxy.invoke(bean, objects);
                System.out.println("我秀个恩爱, 我有女朋友");
            }
            if (o instanceof Girl) {
                ret = methodProxy.invoke(bean, objects);
                System.out.println("我秀个恩爱, 我有男朋友");
            }
        } else {
            ret = methodProxy.invoke(bean, objects);
        }

        return ret;
    }
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    protected  @interface SHOW {}

 

circular-get-bean.xml: 将BoyAndGirlPostProcessor加入容器。

<?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-3.1.xsd
        ">
    <bean class="com.jack.ascp.purchase.app.test.spring.getbean.BoyAndGirlPostProcessor"/>

    <bean class="com.jack.ascp.purchase.app.test.spring.getbean.Boy" id="boy">
        <property name="girl" ref="girl"/>
    </bean>
    <bean class="com.jack.ascp.purchase.app.test.spring.getbean.Girl" id="girl">
        <property name="boy" ref="boy"/>
    </bean>
</beans>

 

测试类:

public class CircularGetBeanApplication {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("circular-get-bean.xml");
        Boy boy = context.getBean(Boy.class);
        Girl girl = context.getBean(Girl.class);
        /**
         * 代理对象并没有原对象的Girl属性,只不过调用getGirl时,会调用原对象的getGirl
         */
        boy.show();
        girl.show();
        System.out.println(girl == boy.getGirl());
        System.out.println(boy == girl.getBoy());
    }
}

 

查看结果:

 

大家可以试一下,如果不想进行代理 将circular-get-bean.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-3.1.xsd
        ">
   <bean class="com.jack.ascp.purchase.app.test.spring.getbean.BoyAndGirlPostProcessor"/>
    <bean class="com.jack.ascp.purchase.app.test.spring.getbean.Boy" id="boy">
        <!--<property name="girl" ref="girl"/>-->
    </bean>
    <bean class="com.jack.ascp.purchase.app.test.spring.getbean.Girl" id="girl">
        <property name="boy" ref="boy"/>
    </bean>
</beans>

断开循环引用之后,二者的代理都会在postProcessBeforeInitialization中完成。

补充说明:动态代理不是一定要在 postProcessBeforeInitialization 实现,也可以在BeanPostProcessor#postProcessAfterInitialization中完成。

 

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值