spring boot 开发—第六篇AOP记录系统关键操作日志

 

原创

spring boot 开发—第六篇AOP记录系统关键操作日志

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/liuweilong07/article/details/80396055

1、AOP简介

AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。 
  • 1
  • 2
  • 3
  • 4
  • 5

2、AOP核心概念

(1)、横切关注点

对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
  • 1

(2)、切面(aspect)

类是对物体特征的抽象,切面就是对横切关注点的抽象
  • 1

(3)、连接点(joinpoint)

被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
  • 1

(4)、切入点(pointcut)

对连接点进行拦截的定义
  • 1

(5)、通知(advice)

所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类:

Before(前) org.apringframework.aop.MethodBeforeAdvice 

After-returning(返回后) org.springframework.aop.AfterReturningAdvice 

After-throwing(抛出后) org.springframework.aop.ThrowsAdvice 

Introduction(引入) org.springframework.aop.IntroductionInterceptor 

Arround(环绕) org.aopaliance.intercept.MethodInterceptor 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

(6)、目标对象

代理的目标对象
  • 1

(7)、织入(weave)

将切面应用到目标对象并导致代理对象创建的过程
  • 1

(8)、引入(introduction)

在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
  • 1

3、具体实例

3.1、定义注解,拦截controller

package com.vesus.springbootlog.annotation;

import java.lang.annotation.*;

/**
 * @Description:  定义注解,拦截controller
 * @Author: vesus
 * @CreateDate: 2018/5/20 上午10:54
 * @Version: 1.0
 */
@Retention(RetentionPolicy.RUNTIME)//元注解,定义注解被保留策略,一般有三种策略
                                    //1、RetentionPolicy.SOURCE 注解只保留在源文件中,在编译成class文件的时候被遗弃
                                    //2、RetentionPolicy.CLASS 注解被保留在class中,但是在jvm加载的时候北欧抛弃,这个是默认的声明周期
                                    //3、RetentionPolicy.RUNTIME 注解在jvm加载的时候仍被保留
@Target({ElementType.METHOD}) //定义了注解声明在哪些元素之前
@Documented
public @interface SystemControllerLog {
    //定义成员
    String descrption() default "" ;//描述
    String actionType() default "" ;//操作的类型,1、添加 2、修改 3、删除 4、查询
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

3.2、定义注解,拦截service

package com.vesus.springbootlog.annotation;

import java.lang.annotation.*;

/**
 * @Description: 定义注解,拦截service
 * @Author: vesus
 * @CreateDate: 2018/5/20 上午10:54
 * @Version: 1.0
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface SystemServiceLog {
    //定义成员
    String decription() default "" ;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

3.3、定义日志切入类

package com.vesus.springbootlog.aspect;

import com.vesus.springbootlog.annotation.SystemControllerLog;
import com.vesus.springbootlog.annotation.SystemServiceLog;
import com.vesus.springbootlog.model.ExecutionResult;
import com.vesus.springbootlog.model.SystemLog;
import com.vesus.springbootlog.service.SystemLogService;
import com.vesus.springbootlog.util.ReturnCode;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.core.annotation.Order;
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.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @Description: 定义日志切入类
 * @Author: vesus
 * @CreateDate: 2018/5/20 上午11:05
 * @Version: 1.0
 */
@Aspect
@Component
@Order(-5)
public class SystemLogAspect {

    @Autowired
    private SystemLogService systemLogService;

    /***
     * 定义service切入点拦截规则,拦截SystemServiceLog注解的方法
     */
    @Pointcut("@annotation(com.vesus.springbootlog.annotation.SystemServiceLog)")
    public void serviceAspect(){}

    /***
     * 定义controller切入点拦截规则,拦截SystemControllerLog注解的方法
     */
    @Pointcut("@annotation(com.vesus.springbootlog.annotation.SystemControllerLog)")
    public void controllerAspect(){}

    /***
     * 拦截控制层的操作日志
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("controllerAspect()")
    public ExecutionResult recordLog(ProceedingJoinPoint joinPoint) throws Throwable {
        SystemLog systemLog = new SystemLog();
        Object proceed = null ;
        //获取session中的用户
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        request.getSession().getAttribute("user");
        systemLog.setUserid("vesus");
        //获取请求的ip
        String ip = request.getRemoteAddr();
        systemLog.setRequestip(ip);
        //获取执行的方法名
        systemLog.setActionmethod(joinPoint.getSignature().getName());

        //获取方法执行前时间
        Date date=new Date();
        systemLog.setActiondate(date);

        proceed = joinPoint.proceed();
        //提取controller中ExecutionResult的属性
        ExecutionResult result = (ExecutionResult) proceed;

        if (result.getResultCode().equals(ReturnCode.RES_SUCCESS)){
            //设置操作信息
            systemLog.setType("1");
            //获取执行方法的注解内容
            systemLog.setDescription(getControllerMethodDescription(joinPoint)+":"+result.getMsg());
        }else{
            systemLog.setType("2");
            systemLog.setExceptioncode(result.getMsg());
        }

        Object[] params = joinPoint.getArgs() ;
        String returnStr = "" ;
        for (Object param : params) {
            if (param instanceof String){
                returnStr+= param ;
            }else if (param instanceof Integer){
                returnStr+= param ;
            }
        }
        systemLog.setParams(returnStr);

        systemLogService.saveUser(systemLog);

        return result ;
    }

    //异常处理
    @AfterThrowing(pointcut = "controllerAspect()",throwing="e")
    public void doAfterThrowing(JoinPoint joinPoint,Throwable e) throws Throwable{
        SystemLog systemLog = new SystemLog();
        Object proceed = null ;
        //获取session中的用户
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        request.getSession().getAttribute("user");
        systemLog.setUserid("vesus");
        //获取请求的ip
        String ip = request.getRemoteAddr();
        systemLog.setRequestip(ip);
        systemLog.setType("2");
        systemLog.setExceptioncode(e.getClass().getName());
        systemLog.setExceptiondetail(e.getMessage());
        systemLogService.saveUser(systemLog);
    }


    /***
     * 获取service的操作信息
     * @param joinpoint
     * @return
     * @throws Exception
     */
    public String getServiceMethodMsg(JoinPoint joinpoint) throws Exception{
        //获取连接点目标类名
        String className =joinpoint.getTarget().getClass().getName() ;
        //获取连接点签名的方法名
        String methodName = joinpoint.getSignature().getName() ;
        //获取连接点参数
        Object[] args = joinpoint.getArgs() ;
        //根据连接点类的名字获取指定类
        Class targetClass = Class.forName(className);
        //拿到类里面的方法
        Method[] methods = targetClass.getMethods() ;

        String description = "" ;
        //遍历方法名,找到被调用的方法名
        for (Method method : methods) {
            if (method.getName().equals(methodName)){
                Class[] clazzs = method.getParameterTypes() ;
                if (clazzs.length==args.length){
                    //获取注解的说明
                    description = method.getAnnotation(SystemServiceLog. class).decription();
                    break;
                }
            }
        }
        return description ;
    }

    /***
     * 获取controller的操作信息
     * @param point
     * @return
     */
    public String getControllerMethodDescription(ProceedingJoinPoint point) throws  Exception{
        //获取连接点目标类名
        String targetName = point.getTarget().getClass().getName() ;
        //获取连接点签名的方法名
        String methodName = point.getSignature().getName() ;
        //获取连接点参数
        Object[] args = point.getArgs() ;
        //根据连接点类的名字获取指定类
        Class targetClass = Class.forName(targetName);
        //获取类里面的方法
        Method[] methods = targetClass.getMethods() ;
        String description="" ;
        for (Method method : methods) {
            if (method.getName().equals(methodName)){
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == args.length){
                    description = method.getAnnotation(SystemControllerLog.class).descrption();
                    break;
                }
            }
        }
        return description ;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189

3.4、定义控制器controller

package com.vesus.springbootlog.controller;

import com.vesus.springbootlog.annotation.SystemControllerLog;
import com.vesus.springbootlog.model.ExecutionResult;
import com.vesus.springbootlog.model.User;
import com.vesus.springbootlog.service.UserService;
import com.vesus.springbootlog.util.ReturnCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class UserController {

    @Autowired
    UserService userService ;

    @RequestMapping(value = "/userlist")
    @SystemControllerLog(descrption = "查询用户信息",actionType = "4")
    public ExecutionResult getUserList(String id) throws Exception{

        ExecutionResult result = new ExecutionResult();
        try {
            List<User> users = userService.findAll() ;
            result.setTotal(users.size());
            result.setResultCode(ReturnCode.RES_SUCCESS);
            result.setFlag(true);
            result.setData(users);
            result.setMsg("查询成功!");
            //异常处理
            int aa= 5/0;
        }catch (Exception e){
            result.setFlag(true);
            result.setData(null);
            result.setResultCode(ReturnCode.RES_FAILED);
            result.setMsg("查询失败!");
            throw e ;
        }
        return result ;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

3.5、启动

访问http://localhost:8080//userlist?id=123456,可以看到数据库增加了日志文件。

1   0:0:0:0:0:0:0:1 1   vesus   查询用户信息:查询成功!    2018-05-21          getUserList 
2   0:0:0:0:0:0:0:1 1   vesus   查询用户信息:查询成功!    2018-05-21          getUserList 
3   0:0:0:0:0:0:0:1 1   vesus   查询用户信息:查询成功!    2018-05-21          getUserList 
4   0:0:0:0:0:0:0:1 1   vesus   查询用户信息:查询成功!    2018-05-21          getUserList 
5   0:0:0:0:0:0:0:1 1   vesus   查询用户信息:查询成功!    2018-05-21          getUserList 
posted @ 2019-12-08 14:19  liuxf1  阅读(210)  评论(0编辑  收藏  举报