zoukankan      html  css  js  c++  java
  • 如何利用aop的环绕消息处理log, 以及各种坑的记录

    如何利用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 = "
    <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<";
        public static final String STIRNG_END = "
    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>";
    //    execution the scan of pakage 切点package
        @Pointcut("execution( * com.controller..*(..)) || execution( * com.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("
    {} 调用结束<-- {} 返回值:{} 耗时:{}ms" + STIRNG_END, classAndMethod.toString(), target, JSONObject.toJSONStringWithDateFormat(result, dateformat, SerializerFeature.WriteMapNullValue), timeConsuming);
                log.info("
    {} 调用结束<-- {}  耗时:{}ms" + STIRNG_END, classAndMethod.toString(), target, timeConsuming);
                return result;
            } catch (Throwable throwable) {
                log.error("调用异常", throwable.getMessage(), throwable);
            }
            return null;
        }
    }

     踩坑:@Pointcut("execution( * com.controller..*(..)) || execution( * com.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.xxx, 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

  • 相关阅读:
    Java随学随记
    jquery的bind()和trigger()
    js跨域访问
    好文推荐系列-------(5)js模块化编程
    好文推荐系列---------(4)使用Yeoman自动构建Ember项目
    好文推荐系列--------(3)GruntJS 在线重载 提升生产率至新境界
    好文推荐系列--------(2)GruntJS——重复乏味的工作总会有人做(反正我不做)
    好文推荐系列--------(1)bower---管理你的客户端依赖
    英文邮件写作
    原型
  • 原文地址:https://www.cnblogs.com/zizaiwuyou/p/11667423.html
Copyright © 2011-2022 走看看