演示如何使用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中完成。

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

被折叠的 条评论
为什么被折叠?



