如何利用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 = "\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, 以及各种坑的记录的更多相关文章
- AOP 切面编程------JoinPoint ---- log日志
AOP 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件 ...
- 利用AOP切面打印项目中每个接口的运行情况
1.前言 AOP切面技术,大家应该都听知道,Spring框架的主要功能之一. AOP切面的用途很广,其中一个常见的用途就是打印接口方法的运行日志和运行时间. 日志对于一个项目很是重要,不仅有助于调错, ...
- SpringBoot31 整合SpringJDBC、整合MyBatis、利用AOP实现多数据源
一.整合SpringJDBC 1 JDBC JDBC(Java Data Base Connectivity,Java 数据库连接)是一种用于执行 SQL 语句的 Java API,可以为多种关系数 ...
- SpringAOP01 利用AOP实现权限验证、利用权限验证服务实现权限验证
1 编程范式 1.1 面向过程 1.2 面向对象 1.3 面向切面编程 1.4 函数式编程 1.5 事件驱动编程 2 什么是面向切面编程 2.1 是一种编程范式,而不是一种编程语言 2.2 解决一些特 ...
- SpringBoot2.x整合Email并利用AOP做一个项目异常通知功能
因为不知aop能干嘛,因此用aop做个小功能,再结合最近学的springboot-Email做了个系统异常自动邮件通知的功能, 感觉满满的成就感. AOP不懂的可以看上一篇:https://www.c ...
- 利用AOP与ToStringBuilder简化日志记录
刚学spring的时候书上就强调spring的核心就是ioc和aop blablabla...... IOC到处都能看到...AOP么刚开始接触的时候使用在声明式事务上面..当时书上还提到一个用到ao ...
- 工控随笔_07_西门子_WinCC利用命令行实现操作log日志
在WinCC中可以通过报警纪录来实现操作员纪录,这个需要WinCC的消息系统进行组态和配置. 利用消息系统进行实现上诉功能不但复杂而且时间久啦也不方便查询.那么有没有一种简单的方法来 实现操作员纪录呢 ...
- Spring Boot AOP 扫盲,实现接口访问的统一日志记录
AOP 是 Spring 体系中非常重要的两个概念之一(另外一个是 IoC),今天这篇文章就来带大家通过实战的方式,在编程猫 SpringBoot 项目中使用 AOP 技术为 controller 层 ...
- 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 ...
随机推荐
- 【CodeVS】1792 分解质因数
1792 分解质因数 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 青铜 Bronze 题目描述 Description 编写一个把整数N分解为质因数乘积的程序. 输入描述 Inp ...
- centos下nginx无法加载php文件,404
前提:html文件可以正常加载,php安装正常,nginx配置正确.仍然无法加载php文件,明明文件是存在,却报404,而不是直接输出文件 原因是:未启动php-fpm,未开启9000端口 首先查看是 ...
- 获取电脑名和Ip
private string GetHostNameAndIP( bool isv4Orv6) { string HostName = Dns.GetHostN ...
- 为什么不用原生的Spring Cloud Config
引言 近几年传统应用架构已经逐渐朝着微服务架构演进.那么随着业务的发展,微服务越来越庞大,此时服务配置的管理变得会复杂起来.为了方便服务配置文件统一管理,实时更新,配置中心应运而生.其实,所谓配置中心 ...
- 工程没有生成lib文件,只生成了dll文件
解决办法: 在工程上右键 -> 添加 -> 新建项 -> 选"模块定义文件(.def)" -> 随便填写个名字 -> 添加 重新编译编译就可生成.li ...
- Mac上代码开启dump的core文件生成方案
#ifdef Q_OS_MAC struct rlimit rl; getrlimit(RLIMIT_NOFILE,&rl); rl.rlim_cur = qMin((rlim_t)OPEN_ ...
- Codeforces 442B
题目链接 B. Andrey and Problem time limit per test 2 seconds memory limit per test 256 megabytes input s ...
- springboot(十九)使用actuator监控应用【转】【补】
springboot(十九)使用actuator监控应用 微服务的特点决定了功能模块的部署是分布式的,大部分功能模块都是运行在不同的机器上,彼此通过服务调用进行交互,前后台的业务流会经过很多个微服务的 ...
- WPF/Silverlight深度解决方案:(七)HLSL自定义渲染特效之完美攻略(中)
原文:WPF/Silverlight深度解决方案:(七)HLSL自定义渲染特效之完美攻略(中) 通过上一节的解说,大家是否已经对HLSL有了较深刻的认识和理解,HLSL的渲染不仅仅局限于静态处理,通过 ...
- More Effective C++: 03异常
C++的异常机制使得程序付出某些代价:资源泄漏的可能性增加了:写出具有你希望的行为的构造函数与析构函数变得更加困难:执行程序和库程序尺寸增加了,同时运行速度降低了等等. 但是为什么使用异常呢?C程序使 ...