如何利用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 ...
随机推荐
- golang之if
1.if语句 (1)if (2)if else (3)if esle ...else
- 高维护性的javascript
养成良好的编码习惯,提高代码的可维护性 避免定义全局变量或函数 定义全局的变量和函数,会影响代码的可维护性.如果在页面中运行的javascript 代码是在相同的作用域里面,那就可能代码之间存在互相影 ...
- ecshop二次开发之后台秒杀
1.进入admin->includes->inc_menu.PHP中此文件为定义左侧功能模块超链接 2.添加include/inc_menu.php秒杀管理超链接找链接 $modules[ ...
- 我从HTML的meta中学到了什么
meta meta中有这样几个常用属性:http-equiv,name,content,包括html5新增的charset. 注意:content属性用来存储meta信息的内容,所有的主流浏览器都支持 ...
- linux系统命令配置文件
系统命令要独占地控制系统,并让一切正常工作.所有如 login(完成控制台用户身份验证阶段)或 bash(提供用户和计算机之间交互)之类的程序都是系统命令.因此,和它们有关的文件也特别重要.这一类别中 ...
- SPSS能做Cochran-Armitage趋势检验吗
SPSS能做Cochran-Armitage趋势检验吗 Cochran-Armitage (CA) 趋势检验是一种用于分析1个二分类变量和1个有序分类变量关联性的统计方法,由Cochran和Armti ...
- 使用HashMap编写一程序实现存储某班级学生信息
1. 使用HashMap编写一程序实现存储某班级学生信息,要求在屏幕上打印如下列表 学号 姓名 性别 年龄 001 张三 男 23 002 李四 男 ...
- Nginx教程(一) Nginx入门教程 (转)
1 Nginx入门教程 Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like协议下发行.由俄罗斯的程序设计师IgorSysoev所开 ...
- @Transactional注解事务
打了这个注解的类或者方法表示该类里面的所有方法或者这个方法的事务由spring处理,来保证事务的原子性,即方法里面对数据库操作,如果失败则spring负责回滚操作,成功提交操作 一.特性 1.serv ...
- LayUI+Echart实现图表
1.首先 定义一个容器存放图表 需要指定这个容器的大小 <div class="layui-card"> <div class="layui-card ...