java如何讓程式碼變得優雅——自定義註解
一、什麼是註解
java中,註解分兩種,元註解和自定義註解。
我們常用的一些註解,如:@Autowired、@Override等都是自定義註解。
二、java的元註解
可以理解為描述註解的註解,除了這幾個元註解,所有註解都是自定義註解。
- @Document:表示是否將註解資訊新增在java文件中
- @Target:表示註解用於什麼地方。
- ElementType.CONSTRUCTOR: 用於描述構造器
- ElementType.FIELD: 成員變數、物件、屬性(包括enum例項)
- ElementType.LOCAL_VARIABLE: 用於描述區域性變數
- ElementType.METHOD: 用於描述方法
- ElementType.PACKAGE: 用於描述包
- ElementType.PARAMETER: 用於描述引數
- ElementType.TYPE: 用於描述類、介面(包括註解型別) 或enum宣告
- @Retention:定義該註解的生命週期
- RetentionPolicy.SOURCE:在編譯階段丟棄。不寫入位元組碼。如:@Override,@SuppressWarnings都屬於這類註解。
- RetentionPolicy.CLASS:在類載入的時候丟棄。在位元組碼檔案的處理中有用。註解預設使用這種方式
- RetentionPolicy.RUNTIME:始終不會丟棄,執行期也保留該註解,因此可以使用反射機制讀取該註解資訊。
- @Inherited:定義該註釋和子類的關係
- 是一個標記註解,闡述了某個被標註的型別是被繼承的。如果一個使用了@Inherited修飾的annotation型別被用於一個class,則這個annotation將被用於該class的子類。
三、自定義註解
自定義註解編寫規則:
- Annotation型定義為@interface,所有的Annotation會自動繼承java.lang.Annotation這一介面,並且不能再去繼承別的類或是介面。
- 引數成員只能用public或預設(default)這兩個反問權修飾
- 引數成員只能用八種基本資料型別和String、Enum、Class、annotations等資料型別,以及這些型別的陣列。
- 要獲取類方法和欄位的註解資訊,必須通過java的反射技術來獲取Annotation物件,除此之外沒有別的獲取註解物件的方法
四、註解實現:記錄訪問日誌
4.1 pom檔案
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1-jre</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
4.2 自定義一個註解
/**
* @Author: KD
* @Date: 2021/1/1 1:02 下午
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TheLog {
/**
* 業務物件名稱
*
* @return
*/
public String name();
/**
* 描述瞭如何獲取id的表示式
*
* @return
*/
public String idExpression();
}
這個類中定義了註解的兩個屬性,第一個是name,第二個是一個Spel表示式。
4.3 切面
/**
* @Author: KD
* @Date: 2021/1/1 1:02 下午
*/
@Aspect
@Component
public class TheAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(TheAspect.class);
@Autowired
HttpServletRequest request;
@Around("@annotation(com.example.zhujie.anno.TheLog)")//對標註了TheLog註解的方法設定切面
public Object log(ProceedingJoinPoint pjp) throws Exception {
Method method = ((MethodSignature)pjp.getSignature()).getMethod();
TheLog theLog = method.getAnnotation(TheLog.class);
Object response = null;
try {
// 目標方法執行
response = pjp.proceed();
} catch (Throwable throwable) {
throw new Exception(throwable);
}
if (StringUtils.isNotEmpty(theLog.idExpression())) {
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(theLog.idExpression());
EvaluationContext context = new StandardEvaluationContext();
// 獲取引數值
Object[] args = pjp.getArgs();
// 獲取執行時引數的名稱
LocalVariableTableParameterNameDiscoverer discoverer
= new LocalVariableTableParameterNameDiscoverer();
String[] parameterNames = discoverer.getParameterNames(method);
// 將引數繫結到context中
if (parameterNames != null) {
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i]);
}
}
// 將方法的resp當做變數放到context中,變數名稱為該類名轉化為小寫字母開頭的駝峰形式
if (response != null) {
context.setVariable(
CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, response.getClass().getSimpleName()),
response);
}
// 解析表示式,獲取結果
String itemId = String.valueOf(expression.getValue(context));
// 執行日誌記錄
handle(theLog.name(), itemId);
}
return response;
}
private void handle(String name, String theId) {
// 通過日誌列印輸出
LOGGER.info("theType = " + name + ",theId = " + theId);
}
}
這裡主要是通過aspectj中的類庫來實現反射,獲取註解屬性,然後解析Spel表示式,最後在handle方法中實現日誌的列印輸出。
4.4 使用註解
/**
* @Author: KD
* @Date: 2021/1/1 12:41 下午
*/
@Controller
public class TestController {
@GetMapping("test")
@ResponseBody
@TheLog(name="test1",idExpression="#id")
public String test(String id){
return id;
}
}
其中idExpression是Spel表示式,"#id"表示獲取方法中名為id的區域性變數。
訪問url:
localhost:8080/test?id=123123
日誌列印如下:
2021-01-01 13:43:38.157 INFO 52624 --- [nio-8080-exec-1] com.example.zhujie.anno.TheAspect : theType = test1,theId = 123123
相關文章
- 想讓你的程式碼變得更加優雅嗎?
- 讓程式碼變得優雅簡潔的神器:Java8 Stream流式程式設計Java程式設計
- java中如何自定義註解Java
- Java 如何優雅的使用註解Java
- 自定義JAVA註解Java
- API開發 – 讓異常變得優雅API
- app直播原始碼,java自定義註解APP原始碼Java
- Java中的註解-自定義註解Java
- WPF自定義Panel:讓拖拽變得更簡單
- JAVA-註解(2)-自定義註解及反射註解Java反射
- 如何提高Java程式碼質量-優雅的寫程式碼Java
- Android 自定義優雅的BezierSeekBarAndroid
- 全面吃透JAVA Stream流操作,讓程式碼更加的優雅Java
- 自定義註解
- Java 自定義註解及使用場景Java
- 如何優雅地寫註釋:找到程式碼註釋的黃金平衡點
- java自定義註解學習(三)_註解解析及應用Java
- 自定義ConditionalOnXX註解
- @lombok註解背後的原理是什麼,讓我們走近自定義Java註解處理器LombokJava
- 聊一聊Java8 Optional,讓你的程式碼更加優雅Java
- Java註解-後設資料、註解分類、內建註解和自定義註解Java
- Lombok - 使用註解讓你的JavaBean變得更加簡潔LombokJavaBean
- 如何寫出優雅的程式碼?
- 如何優雅的打包前端程式碼前端
- 如何讓程式設計師變得沒朋友程式設計師
- DSL-讓你的 Ruby 程式碼更加優雅
- 如何讓git優雅的使用php-cs-fix格式化程式碼GitPHP
- 如何用 SpringBoot 優雅的寫程式碼Spring Boot
- 【優雅寫程式碼系統】springboot+mybatis+pagehelper+mybatisplus+druid教你如何優雅寫程式碼Spring BootMyBatisUI
- 優雅的程式碼
- Spring Boot 自定義註解失效Spring Boot
- SpringBoot自定義校驗註解Spring Boot
- 自定義校驗註解ConstraintValidatorAI
- springBoot自定義註解的使用Spring Boot
- 巧用自定義註解,一行程式碼搞定審計日誌行程
- 如何優雅得關閉協程呢
- Nginx 如何自定義變數?Nginx變數
- 如何使方法行數達到最優、常量與變數如何優雅的定義?變數