如何利用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.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("\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.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

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

  1. AOP 切面编程------JoinPoint ---- log日志

    AOP 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件 ...

  2. 利用AOP切面打印项目中每个接口的运行情况

    1.前言 AOP切面技术,大家应该都听知道,Spring框架的主要功能之一. AOP切面的用途很广,其中一个常见的用途就是打印接口方法的运行日志和运行时间. 日志对于一个项目很是重要,不仅有助于调错, ...

  3. SpringBoot31 整合SpringJDBC、整合MyBatis、利用AOP实现多数据源

    一.整合SpringJDBC 1  JDBC JDBC(Java Data Base Connectivity,Java 数据库连接)是一种用于执行 SQL 语句的 Java API,可以为多种关系数 ...

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

    1 编程范式 1.1 面向过程 1.2 面向对象 1.3 面向切面编程 1.4 函数式编程 1.5 事件驱动编程 2 什么是面向切面编程 2.1 是一种编程范式,而不是一种编程语言 2.2 解决一些特 ...

  5. SpringBoot2.x整合Email并利用AOP做一个项目异常通知功能

    因为不知aop能干嘛,因此用aop做个小功能,再结合最近学的springboot-Email做了个系统异常自动邮件通知的功能, 感觉满满的成就感. AOP不懂的可以看上一篇:https://www.c ...

  6. 利用AOP与ToStringBuilder简化日志记录

    刚学spring的时候书上就强调spring的核心就是ioc和aop blablabla...... IOC到处都能看到...AOP么刚开始接触的时候使用在声明式事务上面..当时书上还提到一个用到ao ...

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

    在WinCC中可以通过报警纪录来实现操作员纪录,这个需要WinCC的消息系统进行组态和配置. 利用消息系统进行实现上诉功能不但复杂而且时间久啦也不方便查询.那么有没有一种简单的方法来 实现操作员纪录呢 ...

  8. Spring Boot AOP 扫盲,实现接口访问的统一日志记录

    AOP 是 Spring 体系中非常重要的两个概念之一(另外一个是 IoC),今天这篇文章就来带大家通过实战的方式,在编程猫 SpringBoot 项目中使用 AOP 技术为 controller 层 ...

  9. C# Asp.net中的AOP框架 Microsoft.CCI, Mono.Cecil, Typemock Open-AOP API, PostSharp -摘自网络 (可以利用反射 Attribute 进行面向切面编程 可以用在记录整个方法的Log方面)

    Both Microsoft.CCI and Mono.Cecil are low-level, and don't validate produced assemblies. It takes lo ...

随机推荐

  1. Python 文件读写小结

  2. uptime查看服务器运行时间

    uptime命令用于查看服务器运行了多长时间以及有多少个用户登录,快速获知服务器的负荷情况. uptime的输出包含一项内容是load average,显示了最近1,5,15分钟的负荷情况.它的值代表 ...

  3. 2016计蒜之道复赛A 百度地图的实时路况

    百度地图的实时路况功能相当强大,能方便出行的人们避开拥堵路段.一个地区的交通便捷程度就决定了该地区的拥堵情况.假设一个地区有 nnn 个观测点,编号从 111 到 nnn.定义 d(u,v,w)d(u ...

  4. bzoj2212/3702 [Poi2011]Tree Rotations 线段树合并

    Description Byteasar the gardener is growing a rare tree called Rotatus Informatikus. It has some in ...

  5. DOM 创建元素 删除元素(结点)

    创建新的 HTML 元素 如需向 HTML DOM 添加新元素,您必须首先创建该元素(元素节点),然后向一个已存在的元素追加该元素. <script> var para=document. ...

  6. 各大ACM OJ网址

    做快乐的程序员 以你的条件,你不必追求优秀,但你可以做到良好. 各大OJ网址 ACM OJ Collection(排名不分先后): 中国:(China) 华东地区: 浙江: 杭州电子科技大学(HDU) ...

  7. CentOS8/RHEL8--恢复root用户密码及简易加固GRUB

    CentOS8/RHEL8--简易加固GRUB 今天突然想到放在数据中心的虚拟化平台下的Linux服务器,都是采用默认方式安装的,没有设置太多的安全选项,如果有恶意用户重启服务器后,通过GRUB调整启 ...

  8. npm上面实用的第三方工具包

    npm上面实用的第三方工具包 live-server 作用:为页面提供实时刷新重载的功能,并且能提供一个http服务器 官方地址:https://www.npmjs.com/package/live- ...

  9. WinForm 实现主程序(exe)与其关联类库(*.dll)分开存放

    WinForm 实现主程序(exe)与其关联类库(*.dll)分开存放    开发环境:   Microsoft Windows 7 SP1 Microsoft.NET Framework 4.0 ( ...

  10. python的数据类型和变量

    数据类型 计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值.但是,计算机能处理的远不止数值,还可以处理文本.图形.音频.视频.网页等各种各样的数据,不同的数据,需要定 ...