可获得两大新人礼包
36份一线互联网Java面试电子书
84个Java稀缺面试题视频
1. 什么是AOP
AOP:Aspect Oriented Programming的缩写,意为:面向切面编程。面向切面编程的目标就是分离关注点。什么是关注点呢?就是你要做的事,就是关注点。假如你是个公子哥,没啥人生目标,天天就是衣来伸手,饭来张口,整天只知道玩一件事!那么,每天你一睁眼,就光想着吃完饭就去玩(你必须要做的事),但是在玩之前,你还需要穿衣服、穿鞋子、叠好被子、做饭等等等等事情,这些事情就是你的关注点,但是你只想吃饭然后玩,那么怎么办呢?这些事情通通交给别人去干。在你走到饭桌之前,有一个专门的仆人A帮你穿衣服,仆人B帮你穿鞋子,仆人C帮你叠好被子,仆人C帮你做饭,然后你就开始吃饭、去玩(这就是你一天的正事),你干完你的正事之后,回来,然后一系列仆人又开始帮你干这个干那个,然后一天就结束了!
AOP的好处就是你只需要干你的正事,其它事情别人帮你干。也许有一天,你想裸奔,不想穿衣服,那么你把仆人A解雇就是了!也许有一天,出门之前你还想带点钱,那么你再雇一个仆人D专门帮你干取钱的活!这就是AOP。每个人各司其职,灵活组合,达到一种可配置的、可插拔的程序结构。
2. SpringBoot中的AOP处理
2.1 AOP依赖
使用AOP,首先需要引入AOP的依赖。
2.2 实现AOP切面
SpringBoot中使用AOP非常简单,假如我们要在项目中打印一些log,在引入了上面的依赖之后,我们新建一个类LogAspectHandler,用来定义切面和处理方法。只要在类上加个@Aspect注解即可。
@Aspect
@Component
public class LogAspectHandler {
}
这里主要介绍几个常用的注解:
1.@Pointcut:定义一个切面,即上面所描述的关注的某件事入口。
2.@Before:在做某件事之前做的事。
3.@After:在做某件事之后做的事。
4.@AfterReturning:在做某件事之后,对其返回值做增强处理。
5.@AfterThrowing:在做某件事抛出异常时,处理。
具体看如下代码注释:
/**
* 使用AOP处理log
* @author shengwu ni
* @date 2018/05/04 20:24
*/
@Aspect
@Component
public class LogAspectHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 定义一个切面,拦截cn.nxcoder.blog.controller包下的所有方法
*/
@Pointcut("execution(* com.itcodai.demo7.controller.*.*(..))")
public void pointCut() {}
/**
* 在上面定义的切面方法之前执行该方法
* @param joinPoint jointPoint
*/
@Before("pointCut()")
public void doBefore(JoinPoint joinPoint) {
// 获取执行的方法和参数
Signature signature = joinPoint.getSignature();
String classMethod = signature.getDeclaringTypeName() + "." + signature.getName();
logger.info("即将执行方法: {}", classMethod);
}
/**
* 在上面定义的切面方法之后执行该方法
* @param joinPoint jointPoint
*/
@After("pointCut()")
public void doAfter(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
String classMethod = signature.getDeclaringTypeName() + "." + signature.getName();
logger.info("方法{}已经执行完", classMethod);
}
/**
* 在上面定义的切面方法返回后执行该方法,可以捕获返回对象或者对返回对象进行增强
* @param joinPoint jointPoint
* @param Object result
*/
@AfterReturning(pointcut = "pointCut()", returning = "result")
public void doAfterReturning(JoinPoint joinPoint, Object result) {
Signature signature = joinPoint.getSignature();
String classMethod = signature.getDeclaringTypeName() + "." + signature.getName();
logger.info("方法{}执行完毕,返回参数为:{}", classMethod, result);
logger.info("对返回参数进行业务上的增强:{}", result + "增强版");
}
/**
* 在上面定义的切面方法执行抛异常时,执行该方法
* @param joinPoint jointPoint
* @param ex ex
*/
@AfterThrowing(pointcut = "pointCut()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Throwable ex) {
Signature signature = joinPoint.getSignature();
String method = signature.getDeclaringTypeName() + "." + signature.getName();
// 处理异常的逻辑
logger.info("执行方法{}出错,异常为:{}", method, ex);
}
}
JointPoint对象很有用,它可以获取请求的方法名,包括参数(joinPoint.getArgs())等,在@Before中,我们还可以获取用户访问的ip等信息,比如:
// 获取请求的url和ip
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String url = request.getRequestURL().toString();
String ip = request.getRemoteAddr();
在@AfterReturning中,属性returning的值必须要和参数保持一致,否则检测不到。对于返回值的增强,可以根据业务需要做相应的封装,上面只是做个demo。 下面写个Controller来测试一下:
@RestController
public class TestController {
@GetMapping("/test/{name}")
public String testAop(@PathVariable String name) {
return "Hello " + name;
}
}
启动程序,在浏览器中输入:localhost:8080/test/aop看下结果:
即将执行方法: com.itcodai.demo7.controller.TestController.testAop
方法com.itcodai.demo7.controller.TestController.testAop已经执行完
方法com.itcodai.demo7.controller.TestController.testAop执行完毕,返回参数为:Hello aop
对返回参数进行业务上的增强:Hello aop增强版
当然了,除了如上定义切面的方式外,还可以针对某个注解定义切面,比如我们对具有@GetMapping注解的方法做切面,可以如下定义切面:
@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
public void annotationCut() {}
然后在其他方法上使用该切面,就会切入注解是@GetMapping的方法。因为在实际项目中,可能对于不同的注解有不同的逻辑处理,比如@GetMapping、@PostMapping、@DeleteMapping等。所以这种按照注解的切入方式也很常用。
閱讀更多 李紅 的文章