概念:java提供了一種原程序中的元素關聯任何信息和任何元數據的途徑和方法
JDK內置系統註解:
@Override 用於修飾此方法覆蓋了父類的方法;
@Deprecated 用於修飾已經過時的方法;
@Suppvisewarnings 用於通知java編譯器禁止特定的編譯警告。
註解按照運行機制劃分
源碼註解:註解只在源碼中存在,編譯成.class文件就不存在了;
編譯時註解:註解在源碼和.class文件中都存在(例:JDK自帶註解);
運行時註解:在運行階段還起作用,甚至會影響運行邏輯的註解(Autowired);
自定義註解的元註解:
- @Target—作用域(constructor(構造方法聲明),field(字段聲明),local_variable(局部變量聲明),method(方法聲明),package(包聲明),parameter(參數聲明),type(類,接口聲明))
- @Retention—生命週期(source:只在源碼顯示,編譯時會丟棄。class:編譯時會記錄到class中,運行時忽略。runtime:運行時存在,可以通過反射讀取)
- Inherited—標識註解(允許子類繼承)
- Documented—生成Javadoc
- 若成員只有一個,必須爲value,這個參數賦值可以不寫value=XXX
成員以無參無異常方式聲明。成員類型是受限的,合法的類型包括原始類型及String,Class,Annotation, Enumeration.
上面用了註解,程序中獲得註解信息的方法是反射。
Class cls = Class.forName(“”);//使用類加載器加載類
cls.isAnnotationPresent(xx.class);//判斷cls這個類上是否有xx的註解,找到類上註解
xx a = (xx)cls.getAnnotation(xx.class);//取得其上的註解
下面附一個實例,是框架如何使用註解描述參數。
結構圖
A.java
package javaBasic;
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.PARAMETER ,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface A {
String value();
String name() default "bingone";
int age() default 20;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
B.java
package javaBasic;
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.PARAMETER ,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface B {
String value();
String name() default "bingone";
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
DataClass.java
package javaBasic;
public class DataClass {
public String name;
public int age;
public DataClass(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
RunClass.java
package javaBasic;
public class RunClass {
public static void run(@A("str") String str,@B("age")int age){
System.out.println("In run Method str:" + str + "age:" + age);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
TestClass.java
package javaBasic;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* 通過註解傳參數
* @author gg_gogoing
*
*/
public class TestClass {
public static void parseMethod(DataClass data,Object obj,String mthname){
//驗證是否有註解
//若有則兩種方法:
//1. 在編譯時刻已經有了對應的表,查表即可
//2. 如下的遍歷方式。
if(obj instanceof RunClass){
String str = null;
int age = 0;
Method [] methods = (Method[])obj.getClass().getMethods();
for(Method method :methods){
if(method.getName().equals(mthname)){
Annotation[][] annotations = method.getParameterAnnotations();
for(Annotation[] tt : annotations){
for(Annotation t:tt){
if(t instanceof A){
str = data.name;
}else if(t instanceof B){
age = data.age;
}
}
}
RunClass.run(str, age);
}
}
}
}
public static void main(String[] args) throws Exception, RuntimeException { // 主方法
//不直接傳參數,而是將一個類中的數據傳入
DataClass dc = new DataClass("gg_gogoing", 20);
parseMethod(dc, new RunClass(), "run");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
日誌記錄簡化,只需要在action或者controller的方法上加logging註解即可。註解:@Logging(description = "{username}登錄"),description是註解內容,{}中爲動態參數,是傳入該方法中的指定po的屬性。
註解類代碼:
- package cn.com.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;
- /**
- * 日誌註解
- * @author ZhangShaobo
- * @date 2017-09-04
- */
- @Documented
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface Logging {
- String description();
- }
具體的日誌實現類,該類中loginfo方法需要在攔截器中調用。
- package cn.com.util;
- import java.lang.reflect.Method;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Map;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- import org.apache.poi.ss.formula.functions.T;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import cn.com.annotation.Logging;
- /**
- * 日誌註解的具體實現類
- * @author ZhangShaobo
- * @date 2018-09-04
- */
- public class LogAnnoUtil {
- private static Logger log = LoggerFactory.getLogger(LogAnnoUtil.class);
- /**
- * 根據註解記錄日誌
- * @param t action對象
- * @param methodName 調用的action方法名稱
- * @param map 參數map
- */
- public static <T> void loginfo(T t, String methodName, Map map){
- Method [] methods = t.getClass().getDeclaredMethods();
- for (final Method m : methods) {
- if (m.getName().equals(methodName)) {
- if(m.isAnnotationPresent(Logging.class)){
- String desc = m.getAnnotation(Logging.class).description();
- List<String> list = descFormat(desc);
- for (String s : list) {
- String value = map.get(s).toString();
- desc = desc.replace("{"+s+"}", value);
- }
- // 暫時只是輸出到了控制檯,具體需要存庫或者存緩存,需要改這段代碼
- log.info(desc);
- }
- }
- }
- }
- /**
- * 獲取日誌信息中的動態參數,然後替換
- * @param desc
- * @return
- */
- private static List<String> descFormat(String desc){
- List<String> list = new ArrayList<>();
- Pattern pattern = Pattern.compile("\\{([^\\}]+)\\}");
- Matcher matcher = pattern.matcher(desc);
- while(matcher.find()){
- String t = matcher.group(1);
- list.add(t);
- }
- return list;
- }
- }
攔截器中寫調用,以上代碼實現的是在struts框架中用註解記錄日誌,springmvc或者springboot需要適當更改代碼。有好的建議或者有不明白的地方歡迎評論溝通!