面向切面编程(Aspect Oriented Programming) 是软件编程思想发展到一定阶段的产物,是面向对象编程的有益补充。AOP一般适用于具有横切逻辑的场合,如访问控制、事务管理、性能检测等。
日志、异常处理、事务控制等都是一个健壮的业务系统所必须的。但是为了保证系统健壮可用,就要再众多业务方法中反复编写类似的代码,使得原本就很复杂的业务处理代码变得更加复杂。业务功能的开发者还要考两次这些额外的代码是否处理正确,是否有遗漏的地方,如果需要修改日志信息的格式或者安全验证的规则,或者再增加辅助功能,都会导致业务代码频繁而大量的修改。
面向切面编程,就是在不改变原程序的基础上为代码增加新的功能,对代码段进行增强处理。他的设计思想来源于代理设计模式。
1.在原对象方法之前插入的增强处理为前置增强
2.该方法执行完以后插入的增强处理为后置增强
3.环绕在方法前后的增强处理为环绕增强,是最强大的增强处理,可以获取或者修改目标方法的参数、返回值、异常处理、甚至决定目标方法是否执行。
4.该方法抛出异常时的增强处理为异常抛出增强
5.最终增强处理,无论方法抛出异常还是正常退出都会得到执行,类似于异常处理机制中finally块的作用,一般用于释放资源
在Springboot中使用注解
需要引入所需要的jar :spring-boot-starter-aop
创建一个aop增强处理类
@Slf4j @Aspect @Component public class LoggerAspect { // 匹配 com.lzz.lzzapp.common.user包及子包下所有类的所有方法 @Pointcut("execution(* com.lzz.lzzapp.common.user..*.*(..))") public void logPointCut(){ } //前置增强 在连接点执行之前执行的通知 @Before("logPointCut()") public void before(){ log.info("即将调用业务方法"); } //最终增强 在连接点执行之后执行的通知(返回通知和异常通知的异常) @After("logPointCut()") public void after(){ log.info("业务方法调用完成"); } /** * 后置增强 在连接点执行之后执行的通知(返回通知) */ @AfterReturning(value="logPointCut()",returning="result") public void doAfterReturning(JoinPoint joinPoint, Object result){ String methodName = joinPoint.getSignature().getName(); log.info("调用"+joinPoint.getTarget()+"的"+methodName+"方法。参数:"+Arrays.toString(joinPoint.getArgs()) +"。方法返回值:"+result); } /** * 异常增强 在连接点执行之后执行的通知(异常通知) */ @AfterThrowing("logPointCut()") public void doAfterThrowing(){ log.info("异常处理完成"); } /** * 环绕增强 */ @Around("logPointCut()") public void doAround(ProceedingJoinPoint jp){ log.info("调用"+jp.getTarget()+"的"+jp.getSignature().getName()+"方法。参数:"+Arrays.toString(jp.getArgs())); try { Object result=jp.proceed();//执行目标方法 log.info("方法返回值:"+result); }catch (Throwable e){ log.error(jp.getSignature().getName()+"方法发生异常"); e.printStackTrace(); } } }
使用@Aspect定义切面,@Pointcut定义切入点
切入点匹配的执行点为连接点为JointPoint,Spring会自动注入该实例,通过joinpoint 的getTarget()方法可以得到被代理对象,getSignature()方法返回被代理的目标方法。getArgs()方法返回传递给目标方法的参数数组
对于后置增强,还可以定义一个用于接收被代理方法的返回值,必须在 @AfterReturning 注解中通过 returning 属性指定该参数的名称
execution是切入点指示符,他括号中是一个切入点表达式,可以配置要切入的方法,切入点表达式支持模糊匹配
public * addUser(com.entity.User) * 表示匹配所有类型的返回值 public void *(com.entity.User) * 表示匹配所有方法名 public void addUser(..) .. 表示匹配所有参数个数和类型 * com.user.*.*(..) 表示匹配com.entity包下所有类的所有方法 * com.user..*.*(..) 表示匹配com.entity包及其子包下所有类的所有方法