java注解结合aspectj AOP进行日志打印的操作

在很多系统开发中,我们希望在指定的方法调用之前或者之后能打印出该方法的调用时间以及方法的出参和入参,就可以使用spring的AOP,还可以结合自定义的注解进行进行一些指定参数的打印

例如:

一个分层的架构系统,每层都有自己的指定系统名字,并且每个方法都有自己指定的作用(通过注解指定,在切面的时候取出该参数),而且可以根据注解的指定日志类型(在注解中指定,在切面的时候取出参数进行判断,然后打印相对应的日志格式)。

1.首先需要自定义注解:

systemName:表示该系统的名称。

bizCode:表示具体的方法描述

logtype:表示日志的格式类型

package myspring;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LogAnnotation {
 String systemName();
 String bizCode();
 LogType logtype() default LogType.TIME;
}

2.定义日志格式枚举:

package myspring;
public enum LogType {
 TIME("TIME", "方法调用时间"),
 PARAM("PARAM", "参数打印");
 private String type;
 private String desc;
 LogType(String type, String desc) {
  this.type = type;
  this.desc = desc;
 }
 public String getType() {
  return type;
 }
 public void setType(String type) {
  this.type = type;
 }
 public String getDesc() {
  return desc;
 }
 public void setDesc(String desc) {
  this.desc = desc;
 }
}

3.切面代码:

其中execution(* *(..))

第一个* :表示所有返回值

第二个* :表示所有包匹配规则和所有类匹配规则以及所有方法匹配规则

两个点.. :表示任何参数匹配

例如:

execution(* *..*Service.*(..))

表示任何返回值 任何包以Service结尾的类或者实现类的任何方法任何参数

*.. :表示所有包

* :Service表示所有以Service结尾的类或者实现类

execution(* cn.lijie.MyService.*(..))

表示任何返回值 cn.lijie包下面 MyService类或者实现类的所有方法 所有参数

代码如下:

package myspring;
import com.alibaba.fastjson.JSON;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
@Component
@Aspect
public class LogAspect {
 private static Logger thisLog = LoggerFactory.getLogger(LogAspect.class);
 private static Logger timeLog = LoggerFactory.getLogger(TimeTypeLog.class);
 private static Logger paramLog = LoggerFactory.getLogger(ParamTypeLog.class);
 @Around("execution(* *(..)) && @annotation(logAnnotation)")
 public Object log(ProceedingJoinPoint point, LogAnnotation logAnnotation) {
  StopWatch stop = new StopWatch();
  stop.start();
  boolean flag = false;
  Object retValue = null;
  try {
   retValue = point.proceed();
   flag = true;
  } catch (Throwable throwable) {
   throwable.printStackTrace();
  } finally {
   stop.stop();
   if (logAnnotation.logtype().equals(LogType.TIME)) {
    timeLog(this.getMethod(point), point, stop.getTotalTimeMillis(), logAnnotation, flag);
   } else {
    paramLog(this.getMethod(point), point, retValue);
   }
  }
  return retValue;
 }
 private void timeLog(String method, ProceedingJoinPoint point, long totalTimeMillis, LogAnnotation logAnnotation, boolean flag) {
  timeLog.info("系统为:{},调用的方法名字:{},调用是否成功:{},运行时间为:{}ms", logAnnotation.systemName(), method, flag, totalTimeMillis);
 }
 private void paramLog(String method, ProceedingJoinPoint point, Object retValue) {
  try {
   String result = JSON.toJSONString(retValue);
   //获取入参
   Object[] args = point.getArgs();
   StringBuffer sb = new StringBuffer();
   for (Object obj : args) {
    String str = JSON.toJSONString(obj);
    sb.append(subStr(str, 200)).append(" ");
   }
   paramLog.info("调用方法为:{},入参为:{},出参为:{}", method, sb, subStr(result, 200));
  } catch (Exception e) {
   thisLog.warn("切面日志 参数日志打印异常,异常信息:{}", e.getMessage());
  }
 }
 private String getMethod(ProceedingJoinPoint pjp) {
  MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
  return methodSignature.getDeclaringTypeName() + "#" + methodSignature.getMethod().getName();
 }
 private String subStr(String string, int length) {
  if (!StringUtils.isEmpty(string)) {
   if (string.length() > length) {
    string = string.substring(0, 200);
    return string;
   }
  }
  return string;
 }
}

4.定义一个操作对象:

package myspring;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component("logTest")
public class LogTest {
 private static Logger logger = LoggerFactory.getLogger(LogTest.class);
 @LogAnnotation(systemName = "计算器系统", bizCode = "+", logtype = LogType.TIME)
 public int testLog01(int a, int b) {
  logger.info("进入+运算");
  return a + b;
 }
 @LogAnnotation(systemName = "计算器系统", bizCode = "-", logtype = LogType.PARAM)
 public int testLog02(int a, int b) {
  logger.info("进入-运算");
  return a - b;
 }
}

5.定义两个空类,用于区分不同类型的日志:

package myspring;
public class TimeTypeLog {
package myspring;
public class ParamTypeLog {
}

6.spring xml配置文件:

<context:component-scan base-package="myspring"/>
<aop:aspectj-autoproxy/>

7.启动spring的测试类:

package myspring;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
 public static void main(String[] args) {
  AbstractApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
  LogTest logTest = (LogTest) context.getBean("logTest");
  logTest.testLog01(1, 2);
  logTest.testLog02(3, 4);
  context.registerShutdownHook();
 }
}

8.pom

<properties>
  <spring_version>4.3.8.RELEASE</spring_version>
 </properties>
 <dependencies>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>${spring_version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${spring_version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-beans</artifactId>
   <version>${spring_version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-aop</artifactId>
   <version>${spring_version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>${spring_version}</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-aop</artifactId>
   <version>${spring_version}</version>
  </dependency>
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjrt</artifactId>
   <version>1.8.11</version>
  </dependency>
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.8.11</version>
  </dependency>
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
   <version>1.7.21</version>
  </dependency>
  <dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-classic</artifactId>
   <version>1.1.7</version>
  </dependency>
  <dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-core</artifactId>
   <version>1.1.7</version>
  </dependency>
  <dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>fastjson</artifactId>
   <version>1.2.4</version>
  </dependency>
 </dependencies>

最后执行测试的类,日志打印如下:

补充:spring boot 自定义注解实现自动打印方法日志(入参和返回值)

pom文件

<?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>
 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.1.4.RELEASE</version>
  <relativePath/> <!-- lookup parent from repository -->
 </parent>
 <groupId>com.aline</groupId>
 <artifactId>demo</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <name>demo</name>
 <description>Demo project for Spring Boot</description>
 <properties>
  <java.version>1.8</java.version>
 </properties>
 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-aop</artifactId>
  </dependency>
  <dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>fastjson</artifactId>
   <version>1.2.7</version>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
  </dependency>
 </dependencies>
 <build>
  <plugins>
   <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
   </plugin>
  </plugins>
 </build>
</project>

定义一个日志注解

SysLog.java

package com.aline.demo.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
}

定义一个日志实例缓存

LoggerCache.class
package com.aline.demo.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
public class LoggerCache {
 /**
  * 日志实例记录在内存中
  */
 private static HashMap<String, Logger> LOGERS = new HashMap<String, Logger>();
 /**
  * 根据类名获取缓存的日志实例
  * @param className 包名加类名 this.getClass().getName();
  * @return
  */
 public static Logger getLoggerByClassName(String className) {
  // 从静态map中获取日志实例
  Logger logger = LOGERS.get(className);
  // 如果没取到
  if (logger == null) {
   // 创建一个日志实例
   logger = LoggerFactory.getLogger(className);
   // 加入到静态map中
   LOGERS.put(className, logger);
  }
  // 返回
  return logger;
 }
}

定义一个切面实现日志记录

SysLogAspect.java

package com.aline.demo.aspect;
import com.alibaba.fastjson.JSON;
import com.aline.demo.util.LoggerCache;
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.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Component
public class SysLogAspect {
 @Pointcut("@annotation(com.aline.demo.annotation.SysLog)")
 public void log() {
 }
 /**
  * 加入注解自动记录方法日志
  *
  * @param joinPoint
  * @return
  * @throws Throwable
  */
 @Around(value = "log()")
 public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
  // 获取执行方法的类的名称(包名加类名)
  String className = joinPoint.getTarget().getClass().getName();
  // 获取实例和方法
  MethodSignature signature = (MethodSignature) joinPoint.getSignature();
  Method method = signature.getMethod();
  // 从缓存中获取日志实例
  Logger log = LoggerCache.getLoggerByClassName(className);
  // 记录日志
  log.info(className + "." + method.getName() + "() 执行");
  Object[] args = joinPoint.getArgs();
  log.info("Params\t===》\t" + JSON.toJSONString(args));
  // 执行方法获取返回值
  Object proceed = joinPoint.proceed();
  // 记录日志
  log.info("Returns\t===》\t" + JSON.toJSONString(proceed));
  // 返回
  return proceed;
 }
}

写个controller测试

TestController.java

package com.aline.demo.controller;
import com.aline.demo.annotation.SysLog;
import com.aline.demo.util.LoggerCache;
import org.slf4j.Logger;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
@RestController
@RequestMapping("/test")
public class TestController {
 static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
 @GetMapping("/now")
 public String now(){
  // 从缓存中获取日志
  Logger LOG = LoggerCache.getLoggerByClassName(this.getClass().getName());
  LOG.info("自定义日志 ==》 日志内容。。。");
  return sdf.format(new Date());
 }
 @GetMapping("/hello")
 @SysLog()
 public String hello(String name){
  return "Hello, " + name;
 }
}

访问 http://localhost:8080/test/hello?name=aline

打印日志:

2019-05-09 16:58:20.410 INFO 40004 --- [nio-8080-exec-1] c.aline.demo.controller.TestController : com.aline.demo.controller.TestController.hello() 执行,参数 ==》
2019-05-09 16:58:20.584 INFO 40004 --- [nio-8080-exec-1] c.aline.demo.controller.TestController : Params ===》 ["aline"]
2019-05-09 16:58:20.598 INFO 40004 --- [nio-8080-exec-1] c.aline.demo.controller.TestController : Returns ===》 "Hello, aline"

访问 http://localhost:8080/now

打印日志:

2019-05-09 16:58:29.208 INFO 40004 --- [nio-8080-exec-3] c.aline.demo.controller.TestController : 自定义日志 ==》 日志内容。。。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • Log4j定时打印日志及添加模块名配置的Java代码实例

    配置间隔时间,定时打印日志  接到个需求,通过log4j定时打印日志,需求描述如下:需要能够定时打印日志,时间间隔可配.说到定时,首先想到了DailyRollingFileAppender类,各种定时,根据datePattern,这个可以参考类SimpleDateFormat类,常见的一些定时设置如下: '.'yyyy-MM: 每月 '.'yyyy-ww: 每周 '.'yyyy-MM-dd: 每天 '.'yyyy-MM-dd-a: 每天两次 '.'yyyy-MM-dd-HH: 每小时 '.'yy

  • Java工程如何打印程序日志过程解析

    很久之前,有个朋友问我,如果一个老项目让你接手去进行后续维护,你会先从哪里入手.让自己更快地上手项目?当时我没有特别正面去回答这个朋友的问题,我说:一个老项目是否容易上手,一个非常关键的地方就是这个项目的日志是否打得足够好.因为通常来说,一个老项目相对比较稳定了,后续大概率不会有比较大的变更和改动,那么对于这样的项目,核心就是"维稳".但是任何人都无法保证项目在线上运行时不会出线上故障,在出现线上问题或者故障时,如何快速止损就是第一要义,而日志在止损过程中就扮演着非常重要的角色.日志打

  • java启动jar包将日志打印到文本的简单操作

    启动命令: java -jar weichi-1.0.0.jar 将命令打印到1.log上 java -jar weichi-1.0.0.jar > 1.log 补充知识:Java中日志的使用(包含指定日志信息输出到指定地方) 一.前言 对于我们开发者而言,日志存在的意义十分重大:本文主要是自己整理了关于日志的一些知识点,希望能帮助到需要的人,也希望各位能指出我的错误. 二.日志的作用 ① 记录运行信息,方便调试 ② 记录错误信息,方便排查错误 ③ 存储运行记录,方便后期的数据分析 三.日志的主

  • java注解结合aspectj AOP进行日志打印的操作

    在很多系统开发中,我们希望在指定的方法调用之前或者之后能打印出该方法的调用时间以及方法的出参和入参,就可以使用spring的AOP,还可以结合自定义的注解进行进行一些指定参数的打印 例如: 一个分层的架构系统,每层都有自己的指定系统名字,并且每个方法都有自己指定的作用(通过注解指定,在切面的时候取出该参数),而且可以根据注解的指定日志类型(在注解中指定,在切面的时候取出参数进行判断,然后打印相对应的日志格式). 1.首先需要自定义注解: systemName:表示该系统的名称. bizCode:

  • springboot配置aop切面日志打印过程解析

    这篇文章主要介绍了springboot配置aop切面日志打印过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.SpringBoot Aop说明 1. Aop AOP(Aspect-Oriented Programming,面向切面编程),它利用一种"横切"的技术,将那些多个类的共同行为封装到一个可重用的模块.便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性. 2. AOP相关概念: Aspect

  • SpringBoot自定义注解之实现AOP切面日志详解

    通过自定义注解的方式(如:@SysLog(obj = "操作对象", text = "操作内容"),在 SpringBoot 中来实现 AOP 切面统一打印出入参日志. 一.先看下项目结构 二.Maven JAR依赖 <!-- AOP -->     <dependency>             <groupId>org.springframework.boot</groupId>             <

  • java通过AOP实现全局日志打印详解

    目录 几个常用的切点注解,这次使用了@Before和@Around 切Controller打印请求的接口.参数.返回值以及耗时情况. 切Service打印日志,URL,请求方式,IP,类名,方法 总结 几个常用的切点注解,这次使用了@Before和@Around 1.@Before 前置增强(目标方法执行之前,执行注解标注的内容) 2.@AfterReturning 后置增强(目标方法正常执行完毕后,执行) 3.@Around 环绕增强(目标方法执行前后,分别执行一些代码) 4.@AfterTh

  • SpringBoot使用AOP统一日志管理的方法详解

    目录 前言 实现 1.引入依赖 2.定义logback配置 3.编写切面类 4.测试 前言 请问今天您便秘了吗?程序员坐久了真的会便秘哦,如果偶然点进了这篇小干货,就麻烦您喝杯水然后去趟厕所一边用左手托起对准嘘嘘,一边用右手滑动手机看完本篇吧. 实现 本篇AOP统一日志管理写法来源于国外知名开源框架JHipster的AOP日志管理方式 1.引入依赖 <!-- spring aop --> <dependency> <groupId>org.springframework

  • springboot使用自定义注解实现aop切面日志

    平时我们在开发过程中,代码出现bug时为了更好的在服务器日志中寻找问题根源,会在接口的首尾打印日志,看下参数和返回值是否有问题.但是手动的logger.info() 去编写时工作量较大,这时我们可以使用AOP切面,为所有接口的首尾打印日志. 实现AOP切面日志一般有两种方式: 1.拦截所有接口controller,在首尾打印日志2.拦截指定注解的接口,为有该注解的接口首尾打印日志 我们尝试用自定义注解来实现AOP日志的打印,这样拥有更高的灵活性.废话不多说,我们开始 1. 导入切面需要的依赖包

  • Spring Boot如何通过自定义注解实现日志打印详解

    前言 在我们日常的开发过程中通过打印详细的日志信息能够帮助我们很好地去发现开发过程中可能出现的Bug,特别是在开发Controller层的接口时,我们一般会打印出Request请求参数和Response响应结果,但是如果这些打印日志的代码相对而言还是比较重复的,那么我们可以通过什么样的方式来简化日志打印的代码呢? SpringBoot 通过自定义注解实现权限检查可参考我的博客:SpringBoot 通过自定义注解实现权限检查 正文 Spring AOP Spring AOP 即面向切面,是对OO

  • SpringBoot通过自定义注解实现日志打印的示例代码

    前言 在我们日常的开发过程中通过打印详细的日志信息能够帮助我们很好地去发现开发过程中可能出现的Bug,特别是在开发Controller层的接口时,我们一般会打印出Request请求参数和Response响应结果,但是如果这些打印日志的代码相对而言还是比较重复的,那么我们可以通过什么样的方式来简化日志打印的代码呢? SpringBoot 通过自定义注解实现权限检查可参考我的博客:SpringBoot 通过自定义注解实现权限检查 正文 Spring AOP Spring AOP 即面向切面,是对OO

  • Spring使用AspectJ的注解式实现AOP面向切面编程

    1.认识Spring AOP 1.1 AOP的简介 AOP:面向切面编程,相对于OOP面向对象编程. Spring的AOP的存在目的是为了解耦.AOP可以让一组类共享相同的行为.在OOP中只能通过继承类和实现接口,来使代码的耦合度增强,而且类的继承只能为单继承,阻碍更多行为添加到一组类上,AOP弥补了OOP的不足. 1.2 AOP中的概念 切入点(pointcut): 切入点(pointcut):在哪些类.哪些方法上切入. 通知(advice):在方法前.方法后.方法前后做什么. 切面(aspe

  • Spring AspectJ AOP框架注解原理解析

    什么是AspectJ AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件. AspectJ是一个基于Java语言的AOP框架 Spring2.0以后新增了对AspectJ切点表达式支持 @AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面 新版本Spring框架,建议使用AspectJ方式来开发AOP AspectJ表达式: 语法:exe

随机推荐