Spring中被代理对象内部调用
众所周知,Spring AOP是基于动态代理实现的,最近被同事问到在被代理对象中直接进行内部调用,拦截器或切面不生效的问题,在这里做一个简单的记录。
情景重现
Spring版本:5.1.6
1 2 3 4 5 6 7 8 9 10 11 12
| @Service public class Impl implements Service{
public void a(){ b(); } public void b(){ } }
|
如上所示,在Impl类外部调用a方法时,针对b方法的拦截器或切面并不能生效。原因是这种情形下,this代表被代理对象,调用并没有通过代理对象执行。
解决方案一
确保使用CGLIB并将代理对象暴露给用户。
1
| @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
|
获取代理对象,强制转型为被代理对象(这里牵扯到CGlLIB的特点,代理对象其实是被代理对象的子类实例)
1 2 3 4
| //继续使用上面的例子 public void a(){ ((Impl)AopContext.currentProxy()).b(); }
|
解决方案二
Bean加载后,向内部注入增强对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Service public class Impl implements Service{
@Autowired //注入上下文 private ApplicationContext context; private Impl proxy;//表示代理对象,不是目标对象 @PostConstruct //③ 初始化方法 private void setSelf() { //此种方法不适合于prototype Bean,因为每次getBean返回一个新的Bean proxy = context.getBean(Impl.class); }
public void a(){ proxy.b(); } public void b(){ } }
|
解决方案三
方案二需要类自己实现注入增强对象的方法,可以通过BeanPostProcessor在目标对象中注入,这样便提高了代码复用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public interface BeanSelfAware { void setSelf(Object proxy); }
@Service public class Impl implements Service, BeanSelfAware{ private Impl proxy;//表示代理对象,不是目标对象 private void setSelf() { //此种方法不适合于prototype Bean,因为每次getBean返回一个新的Bean proxy = context.getBean(Impl.class); }
public void a(){ proxy.b(); } public void b(){ } }
@Component public class InjectBeanSelfProcessor implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { //如果Bean实现了BeanSelfAware标识接口,就将代理对象注入 if(bean instanceof BeanSelfAware) { //即使是prototype Bean也可以使用此种方式 ((BeanSelfAware) bean).setSelf(bean); } return bean; } }
|