AOP基础概念
AOP(面向切面编程)是一种编程范式,它通过分离关注点(Separation of Concerns, SoC)来提高代码的可维护性和模块化程度。AOP主要依赖于代理设计模式,包括静态代理和动态代理。动态代理分为两种:
- JDK动态代理:当目标对象实现了接口时,Spring AOP使用JDK Proxy机制创建代理对象。
- CGLIB动态代理:当目标对象未实现接口时,Spring AOP使用CGLIB生成目标对象的子类作为代理对象。
代理模式的应用场景
AOP在多种场景下都能发挥重要作用,例如:
- 日志采集
- 权限控制
- 事务管理
- 全局异常处理
- 远程过程调用(RPC)
- 数据源代理
动态代理示例
下面是一个使用CGLIB实现转账业务中事务管理的例子:
- AccountService —— 转账业务逻辑的实现。
- ProxyBeanFactory —— 代理工厂,负责创建事务管理的代理对象。
AccountService:
@Override
public void transfer(String sourceName, String targetName, int money) {
Account sourceAccount = accountMapper.findByAname(sourceName);
Account targetAccount = accountMapper.findByAname(targetName);
sourceAccount.setAmoney(sourceAccount.getAmoney() - money);
targetAccount.setAmoney(targetAccount.getAmoney() + money);
accountMapper.updateById(sourceAccount);
accountMapper.updateById(targetAccount);
}
ProxyBeanFactory:
public class ProxyBeanFactory {
private IAccountService accountService;
private TransactionUtil transactionUtil;
public IAccountService createProxy() {
return (IAccountService) Enhancer.create(
accountService.getClass(),
new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
try {
transactionUtil.beginTx();
result = method.invoke(accountService, args);
transactionUtil.commitTx();
} catch (Exception e) {
transactionUtil.rollbackTx();
} finally {
transactionUtil.closeTx();
}
return result;
}
}
);
}
// Setters for DI
}
applicationContext.xml 配置文件:
<bean id="accountService" class="com.yk.service.AccountServiceImpl">
<property name="accountMapper" ref="mapperImp"/>
</bean>
<bean id="proxyService" factory-bean="factory" factory-method="createProxy"/>
<bean id="factory" class="com.yk.factory.ProxyBeanFactory">
<property name="transactionUtil" ref="transactionUtil"/>
<property name="accountService" ref="accountService"/>
</bean>
<bean id="controller" class="com.yk.controller.AccountControllerImpl">
<property name="accountService" ref="proxyService"/>
</bean>
AOP常用术语
- 连接点:程序执行中的某个特定点,如方法调用。
- 切点:匹配连接点的规则,即AOP要切入的位置。
- 通知:AOP框架执行的代码,可以是在方法前、方法后等位置执行的代码块。
- AOP将抽取出来的共性功能称为通知;
- 通知类型:以通知在上下文中的具体位置作为划分
前置通知(Before)
返回通知(After-returning)
异常通知(After-throwing)
后置通知(After)
环绕通知(Around)
切入点表达式
execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表])) 的语法详解如下:
- 修饰符 可以省略,代表任意。
- 返回类型 可以使用
"*"
代表任意。 - 包名 可以使用
"*"
或".."
代表任意数量的任意名称。 - 类名与方法名 可以使用
"*"
代表任意。 - 参数列表 可以使用
".."
代表任意数量和类型的参数。
示例配置
XML配置:
<aop:config>
<aop:aspect id="aopAspect" ref="logger">
<aop:pointcut id="dian" expression="execution(* com.yk.service.*.*(..))"/>
<aop:before method="beforeMethod" pointcut-ref="dian"/>
<aop:after-returning method="afterReturningMethod" pointcut-ref="dian"/>
<aop:after-throwing method="afterThrowingMethod" pointcut-ref="dian"/>
<aop:after method="afterMethod" pointcut-ref="dian"/>
<aop:around method="aroundMethod" pointcut-ref="dian"/>
</aop:aspect>
</aop:config>
注解形式:
@Aspect
@Component
public class TransactionAspect {
@Autowired
private TransactionUtil transactionUtil;
@Pointcut("execution(* com.yk.service.*.*(..))")
public void dian() {}
@Before("dian()")
public void beforeService() {
System.out.println("前置通知");
}
@AfterReturning("dian()")
public void afterReturningMethod() {
System.out.println("返回通知");
}
@AfterThrowing("dian()")
public void afterThrowingMethod() {
System.out.println("异常通知");
}
@After("dian()")
public void afterMethod() {
System.out.println("后置通知");
}
@Around("dian()")
public Object aroundService(ProceedingJoinPoint point) {
Object result = null;
try {
transactionUtil.beginTx();
result = point.proceed();
transactionUtil.commitTx();
} catch (Throwable e) {
transactionUtil.rollbackTx();
e.printStackTrace();
System.out.println("异常通知");
} finally {
transactionUtil.closeTx();
}