背景
在工作中,日志是查找BUG的重要手段之一,常规的方式就是代码中通过面向过程的方式,即在代码中将想要显示的变量输出。
logger.debug("id="+id);
然而,在是调试当中,经常需要查看方法的传入参数,但是如果代码中没有log,那查日志就GG了;但是在每个方法都log一段代码,那就有点累赘了,所以接下来将用Spring中的AOP(切面编程)的方式,实现方法环绕日志。
需求
用切面编程的方式,记录代码运行中传入变量的值。
步骤
1.maven依赖
2.编写切面
3.注入bean和配置Pointcut
过程
- maven依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${springframework.version}</version>
</dependency>
- 编写切面(用于记录方法参数的日志操作)
package com.liushiyao.common;
import java.util.Arrays;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class LogAspect {
private final Logger logger = Logger.getLogger(this.getClass());
/**
* 输出调用的参数
*
* @param joinPoint
*/
public void after(JoinPoint joinPoint) {
//获取参数列表
Object[] arguments = joinPoint.getArgs();
//方法名
String methodName = joinPoint.getSignature().getName();
String targetName = joinPoint.getTarget().getClass().getName();
logger.debug("{refer}" + targetName + "." + methodName + ",{arg}" + Arrays.toString(arguments));
}
}
- 类LogAspect即为AOP编程中的Aspect,里面用来声明before(略),after,around(略)
- after方法即为AOP编程中的Advice,是AOP程序的具体操作。
- after方法是调用方法之后执行的,通过JoinPoint对象,可以获得方法的参数列表已经对应的名字等等
- 注入bean和配置Pointcut
在dispatcher-servlet.xml增加以下代码
<!--指定扫描目录-->
<aop:aspectj-autoproxy proxy-target-class="true" />
<!--将日志类注入到bean中。-->
<bean id="logAspect" class="com.liushiyao.common.LogAspect"></bean>
<aop:config>
<!--调用日志类-->
<aop:aspect id="LogAspect" ref="logAspect">
<!--配置在service包下所有的类在调用之前都会被拦截-->
<aop:pointcut id="log" expression="execution(* com.liushiyao.blog.service..*.*(..))"/>
<!--方法前触发 <aop:before pointcut-ref="log" method="before"/>-->
<!-- 方法后触发 --><aop:after pointcut-ref="log" method="after"/>
<!-- 环绕触发 <aop:around pointcut-ref="log" method="around"/> -->
</aop:aspect>
</aop:config>
- pointcut(切点),匹配service下的所有方法,当service中的方法被调用的时候,就会执行after方法。
运行
写个测试的controller
@RequestMapping("/aop")
@CrossOrigin
private void aop(Integer integer,String string){
testService.aopLogTest(integer,string);
}
- controller使用到了testService中的aopLogTest的方法
而aopLogTest方法只是个空方法,
public void aopLogTest(int arg,String strArg){
}
调用接口,输入日志
[DEBUG] 2019-10-11 00:08:58,739 method:com.liushiyao.common.LogAspect.after(LogAspect.java:30)
{refer}com.liushiyao.blog.service.TestService.aopLogTest,{arg}[1, 我是字符串]
从日志可以看出,通过AOP的方式被调用的方法的参数列表自动的以日志的方式记录了下来(即使被调用的方法中没有手动把方法参数打印出来),这就是面向切面编程。
拓展
pointcut是通过配置文件匹配的,那能不能通过注解的方式(当然,匹配的方式也可以实现,只是注解更加的灵活),想要记录哪些方法就记录哪些方法?
当然可以!
- 先定义一个注解
import com.liushiyao.common.def.Log;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AutoLog {
//日志类型
public int type () default Log.Type.DEBUG;
}
- 定义日志类型
public class Log {
public static final class Type{
public static final int DEBUG = 1;
public static final int INFO = 2;
public static final int ERROR = 3;
}
}
- 对after方法进行改造
/**
* 输出调用的参数
*
* @param joinPoint
*/
public void after(JoinPoint joinPoint) {
//获取参数列表
Object[] arguments = joinPoint.getArgs();
//方法名
String methodName = joinPoint.getSignature().getName();
String targetName = joinPoint.getTarget().getClass().getName();
Class targetClass = null;
try {
targetClass =
Class.forName(targetName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method methods [] = targetClass.getMethods();
for (Method method:methods){
//找到对应的方法
if(method.getName().equals(methodName)){
//是否有注解
AutoLog autoLog = method.getAnnotation(AutoLog.class);
if(autoLog != null){
if(autoLog.type() == Log.Type.DEBUG){
logger.debug("{refer}" + targetName +"." + methodName + ",{arg}" + Arrays.toString(arguments));
}else if(autoLog.type() == Log.Type.INFO){
logger.info("{refer}" + targetName +"." + methodName + ",{arg}" + Arrays.toString(arguments));
}else if(autoLog.type() == Log.Type.ERROR){
logger.error("{refer}" + targetName +"." + methodName + ",{arg}" + Arrays.toString(arguments));
}
}
}
}
}
- 对需要打日志的方式加注解
- TestService
public void aopLogTest(int arg,String strArg) {
}
@AutoLog(type = Log.Type.INFO)
public void aopLogTest2(String strArg){
}
@RequestMapping("/aop")
private void aop(Integer integer,String string){
testService.aopLogTest(integer,string);
testService.aopLogTest2(string);
}
- 运行结果
[INFO ] 2019-10-12 22:16:17,020 method:com.liushiyao.common.LogAspect.after(LogAspect.java:52)
{refer}com.liushiyao.blog.service.testService.aopLogTest2,{arg}[555]
可以看出只有加了@AutoLog的方法才会记录日志。
以上就是对环绕日志应用和注解日志的介绍,如有错误,欢迎指正