如何利用aop的环绕消息处理log, 以及各种坑的记录

本文链接: https://www.cnblogs.com/zizaiwuyou/p/11667423.html

因为项目里有很多地方要打log, 所以决定改为AOP统一处理, 好不容易整好了, 特此记录一下:

一, 新建项目, 添加注解类和切面类

pom.xml文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.jhyx</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>项目名</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

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

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

        <!-- spring boot start denpendency -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- slf4j log dependency -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>

        <!-- json dependency -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>LATEST</version>
        </dependency>

        <!-- test dependency -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>LATEST</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-core</artifactId>
            <version>LATEST</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito</artifactId>
            <version>LATEST</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-testng</artifactId>
            <version>1.7.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

</project>

  

注解类内容如下:

package com.xxx.common.log;

import org.springframework.stereotype.Component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 *  TYPE ->  Class, interface (including annotation type), or enum declaration
 *  METHOD ->  Method declaration
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {

    String value() default "";

    boolean ignore() default false;
}

切面类如下:

package com.xxx.common.log;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * Log 注解类实现
 */
@Aspect
@Order(100)
@Component
public class LogAspect {

    public static final Logger log =  LoggerFactory.getLogger(LogAspect.class);
    public static final String dateformat = "yyyy:MM:dd HH:mm:ss";
    public static final String STIRNG_START = "\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<";
    public static final String STIRNG_END = "\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>";
//    execution the scan of pakage 切点package
    @Pointcut("execution( * com.qingniu.qfpay.rvwbackend.controller..*(..)) || execution( * com.qingniu.qfpay.rvwbackend.service..*(..))")
    public void serviceLog(){

    }

    @Around("serviceLog()")
    public Object around(ProceedingJoinPoint joinPoint) { // ProceedingJoinPoint 为JoinPoint 的子类,在父类基础上多了proceed()方法,用于增强切面
        try {
            // 获取方法签名
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            //java reflect相关类,通过反射得到注解
            Method method = signature.getMethod();
            Class<?> targetClass = method.getDeclaringClass();

            StringBuffer classAndMethod = new StringBuffer();

            //获取类注解Log
            Log classAnnotation = targetClass.getAnnotation(Log.class);
            //获取方法注解Log
            Log methodAnnotation = method.getAnnotation(Log.class);

            //如果类上Log注解不为空,则执行proceed()
            if (classAnnotation != null) {
                if (classAnnotation.ignore()) {
                    //proceed() 方法执行切面方法,并返回方法返回值
                    return joinPoint.proceed();
                }
                classAndMethod.append(classAnnotation.value()).append("-");
            }

            //如果方法上Log注解不为空,则执行proceed()
            if (methodAnnotation != null) {
                if (methodAnnotation.ignore()) {
                    return joinPoint.proceed();
                }
                classAndMethod.append(methodAnnotation.value());
            }

            //拼凑目标类名和参数名
            String target = targetClass.getName() + "#" + method.getName();
            String params = JSONObject.toJSONStringWithDateFormat(joinPoint.getArgs(), dateformat, SerializerFeature.WriteMapNullValue);

            log.info(STIRNG_START + "{} 开始调用--> {} 参数:{}", classAndMethod.toString(), target, params);

            long start = System.currentTimeMillis();
            //如果类名上和方法上都没有Log注解,则直接执行 proceed()
            Object result = joinPoint.proceed();
            long timeConsuming = System.currentTimeMillis() - start;

//            log.info("\n{} 调用结束<-- {} 返回值:{} 耗时:{}ms" + STIRNG_END, classAndMethod.toString(), target, JSONObject.toJSONStringWithDateFormat(result, dateformat, SerializerFeature.WriteMapNullValue), timeConsuming);
            log.info("\n{} 调用结束<-- {}  耗时:{}ms" + STIRNG_END, classAndMethod.toString(), target, timeConsuming);
            return result;
        } catch (Throwable throwable) {
            log.error("调用异常", throwable.getMessage(), throwable);
        }
        return null;
    }
}

踩坑:@Pointcut("execution( * com.qingniu.qfpay.rvwbackend.controller..*(..)) || execution( * com.qingniu.qfpay.rvwbackend.service..*(..))")

设置execution表达式的具体详情请参考https://blog.csdn.net/yangshangwei/article/details/77627825

@Around("serviceLog()")定义的是触发时机, 通过一个空的方法来转换, 这种方式可以写多个环绕消息, 示例中只写了一个

二,导出JAR包

在eclipse中, 右键项目, Export- > JAR File

踩坑:

图中的这一项必须勾选, 不然无法被springBoot扫描

三, 在需要使用log的项目中导入jar包

我使用的是gradle, builde.gradle文件中加入下列配置

然后在项目根目录加一个libs文件夹, 把jar放进去

修改项目启动文件:

package com.qingniu.qfpay.rvwbackend;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
@Import(AppConfig.class)
@ComponentScan(basePackages = {"com.qingniu.qfpay.rvwbackend, com.xxx.common.log"})
public class App {
    public static void main(final String[] args) {
        SpringApplication.run(App.class, args);
    }
}

  踩坑:这里的@ComponentScan配置,如果不声明, spring默认是扫描当前文件层级以及子孙级,但是如果声明的话, 就全按照声明的路径扫描, 所以要加上当前路径

四, 启动项目,

所有controller和service包下的类中的方法都会打印进出log, 如果要log入库, 在切面类中自定义操作就行了, 注解的作用在于定制化打印log, 可以按照自己的使用需求, 更改代码

参考资料:

https://my.oschina.net/xiaomingnevermind/blog/1619274

https://blog.csdn.net/d_ohko/article/details/78962458

https://blog.csdn.net/yangshangwei/article/details/77627825

https://www.cnblogs.com/cac2020/p/9216509.html

原文地址:https://www.cnblogs.com/zizaiwuyou/p/11667423.html

时间: 2024-11-04 00:43:33

如何利用aop的环绕消息处理log, 以及各种坑的记录的相关文章

Spring+SpringMVC+Mybatis 利用AOP自定义注解实现可配置日志快照记录

目的: 需要对一些事物的操作进行日志记录,如果在service内进行记录,大量的代码重复,并且维护比较麻烦.所以采用AOP的方式对service进行拦截.使用自定义注解的目的则是判断是否需要记录日志和传递额外的信息. 方式 本次解决方案十分感谢博主-跳刀的兔子的博文 本文绝大部分参考与本文,略有不同,所以做一些整理,博主的文章更详细一些. 1.首先新建自定义注解 @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @

利用AOP实现空模式的无缝使用 创世纪代码应用之一:一行代码让接口框架RUN起来

这是我开播第一篇,朋友们多多支持.捧场,谢谢. 引子 地是空虚混沌.渊面黑暗. 神的灵运行在水面上.  神说.要有光.就有了光.  神看光是好的.就把光暗分开了.  神称光为昼.称暗为夜.有晚上.有早晨.这是头一日. ——引至<圣经.神创造天地> 关键词:null,AOP,Spring.Net框架,空模式,面向接口编程,单元测试,方法拦截器 摘要:在我们编程的时候很难离开null,它给我们带来了很多麻烦.本文从新的视角利用AOP无缝使用空模式部分解决了这个问题,最重要的是可以使得我们的程序尽早

工控随笔_07_西门子_WinCC利用命令行实现操作log日志

在WinCC中可以通过报警纪录来实现操作员纪录,这个需要WinCC的消息系统进行组态和配置. 利用消息系统进行实现上诉功能不但复杂而且时间久啦也不方便查询.那么有没有一种简单的方法来 实现操作员纪录呢? 我们知道在Windows系统中可以通过批处理来实现很多功能,而且操作也比较简单,学习起来也 没有多大的难度.那么我们是否可以Windows的批处理功能来实现日志功能呢? 一.WinCC执行外部程序. 在WinCC中通过 ProgramExecute("notepad.exe"); 函数

SpringAOP01 利用AOP实现权限验证、利用权限验证服务实现权限验证

1 编程范式 1.1 面向过程 1.2 面向对象 1.3 面向切面编程 1.4 函数式编程 1.5 事件驱动编程 2 什么是面向切面编程 2.1 是一种编程范式,而不是一种编程语言 2.2 解决一些特定的问题 2.3 作为面向对象编程的一种补充 3  AOP产生的初衷 3.1 解决代码重复性问题 Don't Repeat Yourself 3.2 解决关注点分离问题 Separation of Concerns 3.2.1 水平分离(技术上划分) 控制层 -> 服务层 -> 持久层 3.2.2

利用iframe无刷新上传文件的坑

原文:利用iframe无刷新上传文件的坑 页面里经常要用到文件上传的功能,而且要求页面不刷新,先说一下原理:页面里放一个file控件和submit按钮,外面用form表单包住,给form表单加上对应的属性值,action.method.entype.name,到这一步,能上传文件了,但是这样上传文件会刷新页面,这不是我们想要的.我们要的是文件上传时不刷新页面,那么也简单,在页面里放一个iframe,设置它的宽高为0,这里有两个坑: 1.需要设置iframe的name值与form的target属性

关于写入Windows Log Event却没有任何记录的问题

在本机或者服务器上,写入Windows Event Log日志代码,执行了没有报错,但是打开Windows Event Log记录面板,却看不到任何记录,很可能是你当前用户没有权限是写入东西.此时,我们做一些配置,就可以使当前用户拥有写入Event Log的权限. 设置:"开始->运行",输入命令,"regedt32",找到"System->CurrentControlSet->Services->Eventlog",选择

利用chkconfig 管理启动服务及脚本一个坑点

利用chkconfig 管理启动服务及脚本一个坑点 在启动脚本头部: #!/bin/bash #chkconfig:  2345 63 90 #description: this is a example 彩色部分是chkconfig管理的要点,必须写入,同时绿色部分代表所引用的运行级别,×××代表启动顺序,越小越靠前[00-99],蓝色部分代表关闭顺序,越小越靠前[00-99],我为了不影响,用了一个100,报错了,所以是个坑点,网络上也没有相关说明.是自己试出来的. 其实启动就相当于开机自动

利用 CocoaLumberjack 搭建自己的 Log 系统(转)

一直需要一个 Log 系统,可以将程序运行过程中打的 log 发送到自己服务器,方便之后数据分析或者除错.之前也尝试过找一些第三方服务,但看来看去,国内貌似没看到专门做这一块的,而国外看了下有 Loggly,似乎满足需求,但它要收费且日志保存时间太短.后来无意间看了下 Loggly 提供的 SDK 源代码,发现了CocoaLumberjack这个好东西,而 Loggly 其也不过就是在 CocoaLumberjack 上自定义了 Logger 和 Formatter 而已.自己做的话,也很简单.

【Spring】利用AOP来做系统性能监控

需求: 假设已经有了一些类,现在想统计每个方法调用花了多长时间,该怎么做? 思路: 我第一个想法就是去每个方法执行前后记录一下当前的时间戳,然后相减统计到日志. OK,没问题,那么这样做合理吗? 首先,工作量大且全部都是重复劳动: 其次,扩招性极其差: 再次,不优雅,写代码不仅要考虑到完成需求,一定要以最优雅的形式完成. 所以决定采用spring的面向切面编程技术来辅助完成这项功能. 步骤: 一.首先新建一个ApiMonitor.java: @Aspect public class ApiMon