- AOP就是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP使得业务逻辑各个部分的耦合度降低,提高程序的可重用性。
- 为什么使用AOP?
我们写的代码难免有些运行错误,通常情况下,我们需要对错误进行包装,不能直接返回异常信息发给前端。
但是我们的前端很容易臃肿,就像我在公司里面写的一样
@GetMapping(value = "kmp")
public BaseResponse kmp(@RequestParam String big, @RequestParam String small) {
logger.info("kmp request,big=%s,small=%s", big, small);
BaseResponse baseResponse = new BaseResponse();
try {
baseResponse.result = analyticsService.stringIndex(big, small);
} catch (Exception e) {
logger.info("kmp error,error=%s", e);
return new BaseResponse(BaseError.FAIL);
}
logger.info("kmp response,result=%s", baseResponse);
return baseResponse;
}
这些代码看起来就是很臃肿,发现里面处理很多错误信息,但是其实只是执行了一种方法:
baseResponse.result = analyticsService.stringIndex(big, small);
看起来有点大费周章了。
- 接下来我们可以使用AOP来解决这种问题
(1)环境配置
在pom.xml中添加依赖的包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
(2)如何使用AOP
我们改造上面的方法:
@GetMapping(value = "kmp")
public Object kmp(@RequestParam String big, @RequestParam String small) {
return analyticsService.stringIndex(big, small);
}
改造成只是简单的调用方法,然后直接返回。
那么问题来了,我们日志呢?我们的异常处理呢?
我们可以在切面里做处理
@Aspect
@Component
public class ExceptionAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 在方法调用之前,打印入参
*/
@Before(value = "execution(public * com.huyan.demo.controller.AnalyticsController.*(..))")
public void before(JoinPoint joinPoint) {
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
StringBuilder params = new StringBuilder();
for (Object arg : args) {
params.append(arg).append(" ");
}
logger.info(className + "的" + methodName + "入参为:" + params.toString());
}
/**
* 过程中监测,catch到异常之后返回包装后的错误信息,并打印日志
*/
@Around(value = "execution(public * com.huyan.demo.controller.AnalyticsController.*(..))")
public BaseResponse catchException(ProceedingJoinPoint joinPoint) {
try {
BaseResponse baseResponse = new BaseResponse(BaseError.SUCCESS);
baseResponse.result = joinPoint.proceed();
return baseResponse;
} catch (Throwable e) {
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
logger.warn("在" + className + "的" + methodName + "中,发生了异常:" + e);
return new BaseResponse(BaseError.FAIL);
}
}
/**
* 返回之后,打印出参
*/
@AfterReturning(value = "execution(public * com.huyan.demo.controller.AnalyticsController.*(..))", returning = "returnVal")
public void afterReturin(JoinPoint joinPoint, Object returnVal) {
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
logger.info(className + "的" + methodName + "结果为:" + returnVal.toString());
}
}
看到上面的信息可以看到注解里面的值就是我们这个切面覆盖的范围。使用execution表达式来定义切点。
(3)AOP优势
看到这个切面类,我们可以清晰的看到,这个方法异常处理类统一放到异常处理类中,极大的减少了代码量。使得业务层业务逻辑看起来更加清晰。
(4)AOP的其他应用场景
性能监控,访问统计以及权限验证也是AOP经典场景
发现注解里面可以我们执行这个处理的文件范围,也可以是我们我自己定义的注解,然后让这个注解去放在方法上,让这个处理生效。