spring AOP切面及日志记录实现

目录

1.什么是AOP切面

2.理解AOP

3.AOP实例

1.自定义注解

2.创建一个切面类

3.将自定义注解标注在测试接口上


1.什么是AOP切面

AOP(Aspect Oriented Programming),面向切面思想,是Spring的三大核心思想之一。

在项目中经常会有些系统性的需求,例如权限校验,日志记录,统计等,这时我们就可以通过AOP切面去实现。

spring AOP切面及日志记录实现_第1张图片

有多少业务代码就需要写多少重复校验和日志记录,这显然是不合理的,我们可以把这些重复操作抽离出来,写成公共的方法。

spring AOP切面及日志记录实现_第2张图片

这样,代码冗余解决了,但是每个地方都要手动去调用还是很麻烦,有没有更好的方式呢?这就要用到我们的AOP(面向切面编程),AOP将权限校验,日志记录等非业务代码提取出来,和业务代码完全分离,并寻找节点切入业务代码中

spring AOP切面及日志记录实现_第3张图片

2.理解AOP

简单理解AOP,AOP主要做三件事

  1. 在哪里切入,也就是日志记录等非业务代码在哪些业务代码中执行。
  2. 在什么时候切入,是在业务代码执行前还是后。
  3. 切入后做什么事情,比如权限校验,日志记录等

 理解如下图

spring AOP切面及日志记录实现_第4张图片

一些概念理解:

Pointcut:切点,决定处理如权限校验、日志记录等在何处切入业务代码中(即织入切面)。切点分为execution方式和annotation方式。前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。
Advice:处理,包括处理时机和处理内容。处理内容就是要做什么事,比如校验权限和记录日志。处理时机就是在什么时机执行处理内容,分为前置处理(即业务代码执行前)、后置处理(业务代码执行后)等。
Aspect:切面,即Pointcut和Advice。
Joint point:连接点,是程序执行的一个点。例如,一个方法的执行或者一个异常的处理。在 Spring AOP 中,一个连接点总是代表一个方法执行。
Weaving:织入,就是通过动态代理,在目标对象方法中执行处理内容的过程。

3.AOP实例

  1. 自定义一个注解OperationLogAnnotation
  2. 创建一个切面类,切点设置为拦截标注OperationLogAnnotation的方法,截取传参,进行日志记录
  3. 将OperationLogAnnotation标注在测试接口上

具体的实现步骤如下:

1.自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLogAnnotion {

    //操作类型
    @NonNull
    OperationType type();

    //操作对象
    @NonNull
    OperationObjectType objType();

}
public enum OperationType {

    CREATE("create"),

    UPDATE("update"),

    DELETE("delete"),

    ADD("add");

    private String type;

    OperationType(String type){
        this.type=type;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}
public enum OperationObjectType {

    USER("user"),
    //项目
    PROJECT("project"),

    //页面管理
    PROJECT_PAGE("project page"),

    //人员管理
    ROLE_USER("role user"),

    //角色页面管理
    ROLE_PAGE("role page");

    private String objType;

    OperationObjectType(String objType){
        this.objType=objType;
    }

    public String getObjType() {
        return objType;
    }

    public void setObjType(String objType) {
        this.objType = objType;
    }
}

2.创建一个切面类

@Aspect
@Component
public class LogAdvice implements ApplicationListener {

    private BlockingQueue operationLogQ = new LinkedBlockingDeque<>();

    @Autowired
    private IOperationLogService operationLogService;

    private static final Logger LOGGER = LogManager.getLogger();

    @Pointcut("@annotation(com.pwd.springdemo.aop.annotation.OperationLogAnnotion)")
    public void logAdvicePointcut(){

    }

    @AfterReturning(pointcut = "logAdvicePointcut()  && @annotation(operationLogAnnotion)", returning = "returnResult")
    public void logAdvice(JoinPoint joinPoint, com.pwd.springdemo.aop.annotation.OperationLogAnnotion operationLogAnnotion, Object returnResult){
        try {
            if (!(returnResult instanceof ResultInfo)) {
                return;
            }

            //filter failed request
            ResultInfo result = (ResultInfo) returnResult;
            if (result.getCode() != ResultCode.SUCCESS.getCode()) {
                return;
            }

            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

            Object[] args = joinPoint.getArgs();

            String requestParams = JSONObject.toJSONString(args);


            String userIp = request.getHeader("UserIp");
            User user = (User) request.getSession().getAttribute("user");

            String userId = null;

            if (user != null) {
                userId = user.getUserId();
            }

            String type = operationLogAnnotion.type().getType();
            String objType = operationLogAnnotion.objType().getObjType();

            Signature signature = joinPoint.getSignature();
            String functionName=signature.getName();

            OperationLog log = new OperationLog(userId, userIp, functionName,type + " " + objType, requestParams);

            operationLogQ.add(log);

        } catch (Exception e) {
            LOGGER.error("operation log aspect error", e);
        }
    }

    /**
     * consume queue 2 save db
     */
    private void saveLogs() {
        while (true) {
            try {
                OperationLog log = operationLogQ.poll(5, TimeUnit.MILLISECONDS);
                if (log != null) {
                    operationLogService.addLog(log);
                }
            } catch (Exception e) {
                LOGGER.error("insert operation log to db error", e);
            }
        }
    }
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ExecutorService pool = new ThreadPoolExecutor(1, 1,
                0L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(), new ThreadFactoryBuilder().setNameFormat("Save-Logs-%d").setDaemon(true).build());
        pool.submit(new Runnable() {
            @Override
            public void run() {
                saveLogs();
            }
        });
    }
}

3.将自定义注解标注在测试接口上

    @ResponseBody
    @OperationLogAnnotion(type = OperationType.DELETE,objType = OperationObjectType.USER)
    @RequestMapping(value = "deleteUserInfoById", method = RequestMethod.POST)
    public ResultInfo deleteUserInfoById(@RequestBody JSONObject jsonObject) {
        Integer id = jsonObject.getInteger("id");
        LOGGER.info("id:" + id);
        userService.deleteUserInfoById(id);
        return ResultInfo.success(null);

    }

你可能感兴趣的:(java,spring,java,后端)