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自定义注解方式实现日志管理的实例讲解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

您可能感兴趣的文章:

  • spring中AOP 注解开发示例详解
  • Spring AOP 自定义注解的实现代码
  • 使用Spring的注解方式实现AOP实例
  • 关于spring中aop的注解实现方法实例详解
  • spring自定义注解实现拦截器的实现方法
  • springMVC自定义注解,用AOP来实现日志记录的方法
  • aop注解方式实现全局日志管理方法
(0)

相关推荐

  • spring中AOP 注解开发示例详解

    一.简介 AOP主要包含了通知.切点和连接点等术语,介绍如下: 通知(advice) 通知定义了切面是什么以及何时调用,何时调用包含以下几种 Before 在方法被调用之前调用通知 After 在方法完成之后调用通知,无论方法执行是否成功 After-returning 在方法成功执行之后调用通知 After-throwing 在方法抛出异常后调用通知 Around 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为 切点(PointCut) 通知定义了切面的什么和何时,切

  • springMVC自定义注解,用AOP来实现日志记录的方法

    需求背景 最近的一个项目,在项目基本完工的阶段,客户提出要将所有业务操作的日志记录到数据库中,并且要提取一些业务的关键信息(比如交易单号)体现在日志中. 为了保证工期,在查阅了资料以后,决定用AOP+自定义注解的方式来完成这个需求. 准备工作 自定义注解需要依赖的jar包有 aspectjrt-XXX.jar ,aspectjweaver-XXX.jar,XXX代表版本号. 自定义注解 在项目下单独建立了一个log包,来存放日志相关的内容 **.common.log.annotation //自

  • 关于spring中aop的注解实现方法实例详解

    前言 在之前的一篇文章中我们讲到spring的xml实现,这里我们讲讲使用注解如何实现aop呢.前面已经讲过aop的简单理解了,这里就不在赘述了.话不多说,来一起看看详细的介绍: 注解方式实现aop我们主要分为如下几个步骤: 1.在切面类(为切点服务的类)前用@Aspect注释修饰,声明为一个切面类. 2.用@Pointcut注释声明一个切点,目的是为了告诉切面,谁是它的服务对象.(此注释修饰的方法的方法体为空,不需要写功能比如 public void say(){};就可以了,方法名可以被候命

  • spring自定义注解实现拦截器的实现方法

    类似用户权限的需求,有些操作需要登录,有些操作不需要,可以使用过滤器filter,但在此使用过滤器比较死板,如果用的话,就必须在配置文件里加上所有方法,而且 不好使用通配符.这里可以采用一种比较简单灵活的方式,是采用spring 的 methodInterceptor拦截器完成的,并且是基于注解的.大概是用法是这样的: @LoginRequired @RequestMapping(value = "/comment") public void comment(HttpServletRe

  • aop注解方式实现全局日志管理方法

    1:日志实体类 public class SysLog { /** */ private Integer id; /** 日志描述*/ private String description; /** 执行的方法*/ private String method; /** 日志类型 0:操作日志:1:异常日志*/ private Integer logType; /** 客户端请求的ip地址*/ private String requestIp; /** 异常代码*/ private String

  • 使用Spring的注解方式实现AOP实例

    spring对AOP的实现提供了很好的支持.下面我们就使用Spring的注解来完成AOP做一个例子. 首先,为了使用Spring的AOP注解功能,必须导入如下几个包.aspectjrt.jar,aspectjweaver.jar,cglib-nodep.jar.然后我们写一个接口 package com.bird.service; public interface PersonServer { public void save(String name); public void update(S

  • Spring AOP 自定义注解的实现代码

    1.在Maven中加入以下以依赖: <!-- Spring AOP + AspectJ by shipengzhi --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>3.0.6.RELEASE</version> </dependency> <

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

    今天继续实现AOP,到这里我个人认为是最灵活,可扩展的方式了,就拿日志管理来说,用Spring AOP 自定义注解形式实现日志管理.废话不多说,直接开始!!! 关于配置我还是的再说一遍. 在applicationContext-mvc.xml中要添加的 <mvc:annotation-driven /> <!-- 激活组件扫描功能,在包com.gcx及其子包下面自动扫描通过注解配置的组件 --> <context:component-scan base-package=&qu

  • Spring Aop之AspectJ注解配置实现日志管理的方法

    最近项目要做一个日志功能,我用Spring Aop的注解方式来实现. 创建日志注解 package com.wyj.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lan

  • SpringBoot通过自定义注解与异步来管理日志流程

    目录 一.前言 二.基础环境 1. 导入依赖 2. 编写yml配置 三.数据库设计 四.主要功能 1. 编写注解 2. 业务类型枚举 3. 编写切片 4. ip工具类 5. 事件发布 6. 监听者 五.测试 1. controller 2. service 3. dao 4. 测试 5. 数据库 六.总结 一.前言 我们在企业级的开发中,必不可少的是对日志的记录,实现有很多种方式,常见的就是基于AOP+注解进行保存,同时考虑到程序的流畅和效率,我们可以使用异步进行保存! 二.基础环境 1. 导入

  • Spring AOP结合注解实现接口层操作日志记录

    目录 1.表和实体设计 1.实体设计 2.表结构设计 2.日志注解 3.核心AOP类 4.用到的工具类 5.测试类 6.测试结果 1.表和实体设计 1.实体设计 实体基类 @Data //映射将仅应用于其子类 @MappedSuperclass //指定要用于实体或映射超类的回调侦听器类.此注释可以应用于实体类或映射的超类. @EntityListeners(AuditingEntityListener.class) public class BaseEntity implements Seri

  • 详解Spring AOP自定义可重复注解没有生效问题

    目录 1. 问题背景 2. 不啰嗦,上代码 3. 问题排查 3.1 是不是切点写得有问题,于是换成如下形式: 3.2 是不是使用的地方不是代理对象 4. 问题原因 1. 问题背景 工作中遇到这样的场景:某个方法需要在不同的业务场景下执行特定的逻辑,该方法已经上生产,不想改变原来的代码,因此决定用AOP做个切面执行逻辑. 2. 不啰嗦,上代码 以下为核心代码: 定义注解: @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(Rete

  • Spring-基于Spring使用自定义注解及Aspect实现数据库切换操作

    实现思路 重写Spring的AbstractRoutingDataSource抽象类的determineCurrentLookupKey方法. 我们来看下Spring-AbstractRoutingDataSource的源码 AbstractRoutingDataSource获取数据源之前会先调用determineCurrentLookupKey方法查找当前的lookupKey. Object lookupKey = determineCurrentLookupKey(); DataSource

  • Redis+AOP+自定义注解实现限流

    目录 Redis安装 下载 解压 准备编译 编译 测试编译 安装 配置 运行 检查端口是否在使用中 查看redis的当前版本: 使redis可以用systemd方式启动和管理 查看本地centos的版本: 客户端连接redis Redis限流 1.引入依赖 2.application.yml配置 3.创建redisConfig,引入redisTemplate 自定义注解和拦截器 1.自定义注解 2.创建拦截器 3.将自定义拦截器加入到拦截器列表中 Redis安装 一提到Redis,相信大家都不会

  • Spring AOP使用接口方式实现

    目录 一. 环境准备 二.Spring接口方式实现AOP步骤 1. 业务接口实现 2. 业务类 3. 通知类 4. 自定义切## 点 5.配置xml文件 6. 方法入口 三. 分析 Spring 提供了很多的实现AOP的方式:Spring 接口方式,schema配置方式和注解. 本文重点介绍Spring使用接口方式实现AOP. 研究使用接口方式实现AOP, 以了解为目的. 更好地理解spring使用动态代理实现AOP. 通常我们使用的更多的是使用注解的方式实现AOP 下面来看看如何实现接口方式的

  • 使用spring aop 统一捕获异常和写日志的示例demo

    之前给大家介绍过Spring AOP的基础知识,需要的朋友点击了解下吧,这边我将给您介绍用spring AOP 实现的异常捕获和日志的小demo,我也会详细解释相关配置. 首先给大家看一下我的工程目录: 大家可以先用eclipse中新建一个maven工程,在工程中pom.xml按下面文件添加依赖: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XM

随机推荐