歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

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進行日志記錄  http://www.linuxidc.com/Linux/2015-11/124731.htm

使用Spring AOP進行性能監控  http://www.linuxidc.com/Linux/2012-07/64681.htm

利用Spring AOP 更新Memcached 緩存策略的實現  http://www.linuxidc.com/Linux/2012-03/56503.htm

Spring AOP的兩種代理  http://www.linuxidc.com/Linux/2015-11/125017.htm

Spring AOP的注解實例 http://www.linuxidc.com/Linux/2015-11/125018.htm

Copyright © Linux教程網 All Rights Reserved