0、前言

1、什么是AOP

  AOP(面向切面编程),是一种横切技术,是对OOP的补充和完善;

  使用AOP的横切,可以对系统进行无侵入性的日志监听、事务、权限管理等;

  思想上跟拦截器其实类似;拦截器是对action进行拦截处理,AOP是对切面进行拦截处理,其实切面也属于一种action集合;

  AOP可以很好解耦;

2、AOP的组成

  Aspect:切面;

  Join point:连接点;

  Advice:通知,在切入点上执行的操作;

  Poincut:带有通知的连接点;

  target:被通知的对象;

  AOP proxy;AOP代理;

其中,Advice(通知)分为以下几种:

  • before(前置通知): 在方法开始执行前执行
  • after(后置通知): 在方法执行后执行
  • afterReturning(返回后通知): 在方法返回后执行
  • afterThrowing(异常通知): 在抛出异常时执行
  • around(环绕通知): 在方法执行前和执行后都会执行

通知的执行顺序:

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和拦截器用哪个比较好,这个就要根据业务场景灵活取舍了,掌握了思想,具体使用那就可以灵活发挥了


最新文章

  1. Linux命令總結
  2. Window Server 2012 R2 没有照片查看器 打开图片都是画板问题怎么解决
  3. React Native iOS环境搭建
  4. 【转】linux dumpe2fs命令
  5. Helpers\Password
  6. 【codeforce 219D】 Choosing Capital for Treeland (树形DP)
  7. python与编码方式
  8. 【SICP读书笔记(二)】使用过程来黏合数据 —— 酷炫吊的消息传递机制
  9. 【NOIP2012】DAY1+DAY2题解
  10. OOP in JS Public/Private Variables and Methods
  11. poj3207
  12. 关于Python中yield的一些个人见解
  13. 3分钟anaconda安装Python框架Tensorflow,1行语句测试是否成功?
  14. pycharm 如何进行全部搜索
  15. 以前的 Delphi版本
  16. .net core中Quartz的使用
  17. nRF51 DK : nRF51822 Development Kit for Bluetooth Smart, ANT and 2.4GHz applications.
  18. window 10 企业版激活
  19. Android Binder分析二:Natvie Service的注冊
  20. Mininet实验 设置带宽之简单性能测试

热门文章

  1. 搭建wordPress遇到无法连接数据库的问题
  2. go语言学习笔记(二)
  3. C++控制台闪回;编译器警告C4305,C4244
  4. pycharm的安装流程
  5. 剖析nsq消息队列(四) 消息的负载处理
  6. webapi跨域使用session
  7. javaScript——label语句
  8. 更好一点的:Vue 利用指令实现禁止反复发送请求
  9. Socket 实现简单的多线程服务器程序
  10. 新浪短网址最新api接口