1. 程式人生 > >ssm springaop 自定義註解加日誌資料到資料庫

ssm springaop 自定義註解加日誌資料到資料庫

弄得過程中出現過好多問題。(報錯時候切入方法不正常執行,)

1、需要導的包:slf4j-log4j12-1.6.2.jar  slf4j-api-1.6.2.jar  (這兩個jar包版本最好保持一致,之前我使用不同版本報錯)

aspectjweaver-1.6.8_2.jar    log4j-1.2.15.jar  aopalliance-1.0.jar

2、新建一個log.java  自定義註解類

  package com.user.sessionFilter;
 import java.lang.annotation.*;
 //https://blog.csdn.net/erlian1992/article/details/53402519   
 //自定義註解  相關知識參考上面連結
 @Target({ElementType.PARAMETER, ElementType.METHOD})  
 @Retention(RetentionPolicy.RUNTIME)  
 @Documented  
 public @interface Log {
   //自定義註解
     /** 要執行的操作型別比如:add操作 **/  
     public String operationType() default "";  
      
     /** 要執行的具體操作比如:新增使用者 **/  
     public String operationName() default "";

 }

3、新建一個SystemLogAspect .java  

 

執行順序如下: 

//正常 device : -->around-->before-->method-->after-->afterReturn

  //報錯 device : -->around-->before-->method-->after-->afterthrowing

package com.user.sessionFilter;
import java.lang.reflect.Method;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.user.entity.SystemLog;
import com.user.entity.User;
import com.user.service.SystemLogService;
@Aspect
@Component
public class SystemLogAspect {
  //正常 device : -->around-->before-->method-->after-->afterReturn
  //報錯 device : -->around-->before-->method-->after-->afterthrowing
    //注入Service用於把日誌儲存資料庫  
    @Autowired  //這裡我用resource註解,一般用的是@Autowired,他們的區別如有時間我會在後面的部落格中來寫
    private SystemLogService systemLogService;  
    @Autowired
    WebContextHolder  webContextHolder ;
    private  static  final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);  
    
    //Controller層切點  
    @Pointcut("execution(* com.user.service.AopService.*(..))") 
      //@Pointcut("execution(* com.user.action.AopController.*(..))")
    public  void controllerAspect() {  
    }  
     /* joinPoint.getArgs();//輸入的引數列表  
      joinPoint.getTarget().getClass().getName();//類全路徑  
      joinPoint.getSignature().getDeclaringTypeName();//介面全路徑  
      joinPoint.getSignature().getName();//呼叫的方法  
*/    /** 
     * 前置通知 用於攔截Controller層記錄使用者的操作 
     * 
     * @param joinPoint 切點 
     */ 
    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint) {
        System.out.println("==========執行controller前置通知===============");
        if(logger.isInfoEnabled()){
            logger.info("before " + joinPoint);
            logger.info("before start");
            joinPoint.getArgs();//輸入的引數列表  
            joinPoint.getTarget().getClass().getName();//類全路徑  
            joinPoint.getSignature().getDeclaringTypeName();//介面全路徑  
            joinPoint.getSignature().getName();//呼叫的方法  
            logger.info("before end");
        }
    }    
    
    //配置controller環繞通知,使用在方法aspect()上註冊的切入點
      @Around("controllerAspect()")
      public void around(ProceedingJoinPoint pjp ){
          System.out.println("==========開始執行controller環繞通知===============");
          long start = System.currentTimeMillis();
          try {
          pjp.proceed();
             // ((ProceedingJoinPoint) joinPoint).proceed();
              long end = System.currentTimeMillis();
              if(logger.isInfoEnabled()){
                  logger.info("around " + pjp + "\tUse time : " + (end - start) + " ms!");
              }
             
              System.out.println("==========結束執行controller環繞通知===============");
           } catch (Throwable e) {
               long end = System.currentTimeMillis();
               if(logger.isInfoEnabled()){
                   logger.info("around " + pjp + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
               }
           }
       }
     
    /**
     * 
     *Project:my
     *Description: 
     *@author Krystal
     * 2018年5月13日上午2:43:47
     * @param joinPoint
     */
      @After("controllerAspect()")  
    public  void after(JoinPoint joinPoint) {  
      System.out.println("=====after=====");  
     } 
     
     //配置後置返回通知,使用在方法aspect()上註冊的切入點
       @AfterReturning("controllerAspect()")
       public void afterReturn(JoinPoint joinPoint){
       HttpServletRequest request = webContextHolder.getRequest();      
           //讀取session中的使用者  
           HttpSession session = request.getSession();  
           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.setDescription(operationName);  
              
              log.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType);  
              log.setRequestIp(ip);  
              log.setExceptioncode( "Exceptioncode");  
              log.setExceptionDetail( "ExceptionDetail");  
              log.setParams( "setParams");  
              log.setCreateBy(user.getName());  
              log.setCreateDate(new Date());  
              //儲存資料庫  
              systemLogService.insertSystemLog(log);  
              System.out.println("=====controller後置通知結束=====");  
          }  catch (Exception e) {  
              //記錄本地異常日誌  
              logger.error("==後置通知異常==");  
              logger.error("異常資訊:{}", e.getMessage());  
          }  
       }
     
     /** 
      * 異常通知 用於攔截記錄異常日誌 
      * 
      * @param joinPoint 
      * @param e 
      */  
      @AfterThrowing(pointcut = "controllerAspect()", throwing="e")  
      public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {  
    // HttpSession session = request.getSession();  
          //讀取session中的使用者  
         // User user = (User) session.getAttribute("user");  
         // User user = (User) request.getSession().getAttribute("user");
         //獲取請求ip  
         
         //獲取使用者請求方法的引數並序列化為JSON格式字串  
    //  HttpServletRequest request = webContextHolder.getRequest();      
      ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();  
          HttpServletRequest request =attributes.getRequest();  
      //讀取session中的使用者  
          HttpSession session = request.getSession();  
          User user = (User) session.getAttribute("user"); 
          String ip = request.getRemoteAddr();
        
         //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.setDescription(operationName);  
             log.setExceptioncode(e.getClass().getName());  
             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.insertSystemLog(log);  
             System.out.println("=====異常通知結束=====");  
         }  catch (Exception ex) {  
             //記錄本地異常日誌  
            logger.error("==異常通知異常==");  
             logger.error("異常資訊:{}", ex.getMessage());  
         }  
          /*==========記錄本地異常日誌==========*/  
         logger.error("異常方法:{}異常程式碼:{}異常資訊:{}引數:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName());  
   
     }  
     

 }

4、controller ---》service

    @Controller
 @RequestMapping("SystemLog")
 public class AopController {
 
     @Autowired
     private AopService userService;
     
     @RequestMapping("testAOP")
    // @Log(operationType="addOperation:",operationName="新增使用者")  
     public ModelAndView testAOP( HttpServletRequest request, HttpServletResponse response,Model model){  
    String success ="0";
    try {
        User user =new User();
        user.setName("sy");
        user.setPsd("22");
              success= userService.insertUser(user);
              System.out.println("dsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsd");
} catch (Exception e) {
success="0";
}
    if("0".equals(success)){
    return new  ModelAndView("user/aopFail");
    }
         return new  ModelAndView("user/aopSuccess");

     }

service:(具體切入方法在此)

 

operationType與operationName和log.java中定義的一致  

@Log(operationType="addOperation:",operationName="新增使用者正常")  

public String insertUser(User user){
iUserMapper.insertUser(user);
return "1";
}

 5、springMvc.xml配置  紅色為配置

 <context:component-scan base-package="com.user" >
<!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> -->
</context:component-scan>
<aop:aspectj-autoproxy  />  

<!-- <aop:aspectj-autoproxy proxy-target-class="true" />  --> 
   <!--  </aop:aspectj-autoproxy>   -->
<!--開啟aopproxy-target-class="true"預設是false,更改為true時使用的是cglib動態代理,這樣只能實現對Controller層的日誌記錄
   proxy-target-class等於true是強制使用cglib代理,proxy-target-class預設是false,如果你的類實現了介面 就走JDK代理,如果沒有,走cglib代理  
      注:對於單利模式建議使用cglib代理,雖然JDK動態代理比cglib代理速度快,但效能不如cglib 

       https://blog.csdn.net/bobozai86/article/details/79229455 -->  

6、最後附上表

CREATE TABLE `systemlog` (
  `id` int(20) NOT NULL auto_increment,
  `description` varchar(100) default NULL,
  `method` varchar(100) default NULL,
  `requestIp` varchar(100) default NULL,
  `exceptioncode` varchar(100) default NULL,
  `exceptionDetail` varchar(100) default NULL,
  `params` varchar(100) default NULL,
  `createBy` varchar(100) default NULL,
  `createDate` timestamp NULL default NULL COMMENT '系統日誌',
  PRIMARY KEY  (`id`),
  KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8