Spring Boot进阶:原理、实战与面试题分析
上QQ阅读APP看书,第一时间看更新

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;
    }
}