美文网首页
Spring Boot中AOP结合自定义注解保存及打印请求日志

Spring Boot中AOP结合自定义注解保存及打印请求日志

作者: 不一定正经 | 来源:发表于2017-11-30 11:37 被阅读0次

    Spring Boot 中引入AOP(面向切面编程)尤为简单,添加AOP依赖即可。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。更好的解耦和,提高代码的可重用性。结合注解就更加的灵活方便了。

    实现步骤如下:

    一、引入AOP的 pom.xml 依赖

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
    

    二、编写自定义注解 @Log

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Log {
    
        /**
         * 描述
         * @return
         */
        String value() default "";
    
        /**
         * 是否保存请求记录
         */
        boolean isRecord() default false;
        
    
        /**
         * 是否打印返回值
         */
        boolean isWrite() default true;
    
    
    }
    

    三、定义一个切面LogAspect

    //使用@Aspect注解将一个java类定义为切面类
    @Aspect
    @Component
    public class LogAspect {
    
        private Logger logger = LoggerFactory.getLogger(getClass());
    
        //保存记录Mapper
        @Autowired
        OperateRecordMapper operateRecordMapper;
    
        //使用@Pointcut定义一个切入点,可以是一个规则表达式,比如下例中某个package下的所有函数,也可以是一个注解等。
        @Pointcut("@annotation(com.xiduoduo.core.annotation.Log)")
        public void log(){}
    
        //使用@Before在切入点开始处切入内容
        @Before("log()")
        public void doBefore(JoinPoint joinPoint) throws Throwable {
            //防止参数转换报错
            try{
                //方法名称
                String methodName = joinPoint.getSignature().getName();
                //方法参数
                String argsJsonStr = JSON.toJSONString(joinPoint.getArgs());
                logger.info("【"+methodName+"】开始执行,参数:"+argsJsonStr);
            }catch (Exception e){
                logger.error("日志参数转换异常,不影响业务逻辑!错误信息:"+e.getMessage());
            }
        }
    
       //使用@AfterReturning在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)
        @AfterReturning(returning = "ret", pointcut = "@annotation(log)")
        public void doAfterReturning(JoinPoint joinPoint, Object ret, Log log) throws Throwable {
            //防止参数转换报错
            try{
                //方法名称
                String methodName = joinPoint.getSignature().getName();
                //方法参数
                String argsJsonStr = JSON.toJSONString(joinPoint.getArgs());
                String retStr = JSON.toJSONString(ret);
                //判断是否输出返回值,列表数据尽量不打印
                if(log.isWrite()){
                    logger.info("【"+methodName+"】执行完毕,返回值:"+retStr);
                }else {
                    logger.info("【"+methodName+"】执行完毕");
                }
                //是否保存请求记录
                if(log.isRecord()){
                    OperateRecord operateRecord = new OperateRecord();
                    operateRecord .setAddTime(DateUtil.getCurrentDate());
                    operateRecord .setContent("[方法:"+methodName+"],[参数:"+argsJsonStr+"],[返回值:"+retStr+"]");
                    operateRecordMapper.insert(operateRecord );
                }
            }catch (Exception e){
                logger.error("日志参数转换异常,不影响业务逻辑!错误信息:"+e.getMessage());
            }
        }
    }
    
    

    四、使用@Log注解输出日志及保存请求记录

         /**
         * 添加用户  保存请求记录,正常打印返回值
         * @param params
         * @return
         */
        @Log(isRecord = true)
        public String  addUser(String params) {
            return userService.addUser(parmas);
        }
    
         /**
         * 查询所有用户  不保存请求记录,不打印返回值(列表数据过多)
         * @param params
         * @return
         */
        @Log(isWrite = false)
        public String  getUserList(String params) {
            return userService.getUserList(parmas);
        }
    
    
         /**
         * 根据用户编号获取用户信息  不保存请求记录,正常打印返回值
         * @param params
         * @return
         */
        @Log
        public String  getUserByUserNo(String params) {
            return userService.getUserByUserNo(parmas);
        }
    
    
    

    其他

    一、AOP的相关概念

    连接点(Joinpoint): 表示需要在程序中插入横切关注点的扩展点,连接点可能是类初始化、方法执行、方法调用、字段调用或处理异常等等,Spring只支持方法执行连接点;在AOP中表示为“在哪里干”;
    
    切入点(Pointcut): 选择一组相关连接点的模式,即可以认为连接点的集合,Spring支持perl5正则表达式和AspectJ切入点模式,Spring默认使用AspectJ语法;在AOP中表示为“在哪里干的集合”;
    
    通知(Advice): 在连接点上执行的行为,通知提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段;包括前置通知(before advice)、后置通知(after advice)、环绕通知(around advice),在Spring中通过代理模式实现AOP,并通过拦截器模式以环绕连接点的拦截器链织入通知;在AOP中表示为“干什么”;
    
    切面(Aspect):横切关注点的模块化,比如日志组件。可以认为是通知、引入和切入点的组合;在Spring中可以使用Schema和@AspectJ方式进行组织实现;在AOP中表示为“在哪干和干什么集合”;
    
    引入(Introduction): 也称为内部类型声明,为已有的类添加额外新的字段或方法,Spring允许引入新的接口(必须对应一个实现)到所有被代理对象(目标对象);在AOP中表示为“干什么(引入什么)”;
    
    目标对象(Target Object):需要被织入横切关注点的对象,即该对象是切入点选择的对象,需要被通知的对象,从而也可称为“被通知对象”;由于Spring AOP 通过代理模式实现,从而这个对象永远是被代理对象;在AOP中表示为“对谁干”;
    
    AOP代理(AOP Proxy): AOP框架使用代理模式创建的对象,从而实现在连接点处插入通知(即应用切面),就是通过代理来对目标对象应用切面。在Spring中,AOP代理可以用JDK动态代理或CGLIB代理实现,而通过拦截器模型应用切面。
    
    织入(Weaving): 织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程,织入可以在编译期、类装载期、运行期进行。组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
    
    

    二、注解相关概念

    @Target 
    说明了Annotation所修饰的对象范围
    取值(ElementType)有:
        1.CONSTRUCTOR:用于描述构造器
        2.FIELD:用于描述域
        3.LOCAL_VARIABLE:用于描述局部变量
        4.METHOD:用于描述方法
        5.PACKAGE:用于描述包
        6.PARAMETER:用于描述参数
        7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
    
    
    @Retention
    定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。
    取值(RetentionPoicy)有:
        1.SOURCE:在源文件中有效(即源文件保留)
        2.CLASS:在class文件中有效(即class保留)
        3.RUNTIME:在运行时有效(即运行时保留)
    
    @Documented 
    用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
    

    等你年事稍长,就会发现,要使世界成为一个尚可容忍的生活场所,首先得承认人类的自私是不可避免的。 -- 威廉·萨默赛特·毛姆 《人生的枷锁》

    相关文章

      网友评论

          本文标题:Spring Boot中AOP结合自定义注解保存及打印请求日志

          本文链接:https://www.haomeiwen.com/subject/cczmbxtx.html