SpringBoot自定義註解
我們經常使用自定義註解和AOP實現操作日誌、許可權、統計執行時間等功能,本文記錄使用SpringBoot實現自定義註解
本文在一個ssm專案的基礎上進行的
SpringBoot搭建SSM專案:https://blog.csdn.net/qidasheng2012/article/details/84233549
開始正文
1. 新增依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
普通的spring專案需要新增下面依賴
<!-- AspectJ --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.10</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.7.2</version> </dependency>
下面給出整個pom.xml檔案
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.springbootssm</groupId> <artifactId>ssm</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>ssm</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.18.BUILD-SNAPSHOT</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <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.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories> </project>
2. 編寫自定義註解類
package com.springbootssm.ssm.annotation;
import java.lang.annotation.*;
/**
* 操作日誌自定義註解
*/
@Target({ElementType.METHOD}) // 作用在方法上
@Retention(RetentionPolicy.RUNTIME) // 註解會在class位元組碼檔案中存在,在執行時可以通過反射獲取到
@Documented // 說明該註解將被包含在javadoc中
public @interface OperatingLogAnnotation {
/**
* 記錄操作描述
*/
String description() default "";
/**
* 增刪改的資料的型別
*/
Class<?> clazz();
}
- @Target
@Target 說明了Annotation所修飾的物件範圍
取值(ElementType)有:
1.CONSTRUCTOR:用於描述構造器
2.FIELD:用於描述域
3.LOCAL_VARIABLE:用於描述區域性變數
4.METHOD:用於描述方法
5.PACKAGE:用於描述包
6.PARAMETER:用於描述引數
7.TYPE:用於描述類、介面(包括註解型別) 或enum宣告
- @Retention
@Retention定義了該Annotation被保留的時間長短:某些Annotation僅出現在原始碼中,而被編譯器丟棄;而另一些卻被編譯在class檔案中;編譯在class檔案中的Annotation可能會被虛擬機器忽略,而另一些在class被裝載時將被讀取(請注意並不影響class的執行,因為Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命週期”限制。
取值(RetentionPoicy)有:
1.SOURCE:在原始檔中有效(即原始檔保留)
2.CLASS:在class檔案中有效(即class保留)
3.RUNTIME:在執行時有效(即執行時保留)
- @Documented
@Documented用於描述其它型別的annotation應該被作為被標註的程式成員的公共API,因此可以被例如javadoc此類的工具文件化。Documented是一個標記註解,沒有成員。
3. 編寫註解類切面
package com.springbootssm.ssm.annotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 操作日誌切面
*/
@Aspect
@Component
public class OperatingLogAspect {
// 切入點簽名
@Pointcut("@annotation(com.springbootssm.ssm.annotation.OperatingLogAnnotation)")
private void cut() {
}
// 前置通知
@Before("cut()")
public void BeforeCall() {
System.out.println("====前置通知start");
System.out.println("====前置通知end");
}
// 環繞通知
@Around(value = "cut()")
public Object AroundCall(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("====環繞通知start");
// 註解所切的方法所在類的全類名
String typeName = joinPoint.getTarget().getClass().getName();
System.out.println("目標物件:" + typeName);
// 註解所切的方法名
String methodName = joinPoint.getSignature().getName();
System.out.println("所切方法名:" + methodName);
StringBuilder sb = new StringBuilder();
// 獲取引數
Object[] arguments = joinPoint.getArgs();
for (Object argument : arguments) {
sb.append(argument.toString());
}
System.out.println("所切方法入參:" + sb.toString());
// 統計方法執行時間
long start = System.currentTimeMillis();
//執行目標方法,並獲得對應方法的返回值
Object result= joinPoint.proceed();
System.out.println("返回結果:" + result);
long end = System.currentTimeMillis();
System.out.println("====執行方法共用時:" + (end - start));
System.out.println("====環繞通知之結束");
return result;
}
// 後置通知
@After("cut()")
public void AfterCall() {
System.out.println("====後置通知start");
System.out.println("====後置通知end");
}
// 最終通知
@AfterReturning("cut()")
public void AfterReturningCall() {
System.out.println("====最終通知start");
System.out.println("====最終通知end");
}
// 異常通知
@AfterThrowing(value = "cut()", throwing="ex")
public void afterThrowing(Throwable ex) {
throw new RuntimeException(ex);
}
}
-
@Aspect : 作用是把當前類標識為一個切面供容器讀取
-
@component : 把普通pojo例項化到spring容器中,相當於配置檔案中的
-
@Pointcut : 定義一個切點
-
@Before : 標識一個前置增強方法,相當於BeforeAdvice的功能
-
@Around : 環繞增強方法
-
@After :後置增強方法
4. 在controler層使用註解
package com.springbootssm.ssm.controller;
import com.springbootssm.ssm.annotation.OperatingLogAnnotation;
import com.springbootssm.ssm.domain.Student;
import com.springbootssm.ssm.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class StudentController {
@Autowired
private StudentService studentService;
@OperatingLogAnnotation(description = "獲取所有使用者資訊", clazz = Student.class)
@RequestMapping("/getAll")
public List<Student> getAll(Student student){
System.out.println("=====真正執行方法 ");
return studentService.getAll();
}
}
5. 開啟瀏覽器,呼叫該介面,控制檯輸出
呼叫該介面:
http://localhost:8080/getAll?id=1&name=‘tom’
控制檯輸出:
====環繞通知start
目標物件:com.springbootssm.ssm.controller.StudentController
所切方法名:getAll
所切方法入參:Student{id=1, name=''tom'', age=null, sex='null'}
====前置通知start
====前置通知end
=====真正執行方法
返回結果:[Student{id=1, name='Tom', age=13, sex='1'}, Student{id=2, name='jick', age=14, sex='1'}, Student{id=3, name='mery', age=15, sex='0'}]
====執行方法共用時:327
====環繞通知之結束
====後置通知start
====後置通知end
====最終通知start
====最終通知end