基于XML的SpringAOP开发
在XML中配置AOP
<!-- AOP配置 -->
<aop:config>
<!-- 配置切入点:告诉spring框架哪些方法需要被增强 -->
<aop:pointcut expression="execution(* cn.itcast.dao.impl.CustomerDaoImpl.save(..))" id="pointcut1"/>
<!-- 配置切面:告诉spring框架调用切面类中的哪个方法来增强 -->
<aop:aspect ref="myAspectXml">
<aop:before method="writeLog" pointcut-ref="pointcut1"/>
</aop:aspect>
</aop:config>
- 切入点表达式语法:[修饰符] 返回类型 包名.类名.方法名(形式参数)
- 常见写法:
execution(public * *(..)) 所有的public方法
execution(* set*(..)) 所有set开头的方法
execution(* com.xyz.service.AccountService.*(..)) AccountService类中的所有方法
execution(* com.xyz.service.*.*(..)) com.xyz.service包下所有的方法
execution(* com.xyz.service..*.*(..)) · com.xyz.service包及其子包下所有的方法
AOP的通知类型
<!-- 配置AOP-->
<aop:config>
<!-- 配置切入点(Pointcut):告诉框架哪些方法需要被增强 -->
<aop:pointcut expression="execution(* cn.itcast.dao.impl.CustomerDaoImpl.save(..))" id="pointcut1"/>
<!-- 配置切面(Aspect):告诉框架调用切面的哪个方法来增强切点方法 以及通知类型 <aop:before /> -->
<aop:aspect ref="myAspectXml">
<aop:before method="writelog" pointcut-ref="pointcut1"/>
</aop:aspect>
</aop:config>
前置通知
应用:权限控制(权限不足抛出异常)、记录方法调用信息日志
<!-- 配置前置通知:告诉spring在目标方法执行之前增强 -->
<aop:before method="before" pointcut-ref="pointcut1"/>
切面类中的增强方法
/**
* 前置通知方法 应用场景: 权限控制 (权限不足,抛出异常)、 记录方法调用信息日志
* joinPoint:连接点,指的是被增强的那个方法
*/
public void before(JoinPoint joinPoint) {
String username = "rose";
if (!"admin".equals(username)) {
// 非admin用户,不具备权限,抛出异常
//joinPoint.getTarget().getClass().getName()获取目标类的名字
//joinPoint.getSignature().getName()获取被增强方法的名字
throw new RuntimeException("对不起!您没有对" + joinPoint.getTarget().getClass().getName() + "类中"
+ joinPoint.getSignature().getName() + "方法的访问权限");
}
}
后置通知
特点:在目标方法运行后返回值后再增强代码逻辑
应用:于业务相关的, 如银行在存取款结束后的发送短信消息
<!-- 配置后置通知 -->
<!-- returning="result" 目标方法返回值为result-->
<aop:after-returning method="afterReturning" pointcut-ref="pointcut2" returning="result"/>
方法
/**
* 后置通知方法
* 应用场景: ATM取款机取款后,自动下发短信
* 参数result:被增强那个方法的返回值
*/
public void afterReturning(JoinPoint joinPoint,Object result){
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = sdf.format(date);
System.out.println("尊敬的用户,您于" + dateStr + "取款" + result + "元");
}
}
环绕通知
特点 :目标方法执行前后,都进行增强(控制目标方法执行)
应用 :日志、缓存、权限、性能监控、事务管理
增强代码的方法要求:
- 接收的参数:ProceedingJoinPoint(可执行的连接点)
- 返回值: Object返回值
- 抛出Throwable异常
<!-- 配置环绕通知 -->
<aop:around method="around" pointcut-ref="pointcut3"/>
/**
* 环绕通知方法
* 应用场景:事务处理
* @param proceedingJoinPoint 正在执行的连接点
*/
public Object around(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("开启事务");
//获取目标方法的参数
Object[] args = proceedingJoinPoint.getArgs();
Object result = null;
try {
//调用目标方法,获取目标方法的返回值
result = proceedingJoinPoint.proceed(args);
System.out.println("提交事务");
} catch (Throwable e) {
System.out.println("回滚事务");
}
//返回目标方法的返回值
return result;
}
}
异常抛出通知
作用:目标代码出现异常,通知执行。记录异常日志、通知管理员(短信、邮件)
应用场景:处理异常(一般不可预知),记录日志
<!-- 异常抛出通知 -->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>
/**
* @param joinPoint
* @param ex 目标方法抛出的异常 要与配置文件命名一致
*/
public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
System.out.println("注意了:在" + joinPoint.getTarget().getClass().getName() + "中的"
+ joinPoint.getSignature().getName() + "方法中发生了异常:" + ex.getMessage());
}
最终通知
作用:不管目标方法是否发生异常,最终通知都会执行(类似于finally代码功能)
应用场景:释放资源 (关闭文件、 关闭数据库连接、 网络连接、 释放内存对象 )
<!-- 最终通知 -->
<aop:after method="after" pointcut-ref="pointcut4" />
public void after(JoinPoint joinPoint){
System.out.println("开始释放资源,对应的连接点信息为:" + joinPoint.getTarget().getClass().getName() + "的"
+ joinPoint.getSignature().getName() + "方法");
}
注意,最终通知和后置通知的区别:最终通知,不管异常与否,都执行;而后置通知在异常时不执行。