3.2.3 ProxyFactoryBean
JDK自带的动态代理以及基于CGLIB的动态代理在Spring框架中都得到了应用,最典型的应用场景就是实现AOP。Spring专门提供了一个ProxyFactoryBean类用于手动创建对象代理,并将创建的代理对象作为目标对象的AOP代理。
ProxyFactoryBean提供了一组配置属性用于指定代理的执行行为,比较常见的包括proxyTargetClass和exposeProxy。如果proxyTargetClass属性为true,则仅使用CGLIB创建代理。如果该属性未设置,那么有两种情况:如果目标类实现了接口,则将使用JDK创建代理;反之,将使用CGLIB创建代理。而exposeProxy属性用于设置是否将当前代理暴露给ThreadLocal。如果该属性为true,那么开发人员可以使用AopContext.currentProxy()方法来获取代理对象。
接下来,我们演示如何使用ProxyFactoryBean来创建和管理代理对象。我们继续沿用3.1.2节中所介绍的案例场景。现在让我们为MethodBeforeAdvice接口提供一个实现类。显然从命名上看,这个实现类是方法执行前通知的,如代码清单3-11所示。
代码清单3-11 MethodBeforeAdvice接口实现类代码
public class AccountTransactionInterceptor implements MethodBeforeAdvice{ private static final Logger LOGGER = Logger.getLogger(AccountTransactionInterceptor.class); @Override public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { LOGGER.info("账户交易被拦截"); } }
接着,我们通过Java代码创建一个通知,实现方式如代码清单3-12所示。
代码清单3-12 Advisor实现类代码
@Bean public Advisor accountServiceAdvisor() { AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); pointcut.setExpression("execution(* com.springboot.aop.service.AccountService.doAccountTransaction(..))"); return new DefaultPointcutAdvisor(pointcut, new AccountTransactionInterceptor()); }
最后,我们创建一个ProxyFactoryBean实例,并设置相关属性,如代码清单3-13所示。
代码清单3-13 ProxyFactoryBean实例代码
@Bean public ProxyFactoryBean accountService(){ ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean(); proxyFactoryBean.setTarget(new AccountServiceImpl()); proxyFactoryBean.addAdvisor(accountServiceAdvisor()); proxyFactoryBean.setExposeProxy(true); return proxyFactoryBean; }
注意,这里我们设置目标类为AccountService接口的实现类AccountServiceImpl,并把exposeProxy属性设置为true。这样,我们在AccountServiceImpl中就可以使用Spring AOP提供的AopContext.currentProxy()方法来获取这个代理对象,示例代码如代码清单3-14所示。
代码清单3-14 AccountService实现类代码
public class AccountServiceImpl implements AccountService{ ... @Override public boolean doAccountTransaction(Account source, Account dest, int amount) { ((AccountService)(AopContext.currentProxy())).doAccountTransaction(source, dest, amount); return true; } }