around> before> around> after> afterReturning
一, 实现示例
光看理论和定义, 很多人可能都觉得很难理解, 其实用法比较简单, 不难的,
我们先来个简单的例子, 看完例子你可能就豁然开朗了,
所谓程序员, 好看书不如多动手:
实现:
1, 添加依赖
- <!-- 8, 集成 AOP -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-aop</artifactId>
- </dependency>
2, 添加切面类 LogAspect
- package com.anson.common.aspect;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.*;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.stereotype.Component;
- import org.springframework.web.context.request.RequestContextHolder;
- import org.springframework.Web.context.request.ServletRequestAttributes;
- import javax.servlet.http.HttpServletRequest;
- import java.util.Arrays;
- /**
- * @description: AOP 切面
- * @author: anson
- * @Date: 2019/12/20 10:11
- */
- @Aspect //1, 添加 AOP 相关注解
- @Component
- public class LogAspect
- {
- private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
- //2, 定义切入点(可以匹配, 注解的方式, 可混用)
- // @Pointcut("execution(public * com.anson.controller.*.*(..))")
- @Pointcut("execution(public * com.anson.controller.*.*(..)) && @annotation(com.anson.common.annotation.LogAnnotation)")
- // @Pointcut("execution(public * com.anson.controller.TestController.get*()) && @annotation(com.anson.common.annotation.LogAnnotation)")
- public void pointcut(){}
- //=========== 通知 多中通知可根据需要灵活选用, 一般 Before ,AfterReturning 即可 =======================
- /**
- * 前置通知: 在连接点之前执行的通知
- * @param joinPoint
- * @throws Throwable
- */
- @Before("pointcut()")
- public void doBefore(JoinPoint joinPoint) throws Throwable {
- // 接收到请求, 记录请求内容
- ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
- HttpServletRequest request = attributes.getRequest();
- // 记录下请求内容
- logger.info("URL :" + request.getRequestURL().toString());
- logger.info("HTTP_METHOD :" + request.getMethod());
- logger.info("IP :" + request.getRemoteAddr());
- logger.info("CLASS_METHOD :" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
- logger.info("ARGS :" + Arrays.toString(joinPoint.getArgs()));
- }
- @AfterReturning(returning = "ret",pointcut = "pointcut()")
- public void doAfterReturning(Object ret) throws Throwable {
- // 处理完请求, 返回内容
- logger.info("RESPONSE :" + ret);
- }
- //==============================================
- @After("pointcut()")
- public void commit() {
- logger.info("after commit");
- }
- @AfterThrowing("pointcut()")
- public void afterThrowing() {
- logger.info("afterThrowing afterThrowing rollback");
- }
- @Around("pointcut()")
- public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
- try {
- System.out.println("around");
- return joinPoint.proceed();
- } catch (Throwable e) {
- e.printStackTrace();
- throw e;
- } finally {
- logger.info("around");
- }
- }
- }
需要注意的是: 上面代码注释 2 的地方[2, 定义切入点(可以匹配, 注解的方式, 可混用)] ;
简单点说就是通过匹配或者注解 (也可两种同时使用) 匹配哪些类的哪些方法;
再直白点说, 就是我们要对哪些类的哪些方法执行处理, 要处理的范围是哪些;
类用 * 作为通配符, 方法用 (..) 表示, 括号中的两点表示匹配任何参数, 包括没有参数;
上面这样其实就可以完成一个切面了,
比如将切面定义那里改为:@Pointcut("execution(public * com.anson.controller.*.*(..))")
那么运行程序后, 所有 com.anson.controller 包下的所有类的所有方法, 都会执行这个切面定义的方法进行相关写日志处理了, 结果如下:
- around
- [2019-12-20 11:19:02,205][INFO ][http-nio-8095-exec-1] URL : http://localhost:8095/user/userall (LogAspect.java:45)
- [2019-12-20 11:19:02,205][INFO ][http-nio-8095-exec-1] HTTP_METHOD : GET (LogAspect.java:46)
- [2019-12-20 11:19:02,206][INFO ][http-nio-8095-exec-1] IP : 0:0:0:0:0:0:0:1 (LogAspect.java:47)
- [2019-12-20 11:19:02,207][INFO ][http-nio-8095-exec-1] CLASS_METHOD : com.anson.controller.UserController.getUserAll (LogAspect.java:48)
- [2019-12-20 11:19:02,208][INFO ][http-nio-8095-exec-1] ARGS : [] (LogAspect.java:49)
- [2019-12-20 11:19:02,510][DEBUG][http-nio-8095-exec-1] ==> Preparing: select id, userName, passWord, realName from user (BaseJdbcLogger.java:143)
- [2019-12-20 11:19:02,606][DEBUG][http-nio-8095-exec-1] ==> Parameters: (BaseJdbcLogger.java:143)
- [2019-12-20 11:19:02,631][DEBUG][http-nio-8095-exec-1] <== Total: 4 (BaseJdbcLogger.java:143)
- [2019-12-20 11:19:02,634][INFO ][http-nio-8095-exec-1] around (LogAspect.java:77)
- [2019-12-20 11:19:02,635][INFO ][http-nio-8095-exec-1] after commit (LogAspect.java:60)
- [2019-12-20 11:19:02,635][INFO ][http-nio-8095-exec-1] RESPONSE : com.anson.common.result.ResultBody@6d9947d (LogAspect.java:55)
如果不用注解的话, 上面就已经完成一个切面了, 如果用注解来定义切面范围呢, 好, 也简单, 我们来定义一个注解
-------------- 华丽丽的分割线 -------------------------------
-------------- 增加自定义注解的方式 ----------------------------
3, 添加一个 LogAnnotation 注解
- package com.anson.common.annotation;
- import java.lang.annotation.*;
- /**
- * @description: 自定义注解
- * @author: anson
- * @Date: 2019/12/20 10:32
- */
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface LogAnnotation
- {
- }
这样就可以了, 然后, 在需要的地方, 加入这个自定义注解:
- //2, 获取所有用户
- @ApiOperation(value = "获取所有用户", notes = "获取所有用户")
- @RequestMapping(value="/userall",method= RequestMethod.GET)
- @LogAnnotation // 自定义注解
- public ResultBody getUserAll()
- {
- List<User> users = userservice.getAll();
- return ResultBody.success(users,"获取所有用户信息成功");
- }
同时, 修改切面范围的定义即可:
- //2, 定义切入点(可以匹配, 注解的方式, 可混用)-- 以下表示范围为: controller 包下所有包含 @LogAnnotation 注解的方法
- @Pointcut("execution(public * com.anson.controller.*.*(..)) && @annotation(com.anson.common.annotation.LogAnnotation)")
- public void pointcut(){
- }
完了, 就这么简单;
至于什么地方该使用 AOP, 以及 AOP 和拦截器用哪个比较好, 这个就要根据业务场景灵活取舍了, 掌握了思想, 具体使用那就可以灵活发挥了
来源: https://www.cnblogs.com/yanghj/p/12072516.html