一、源碼地址
https://github.com/dianjiu/spring-boot-learn
https://gitee.com/dianjiu/spring-boot-learn
https://gitee.com/point9/spring-boot-learn
二、目錄結構
三、源碼介紹
Java自定義註解一般使用場景爲:自定義註解+攔截器或者AOP,使用自定義註解來自己設計框架,使得代碼看起來非常優雅。
1、常用元註解
Target:描述了註解修飾的對象範圍,取值在java.lang.annotation.ElementType
定義,常用的包括:
- METHOD:用於描述方法
- PACKAGE:用於描述包
- PARAMETER:用於描述方法變量
- TYPE:用於描述類、接口或enum類型
Retention: 表示註解保留時間長短。取值在java.lang.annotation.RetentionPolicy
中,取值爲:
- SOURCE:在源文件中有效,編譯過程中會被忽略
- CLASS:隨源文件一起編譯在class文件中,運行時忽略
- RUNTIME:在運行時有效
2、引入依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
3、編寫自定義註解
package cn.point9.aspect.annotation;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import java.lang.annotation.*;
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface MyLog {
}
4、編寫AOP切面
package cn.point9.aspect.annotation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
@Slf4j
@Aspect
@Component
public class MyLogAspect {
// 2. PointCut表示這是一個切點,@annotation表示這個切點切到一個註解上,後面帶該註解的全類名
// 切面最主要的就是切點,所有的故事都圍繞切點發生
// logPointCut()代表切點名稱
@Pointcut("@annotation(cn.point9.aspect.annotation.MyLog)")
public void logPointCut() {
}
;
// 3. 環繞通知
@Around("logPointCut()")
public void logAround(ProceedingJoinPoint joinPoint) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
// 獲取連接點所在類名
String classname = joinPoint.getTarget().getClass().getSimpleName();
// 獲取連接點所在方法名稱
String methodName = joinPoint.getSignature().getName();
// 下面兩個數組中,參數值和參數名的個數和位置是一一對應的。
// 參數名
String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
// 參數值
Object[] args = joinPoint.getArgs();
StringBuilder sb = new StringBuilder();
//不爲空時便利組裝
if (argNames.length > 0 && args.length > 0) {
for (int i = 0; i < args.length; i++) {
sb.append(argNames[i] + ":" + args[i] + ";");
}
}
long startTime = System.currentTimeMillis();
log.info("\n進入【" + classname + "." + methodName + "()】方法的時間是:" + sdf.format(startTime) + "\n參數爲:" + sb.toString());
//執行到這裏開始走進來的方法體(必須聲明)
try {
joinPoint.proceed(args);
} catch (Throwable throwable) {
log.error("MyLogAspect logAround Exception By Method:" + classname + "." + methodName);
throwable.printStackTrace();
}
long endTime = System.currentTimeMillis();
long periodTime = endTime - startTime;
log.info("\n離開【" + classname + "." + methodName + "()】方法的時間是:" + sdf.format(endTime));
log.info("\n在【" + classname + "." + methodName + "()】方法中總共耗時爲:" + periodTime + " 毫秒");
}
}
5、拓展Aspect的執行順序
package cn.point9.aspect.annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* Around最先執行
* 然後在執行proceed方法之前,Before先執行
* 然後纔是方法體本身
* 然後是Around再結尾
* 最後纔是After
*/
@Component
@Aspect
@Order(10)
public class PermisionAspect {
@Pointcut("@annotation(PermisionAnnotation)")
private void AnnotationPointCut() { }
/**
* 定製一個環繞通知
* @param joinPoint
*/
@Around("AnnotationPointCut()&&@annotation(permisionAnnotation)")
public void around(ProceedingJoinPoint joinPoint, PermisionAnnotation permisionAnnotation) throws Throwable {
System.out.println("Around Begin");
String value=permisionAnnotation.value();//獲取註解中的傳值
System.out.println(value);
Method method = getMethod(joinPoint);
PermisionAnnotation permisionAnnotation1 = method.getAnnotation(PermisionAnnotation.class);
joinPoint.proceed();//執行到這裏開始走進來的方法體(必須聲明)
System.out.println("Around End");
}
private Method getMethod(JoinPoint joinPoint) throws Exception {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
return method;
}
//當想獲得註解裏面的屬性,可以直接注入改註解
//方法可以帶參數,可以同時設置多個方法用&&
@Before("AnnotationPointCut()")
public void before(JoinPoint joinPoint) throws Exception {
Method method = getMethod(joinPoint);
PermisionAnnotation permisionAnnotation = method.getAnnotation(PermisionAnnotation.class);
//通過反射可獲得註解上的屬性,然後做日誌記錄相關的操作。
System.out.println("Before");
}
@After("AnnotationPointCut()")
public void after(JoinPoint joinPoint) throws Exception {
Method method = getMethod(joinPoint);
PermisionAnnotation permisionAnnotation = method.getAnnotation(PermisionAnnotation.class);
System.out.println("After");
}
}
6、演示的Controller
package cn.point9.aspect.controller;
import cn.point9.aspect.annotation.MyLog;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class DemoController {
@MyLog
@GetMapping("/sourceC/{source_name}")
@ResponseBody
public String sourceC(@PathVariable("source_name") String sourceName){
return "你正在訪問sourceC資源";
}
}
四、效果演示
五、技術交流