当前位置: 首页 > 编程笔记 >

spring AOP自定义注解方式实现日志管理的实例讲解

尉迟华翰
2023-03-14
本文向大家介绍spring AOP自定义注解方式实现日志管理的实例讲解,包括了spring AOP自定义注解方式实现日志管理的实例讲解的使用技巧和注意事项,需要的朋友参考一下

今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理。废话不多说,直接开始!!!

关于配置我还是的再说一遍。

在applicationContext-mvc.xml中要添加的

<mvc:annotation-driven />
<!-- 激活组件扫描功能,在包com.gcx及其子包下面自动扫描通过注解配置的组件 -->
<context:component-scan base-package="com.gcx" /> 
  
<!-- 启动对@AspectJ注解的支持 --> 
<!-- proxy-target-class等于true是强制使用cglib代理,proxy-target-class默认是false,如果你的类实现了接口 就走JDK代理,如果没有,走cglib代理 -->
<!-- 注:对于单利模式建议使用cglib代理,虽然JDK动态代理比cglib代理速度快,但性能不如cglib -->
<!--如果不写proxy-target-class="true"这句话也没问题-->
<aop:aspectj-autoproxy proxy-target-class="true"/> 
<!--切面-->
<bean id="systemLogAspect" class="com.gcx.annotation.SystemLogAspect"></bean>

接下来开始编写代码。

创建日志类实体

public class SystemLog {
  private String id;
  private String description;
  private String method;
  private Long logType;
  private String requestIp;
  private String exceptioncode;
  private String exceptionDetail;
  private String params;
  private String createBy;
  private Date createDate;
  public String getId() {
    return id;
  }
  public void setId(String id) {
    this.id = id == null ? null : id.trim();
  }
  public String getDescription() {
    return description;
  }
  public void setDescription(String description) {
    this.description = description == null ? null : description.trim();
  }
  public String getMethod() {
    return method;
  }
  public void setMethod(String method) {
    this.method = method == null ? null : method.trim();
  }
  public Long getLogType() {
    return logType;
  }
  public void setLogType(Long logType) {
    this.logType = logType;
  }
  public String getRequestIp() {
    return requestIp;
  }
  public void setRequestIp(String requestIp) {
    this.requestIp = requestIp == null ? null : requestIp.trim();
  }
  public String getExceptioncode() {
    return exceptioncode;
  }
  public void setExceptioncode(String exceptioncode) {
    this.exceptioncode = exceptioncode == null ? null : exceptioncode.trim();
  }
  public String getExceptionDetail() {
    return exceptionDetail;
  }
  public void setExceptionDetail(String exceptionDetail) {
    this.exceptionDetail = exceptionDetail == null ? null : exceptionDetail.trim();
  }
  public String getParams() {
    return params;
  }
  public void setParams(String params) {
    this.params = params == null ? null : params.trim();
  }
  public String getCreateBy() {
    return createBy;
  }
  public void setCreateBy(String createBy) {
    this.createBy = createBy == null ? null : createBy.trim();
  }
  public Date getCreateDate() {
    return createDate;
  }
  public void setCreateDate(Date createDate) {
    this.createDate = createDate;
  }
}

编写dao接口

package com.gcx.dao;
import com.gcx.entity.SystemLog;
public interface SystemLogMapper {
  int deleteByPrimaryKey(String id);
  int insert(SystemLog record);
  int insertSelective(SystemLog record);
  SystemLog selectByPrimaryKey(String id);
  int updateByPrimaryKeySelective(SystemLog record);
  int updateByPrimaryKey(SystemLog record);
}

编写service层

package com.gcx.service;
import com.gcx.entity.SystemLog;
public interface SystemLogService {
  int deleteSystemLog(String id);
  int insert(SystemLog record);
  
  int insertTest(SystemLog record);
  SystemLog selectSystemLog(String id);
  
  int updateSystemLog(SystemLog record);
}

编写service实现类serviceImpl

package com.gcx.service.impl;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.gcx.annotation.Log;
import com.gcx.dao.SystemLogMapper;
import com.gcx.entity.SystemLog;
import com.gcx.service.SystemLogService;
@Service("systemLogService")
public class SystemLogServiceImpl implements SystemLogService {
  @Resource
  private SystemLogMapper systemLogMapper;
  
  @Override
  public int deleteSystemLog(String id) {
    
    return systemLogMapper.deleteByPrimaryKey(id);
  }
  @Override
  
  public int insert(SystemLog record) {
    
    return systemLogMapper.insertSelective(record);
  }
  @Override
  public SystemLog selectSystemLog(String id) {
    
    return systemLogMapper.selectByPrimaryKey(id);
  }
  @Override
  public int updateSystemLog(SystemLog record) {
    
    return systemLogMapper.updateByPrimaryKeySelective(record);
  }
  @Override
  public int insertTest(SystemLog record) {
    
    return systemLogMapper.insert(record);
  }
}

到这里基本程序编写完毕

下面开始自定义注解

package com.gcx.annotation;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER, ElementType.METHOD}) 
@Retention(RetentionPolicy.RUNTIME) 
@Documented 
public @interface Log {
  /** 要执行的操作类型比如:add操作 **/ 
  public String operationType() default ""; 
   
  /** 要执行的具体操作比如:添加用户 **/ 
  public String operationName() default "";
}

下面编写切面

package com.gcx.annotation;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.UUID;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.gcx.entity.SystemLog;
import com.gcx.entity.User;
import com.gcx.service.SystemLogService;
import com.gcx.util.JsonUtil;
/**
 * @author 杨建 
 * @E-mail: email
 * @version 创建时间:2015-10-19 下午4:29:05
 * @desc 切点类 
 */
@Aspect
@Component
public class SystemLogAspect {
  //注入Service用于把日志保存数据库 
  @Resource //这里我用resource注解,一般用的是@Autowired,他们的区别如有时间我会在后面的博客中来写
  private SystemLogService systemLogService; 
  
  private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect. class); 
  
  //Controller层切点 
  @Pointcut("execution (* com.gcx.controller..*.*(..))") 
  public void controllerAspect() { 
  } 
  
  /** 
   * 前置通知 用于拦截Controller层记录用户的操作 
   * 
   * @param joinPoint 切点 
   */ 
  @Before("controllerAspect()")
  public void doBefore(JoinPoint joinPoint) {
    System.out.println("==========执行controller前置通知===============");
    if(logger.isInfoEnabled()){
      logger.info("before " + joinPoint);
    }
  }  
  
  //配置controller环绕通知,使用在方法aspect()上注册的切入点
   @Around("controllerAspect()")
   public void around(JoinPoint joinPoint){
     System.out.println("==========开始执行controller环绕通知===============");
     long start = System.currentTimeMillis();
     try {
       ((ProceedingJoinPoint) joinPoint).proceed();
       long end = System.currentTimeMillis();
       if(logger.isInfoEnabled()){
         logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
       }
       System.out.println("==========结束执行controller环绕通知===============");
     } catch (Throwable e) {
       long end = System.currentTimeMillis();
       if(logger.isInfoEnabled()){
         logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
       }
     }
   }
  
  /** 
   * 后置通知 用于拦截Controller层记录用户的操作 
   * 
   * @param joinPoint 切点 
   */ 
  @After("controllerAspect()") 
  public void after(JoinPoint joinPoint) { 
 
    /* HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); 
    HttpSession session = request.getSession(); */
    //读取session中的用户 
    // User user = (User) session.getAttribute("user"); 
    //请求的IP 
    //String ip = request.getRemoteAddr();
    User user = new User();
    user.setId(1);
    user.setName("张三");
    String ip = "127.0.0.1";
     try { 
      
      String targetName = joinPoint.getTarget().getClass().getName(); 
      String methodName = joinPoint.getSignature().getName(); 
      Object[] arguments = joinPoint.getArgs(); 
      Class targetClass = Class.forName(targetName); 
      Method[] methods = targetClass.getMethods();
      String operationType = "";
      String operationName = "";
       for (Method method : methods) { 
         if (method.getName().equals(methodName)) { 
          Class[] clazzs = method.getParameterTypes(); 
           if (clazzs.length == arguments.length) { 
             operationType = method.getAnnotation(Log.class).operationType();
             operationName = method.getAnnotation(Log.class).operationName();
             break; 
          } 
        } 
      }
      //*========控制台输出=========*// 
      System.out.println("=====controller后置通知开始====="); 
      System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType); 
      System.out.println("方法描述:" + operationName); 
      System.out.println("请求人:" + user.getName()); 
      System.out.println("请求IP:" + ip); 
      //*========数据库日志=========*// 
      SystemLog log = new SystemLog(); 
      log.setId(UUID.randomUUID().toString());
      log.setDescription(operationName); 
      log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType); 
      log.setLogType((long)0); 
      log.setRequestIp(ip); 
      log.setExceptioncode( null); 
      log.setExceptionDetail( null); 
      log.setParams( null); 
      log.setCreateBy(user.getName()); 
      log.setCreateDate(new Date()); 
      //保存数据库 
      systemLogService.insert(log); 
      System.out.println("=====controller后置通知结束====="); 
    } catch (Exception e) { 
      //记录本地异常日志 
      logger.error("==后置通知异常=="); 
      logger.error("异常信息:{}", e.getMessage()); 
    } 
  } 
  
  //配置后置返回通知,使用在方法aspect()上注册的切入点
   @AfterReturning("controllerAspect()")
   public void afterReturn(JoinPoint joinPoint){
     System.out.println("=====执行controller后置返回通知====="); 
       if(logger.isInfoEnabled()){
         logger.info("afterReturn " + joinPoint);
       }
   }
  
  /** 
   * 异常通知 用于拦截记录异常日志 
   * 
   * @param joinPoint 
   * @param e 
   */ 
   @AfterThrowing(pointcut = "controllerAspect()", throwing="e") 
   public void doAfterThrowing(JoinPoint joinPoint, Throwable e) { 
    /*HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); 
    HttpSession session = request.getSession(); 
    //读取session中的用户 
    User user = (User) session.getAttribute(WebConstants.CURRENT_USER); 
    //获取请求ip 
    String ip = request.getRemoteAddr(); */ 
    //获取用户请求方法的参数并序列化为JSON格式字符串 
    
    User user = new User();
    user.setId(1);
    user.setName("张三");
    String ip = "127.0.0.1";
    
    String params = ""; 
     if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) { 
       for ( int i = 0; i < joinPoint.getArgs().length; i++) { 
        params += JsonUtil.getJsonStr(joinPoint.getArgs()[i]) + ";"; 
      } 
    } 
     try { 
       
       String targetName = joinPoint.getTarget().getClass().getName(); 
       String methodName = joinPoint.getSignature().getName(); 
       Object[] arguments = joinPoint.getArgs(); 
       Class targetClass = Class.forName(targetName); 
       Method[] methods = targetClass.getMethods();
       String operationType = "";
       String operationName = "";
       for (Method method : methods) { 
         if (method.getName().equals(methodName)) { 
           Class[] clazzs = method.getParameterTypes(); 
           if (clazzs.length == arguments.length) { 
             operationType = method.getAnnotation(Log.class).operationType();
             operationName = method.getAnnotation(Log.class).operationName();
             break; 
           } 
         } 
       }
       /*========控制台输出=========*/ 
      System.out.println("=====异常通知开始====="); 
      System.out.println("异常代码:" + e.getClass().getName()); 
      System.out.println("异常信息:" + e.getMessage()); 
      System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType); 
      System.out.println("方法描述:" + operationName); 
      System.out.println("请求人:" + user.getName()); 
      System.out.println("请求IP:" + ip); 
      System.out.println("请求参数:" + params); 
        /*==========数据库日志=========*/ 
      SystemLog log = new SystemLog();
      log.setId(UUID.randomUUID().toString());
      log.setDescription(operationName); 
      log.setExceptioncode(e.getClass().getName()); 
      log.setLogType((long)1); 
      log.setExceptionDetail(e.getMessage()); 
      log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")); 
      log.setParams(params); 
      log.setCreateBy(user.getName()); 
      log.setCreateDate(new Date()); 
      log.setRequestIp(ip); 
      //保存数据库 
      systemLogService.insert(log); 
      System.out.println("=====异常通知结束====="); 
    } catch (Exception ex) { 
      //记录本地异常日志 
      logger.error("==异常通知异常=="); 
      logger.error("异常信息:{}", ex.getMessage()); 
    } 
     /*==========记录本地异常日志==========*/ 
    logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params); 
 
  } 
  
}

我这里写的比较全,前置通知,环绕通知,后置通知,异常通知,后置饭后通知,都写上了,在我们实际编写中不写全也没事,我习惯上把记录日志的逻辑写在后置通知里面,我看网上也有些在前置通知里面的,但我感觉写在后置通知里比较好。

下面开始在controller中加入自定义的注解!!

package com.gcx.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.gcx.annotation.Log;
import com.gcx.service.UserService;
@Controller
@RequestMapping("userController")
public class UserController {
  @Autowired
  private UserService userService;
  
  @RequestMapping("testAOP")
  @Log(operationType="add操作:",operationName="添加用户") 
  public void testAOP(String userName,String password){    
    userService.addUser(userName, password);
  }
}

下面编写测试类

@Test
  public void testAOP1(){
    //启动Spring容器    
    ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"classpath:applicationContext-mvc.xml","classpath:applicationContext-dataSource.xml"});
    //获取service或controller组件
    UserController userController = (UserController) ctx.getBean("userController");
    userController.testAOP("zhangsan", "123456");
  }

数据库数据:

我原本想写两个切点,一个是service层,一个是controller层,service层是用来记录异常信息的日志,而controller层的是用来记录功能的日志,运行结果如下。

这样做的话不知道在实际的项目中运行效率好不好,在这里请看到博客的大牛给点建议!!

以上这篇spring AOP自定义注解方式实现日志管理的实例讲解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持小牛知识库。

 类似资料:
  • 本文向大家介绍aop注解方式实现全局日志管理方法,包括了aop注解方式实现全局日志管理方法的使用技巧和注意事项,需要的朋友参考一下 1:日志实体类 2:maven需要的jar 这里要求项目使用的是jdk1.7 3:springServlet-mvc.xml 加上proxy-target-class="true"是为了可以拦截controller里面的方法 4:定义切面,我这里主要写前置通知和异常通

  • 本文向大家介绍springMVC自定义注解,用AOP来实现日志记录的方法,包括了springMVC自定义注解,用AOP来实现日志记录的方法的使用技巧和注意事项,需要的朋友参考一下 需求背景 最近的一个项目,在项目基本完工的阶段,客户提出要将所有业务操作的日志记录到数据库中,并且要提取一些业务的关键信息(比如交易单号)体现在日志中。 为了保证工期,在查阅了资料以后,决定用AOP+自定义注解的方式来完

  • 本文向大家介绍Spring Aop之AspectJ注解配置实现日志管理的方法,包括了Spring Aop之AspectJ注解配置实现日志管理的方法的使用技巧和注意事项,需要的朋友参考一下 最近项目要做一个日志功能,我用Spring Aop的注解方式来实现。 创建日志注解 创建切面通知类 记录操作的方法名,参数和花费的时间,使用环绕通知 扫描和启动aop注解 日志注解的应用 效果 以上这篇Sprin

  • 本文向大家介绍实例讲解Android自定义控件,包括了实例讲解Android自定义控件的使用技巧和注意事项,需要的朋友参考一下 小编在此之前给大家介绍过关于Android自定义控件的用法等,需要的可以参考下: Android开发之自定义控件用法详解 详解Android自定义控件属性 可以看到QQ上的ToolBar其实就是一个自定义的view,可以看到不同的界面就是简单地修改了文字而已,在第二张与第

  • 本文向大家介绍Android自定义控件LinearLayout实例讲解,包括了Android自定义控件LinearLayout实例讲解的使用技巧和注意事项,需要的朋友参考一下 很多时候Android常用的控件不能满足我们的需求,那么我们就需要自定义一个控件了。今天做了一个自定义控件的实例,来分享下。 首先定义一个layout实现按钮内部布局:  接下来写一个类继承LinearLayout,导入刚刚

  • 本文向大家介绍Spring Boot如何通过自定义注解实现日志打印详解,包括了Spring Boot如何通过自定义注解实现日志打印详解的使用技巧和注意事项,需要的朋友参考一下 前言 在我们日常的开发过程中通过打印详细的日志信息能够帮助我们很好地去发现开发过程中可能出现的Bug,特别是在开发Controller层的接口时,我们一般会打印出Request请求参数和Response响应结果,但是如果这些