先定一個注解,用於在Controller方法上記錄每個方法的用途。
-
package com.zjf.spring.annotation;
-
import java.lang.annotation.Documented;
-
import java.lang.annotation.ElementType;
-
import java.lang.annotation.Retention;
-
import java.lang.annotation.RetentionPolicy;
-
import java.lang.annotation.Target;
-
-
/**
-
* 定義注解
-
*
-
@Target : 用來說明該注解可以被聲明在那些元素之前。
-
-
ElementType.TYPE:說明該注解只能被聲明在一個類前。
-
-
ElementType.FIELD:說明該注解只能被聲明在一個類的字段前。
-
-
ElementType.METHOD:說明該注解只能被聲明在一個類的方法前。
-
-
ElementType.PARAMETER:說明該注解只能被聲明在一個方法參數前。
-
-
ElementType.CONSTRUCTOR:說明該注解只能聲明在一個類的構造方法前。
-
-
ElementType.LOCAL_VARIABLE:說明該注解只能聲明在一個局部變量前。
-
-
ElementType.ANNOTATION_TYPE:說明該注解只能聲明在一個注解類型前。
-
-
ElementType.PACKAGE:說明該注解只能聲明在一個包名前。
-
*/
-
@Target({ElementType.METHOD})
-
-
-
/**
-
@Retention :用來說明該注解類的生命周期。它有以下三個參數:
-
-
RetentionPolicy.SOURCE : 注解只保留在源文件中
-
-
RetentionPolicy.CLASS : 注解保留在class文件中,在加載到JVM虛擬機時丟棄
-
-
RetentionPolicy.RUNTIME : 注解保留在程序運行期間,此時可以通過反射獲得定義在某個類上的所有注解。
-
*/
-
@Retention(RetentionPolicy.RUNTIME)
-
/**
-
Annotation(注解)是JDK5.0及以后版本引入的。它可以用於創建文檔,跟蹤代碼中的依賴性,甚至執行基本編譯時檢查。
-
注解是以'@注解名'在代碼中存在的,根據注解參數的個數,我們可以將注解分為:標記注解、單值注解、完整注解三類。
-
它們都不會直接影響到程序的語義,只是作為注解(標識)存在,我們可以通過反射機制編程實現對這些元數據(用來描述數據的數據)的訪問。
-
另外,你可以在編譯時選擇代碼里的注解是否只存在於源代碼級,或者它也能在class文件中出現。
-
*/
-
/*
-
* 注解名字 LogAnnotation
-
*/
-
public @interface LogAnnotation {
-
//為LogAnnotation定義了一個desc屬性 用於描述方法的description 在記錄甚至日志的時候獲取
-
String desc() default "無描述信息";
-
}
定義切面:
-
package com.zjf.spring.log;
-
-
import java.lang.reflect.Method;
-
-
import javax.servlet.http.HttpServletRequest;
-
-
-
-
import org.aspectj.lang.JoinPoint;
-
import org.aspectj.lang.Signature;
-
import org.aspectj.lang.annotation.AfterReturning;
-
import org.aspectj.lang.annotation.AfterThrowing;
-
import org.aspectj.lang.annotation.Aspect;
-
import org.aspectj.lang.annotation.Pointcut;
-
import org.aspectj.lang.reflect.MethodSignature;
-
import org.slf4j.Logger;
-
import org.slf4j.LoggerFactory;
-
import org.springframework.stereotype.Component;
-
import org.springframework.web.context.request.RequestContextHolder;
-
import org.springframework.web.context.request.ServletRequestAttributes;
-
-
import com.zjf.spring.annotation.LogAnnotation;
-
-
/**
-
*
-
* @Aspect生命這個類是一個切面
-
*
-
*/
-
@Aspect
-
@Component
-
public class SystemLogAspect {
-
// 注入Service用於把日志保存數據庫
-
-
// 本地異常日志記錄對象
-
private static final Logger logger = LoggerFactory
-
.getLogger(SystemLogAspect.class);
-
-
// Controller層切點 通過注解進行切點 凡是生命了LogAnnotation注解的方法都要進入這個切面
-
@Pointcut("@annotation(com.zjf.spring.annotation.LogAnnotation)")
-
public void controllerAspect() {
-
-
}
-
-
/**
-
*
-
* 方法操作成功 會進入@AfterReturning
-
* @param joinPoint 代表會記錄切點的信息 就是代碼運行到切點是的變量環境
-
* 可以從joinPoint獲取使用的LogAnnotation信息
-
* 切點
-
*/
-
@AfterReturning(pointcut = "controllerAspect()")
-
public void doBefore(JoinPoint joinPoint) {
-
handleLog(joinPoint, null);
-
}
-
-
/**
-
* 方法執行中出現了異常 會出現在@AfterThrowing中
-
*/
-
@AfterThrowing(value = "controllerAspect()", throwing = "e")
-
public void doAfter(JoinPoint joinPoint, Exception e) {
-
handleLog(joinPoint, e);
-
}
-
-
private void handleLog(JoinPoint joinPoint,Exception e) {
-
try {
-
//從joinPoint獲得LogAnnotation注解
-
LogAnnotation controllerLog = giveController(joinPoint);
-
if(controllerLog == null)
-
{
-
return;
-
}
-
//獲取request
-
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
-
//通過request獲取session 然后獲取用戶信息
-
//通過request獲取ip
-
-
//處理設置注解上的參數 也就是方法的描述
-
String desc = controllerLog.desc();
-
System.out.println(desc);
-
//保存數據庫
-
-
} catch (Exception exp) {
-
//記錄本地異常日志
-
logger.error("==前置通知異常==");
-
logger.error("異常信息:{}", exp.getMessage());
-
exp.printStackTrace();
-
}
-
}
-
-
/**
-
* 是否存在注解,如果存在就記錄日志
-
* @param joinPoint
-
* @param controllerLog
-
* @return
-
* @throws Exception
-
*/
-
private static LogAnnotation giveController(JoinPoint joinPoint) throws Exception
-
{
-
/*
-
* JoinPoint可以獲取什么:
-
l java.lang.Object[] getArgs():獲取連接點方法運行時的入參列表;
-
l Signature getSignature() :獲取連接點的方法簽名對象;
-
l java.lang.Object getTarget() :獲取連接點所在的目標對象;
-
l java.lang.Object getThis() :獲取代理對象本身;
-
*/
-
Signature signature = joinPoint.getSignature();
-
/*
-
* MethodSignature可以獲取什么:
-
Class getReturnType(); 獲取方法的返回類型
-
Method getMethod(); 獲取方法本身
-
*/
-
MethodSignature methodSignature = (MethodSignature) signature;
-
/**
-
* Method可以獲取方法上的各種信息 比如方法名稱 方法參數 注解 參數上的注解等。
-
*/
-
Method method = methodSignature.getMethod();
-
-
if(method != null)
-
{
-
return method.getAnnotation(LogAnnotation.class);
-
}
-
return null;
-
}
-
}
聲明:aop:aspectj-autoproxy
關於aop:aspectj-autoproxy:
通過aop命名空間的<aop:aspectj-autoproxy />聲明自動為spring容器中那些配置@aspectJ切面的bean創建代理,織入切面。當然,spring 在內部依舊采用AnnotationAwareAspectJAutoProxyCreator進行自動代理的創建工作,但具體實現的細節已經被<aop:aspectj-autoproxy />隱藏起來了
<aop:aspectj-autoproxy />有一個proxy-target-class屬性,默認為false,表示使用jdk動態代理織入增強,當配為<aop:aspectj-autoproxy poxy-target-class="true"/>時,表示使用CGLib動態代理技術織入增強。不過即使proxy-target-class設置為false,如果目標類沒有聲明接口,則spring將自動使用CGLib動態代理。
-
<aop:aspectj-autoproxy />
在Controller方法中加入注解:
-
@RequestMapping(value="/test1")
-
@LogAnnotation(desc="test1方法")
-
public String test1(HttpServletRequest request)
-
{
-
request.setAttribute("att1", "val1");
-
return "test1";
-
}
