最后我們寫一個(gè)測(cè)試類
public class TestAn {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {
//獲取Person class 實(shí)例
Class c1 = Person.class;
//反射獲取 類上的注解
MyAnnotation classAnnotation = c1.getAnnotation(MyAnnotation.class);
System.out.println(classAnnotation.msg());
//反射獲取 private屬性上的注解
Field we = c1.getDeclaredField("weight");
MyAnnotation fieldAnnotation = we.getAnnotation(MyAnnotation.class);
System.out.println(fieldAnnotation.msg());
//反射獲取 public屬性上的注解
Field he = c1.getDeclaredField("height");
MyAnnotation field2Annotation = he.getAnnotation(MyAnnotation.class);
System.out.println(field2Annotation.msg());
//反射獲取 方法上的注解
Method me = c1.getMethod("dance",null);
MyAnnotation methodAnnotation = me.getAnnotation(MyAnnotation.class);
System.out.println(methodAnnotation.msg());
}
}
結(jié)果:
this person class this person field private this person field public this person method
我們通過反射讀取api時(shí),一般會(huì)先去校驗(yàn)這個(gè)注解存不存在:
if(c1.isAnnotationPresent(MyAnnotation.class)) {
//存在 MyAnnotation 注解
}else {
//不存在 MyAnnotation 注解
}
我們發(fā)現(xiàn)反射真的很強(qiáng)大,不僅可以讀取類的屬性、方法、構(gòu)造器等信息,還可以讀取類的注解相關(guān)信息。
那反射是如何實(shí)現(xiàn)工作的?
我們來看下源碼:
從 c1.getAnnotation(MyAnnotation.class);
通過idea點(diǎn)進(jìn)去查看源碼,把重點(diǎn)的給貼出來,其他的就省略了
Map<Class extends Annotation>, Annotation> declaredAnnotations =
AnnotationParser.parseAnnotations(getRawAnnotations(), getConstantPool(), this);
parseAnnotations()去 分析注解 ,其第一個(gè)參數(shù)是 獲取原始注解,第二個(gè)參數(shù)是獲取常量池內(nèi)容
public static Annotation annotationForMap(final Class extends Annotation> var0, final Map<String, Object> var1) {
return (Annotation)AccessController.doPrivileged(new PrivilegedAction() {
public Annotation run() {
return (Annotation)Proxy.newProxyInstance(var0.getClassLoader(), new Class[]{var0}, new AnnotationInvocationHandler(var0, var1));
}
});
}
Proxy._newProxyInstance_(var0.getClassLoader(), new Class[]{var0}, new AnnotationInvocationHandler(var0, var1)
創(chuàng)建動(dòng)態(tài)代理,此處var0參數(shù)是由常量池獲取的數(shù)據(jù)轉(zhuǎn)換而來。
我們監(jiān)聽此處的var0:
image-20220616225518195
可以推斷出注解相關(guān)的信息 是存放在常量池中的
我們來總結(jié)一下,反射調(diào)用getAnnotations(MyAnnotation.class)
方法的背后主要操作:
解析注解parseAnnotations()的時(shí)候 從該注解類的常量池中取出注解相關(guān)的信息,將其轉(zhuǎn)換格式后,通過newProxyInstance(注解的類加載器,注解的class實(shí)例 ,AnotationInvocationHandler實(shí)例)
來創(chuàng)建代理對(duì)象,作為參數(shù)傳進(jìn)去,最后返回一個(gè)代理實(shí)例。
其中AnotationInvocationHandler類是一個(gè)典型的動(dòng)態(tài)代理類, 這邊先挖個(gè)坑,暫不展開,不然這篇文章是寫不完了
關(guān)于動(dòng)態(tài)代理類我們只需先知道: 對(duì)象的執(zhí)行方法,交給代理來負(fù)責(zé)
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
...
private final Map<String, Object> memberValues;//存放該注解所有屬性的值
private transient volatile Method[] memberMethods = null;
AnnotationInvocationHandler(Class extends Annotation> var1, Map<String, Object> var2) {
...
}
public Object invoke(Object var1, Method var2, Object[] var3) {
...
//調(diào)用委托類對(duì)象的方法,具體等等一些操作
}
...
}
反射調(diào)用getAnnotations(MyAnnotation.class)
,返回一個(gè)代理實(shí)例,我們可以通過這個(gè)實(shí)例來操作該注解
示例:注解 模擬訪問權(quán)限控制
當(dāng)我們引入springsecurity來做安全框架,然后只需添加@PreAuthorize**(**"hasRole('Admin')"**)**
注解,就能實(shí)現(xiàn)權(quán)限的控制,簡(jiǎn)簡(jiǎn)單單地一行代碼,就優(yōu)雅地實(shí)現(xiàn)了權(quán)限控制,覺不覺得很神奇?讓我們一起模擬一個(gè)出來吧
@Retention(RetentionPolicy.RUNTIME)
public @interface MyPreVer {
String value() default "no role";
}
public class ResourceLogin {
private String name;
@MyPreVer(value = "User")
private void rsA() {
System.out.println("資源A");
}
@MyPreVer(value = "Admin")
private void rsB() {
System.out.println("資源B");
}
}
public class TestLogin {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
//模擬 用戶的權(quán)限
String role = "User";
//模擬 需要的權(quán)限
final String RoleNeeded = "Admin";
//獲取Class實(shí)例
Class c1 = ResourceLogin.class;
//訪問資源A
Method meA = c1.getDeclaredMethod("rsA",null);
MyPreVer meAPre = meA.getDeclaredAnnotation(MyPreVer.class);
if(meAPre.value().equals(RoleNeeded)) {//模擬攔截器
meA.setAccessible(true);
meA.invoke(c1.newInstance(),null);//模擬訪問資源
}else {
System.out.println("騷瑞,你無權(quán)訪問該資源");
}
//訪問資源B
Method meB = c1.getDeclaredMethod("rsB",null);
MyPreVer meBPre = meB.getDeclaredAnnotation(MyPreVer.class);
if(meBPre.value().equals(RoleNeeded)) {//模擬攔截器
meB.setAccessible(true);
meB.invoke(c1.newInstance());//模擬訪問資源
}else {
System.out.println("騷瑞,你無權(quán)訪問該資源");
}
}
}
結(jié)果:
騷瑞,你無權(quán)訪問該資源 資源B
尾語
注解 是一種 標(biāo)記、標(biāo)簽 來修飾代碼, 但它不是代碼本身的一部分,即 注解本身對(duì)代碼邏輯沒有任何影響 ,如何使用注解完全取決于我們開發(fā)者用Java反射來讀取和使用。
我們發(fā)現(xiàn)反射真的很強(qiáng)大,不僅可以讀取類的屬性、方法、構(gòu)造器等信息,還可以讀取類的注解相關(guān)信息,以后還會(huì)經(jīng)常遇到它。
注解一般用于
- 編譯器可以利用注解來探測(cè)錯(cuò)誤和檢查信息,像
@override
檢查是否重寫 - 適合工具類型的軟件用的,避免繁瑣的代碼,生成代碼配置,比如jpa自動(dòng)生成sql,日志注解,權(quán)限控制
- 程序運(yùn)行時(shí)的處理:某些注解可以在程序運(yùn)行的時(shí)候接受代碼的讀取,比如我們可以自定義注解
平時(shí)我們只知道如何使用注解,卻不知道其是如何起作用的,理所當(dāng)然的往往是我們所忽視的。
參考資料:
《Java核心技術(shù) 卷一》
https://blog.csdn.net/qq_20009015/article/details/106038023
https://zhuanlan.zhihu.com/p/258429599
-
JAVA
+關(guān)注
關(guān)注
19文章
2958瀏覽量
104544 -
spring
+關(guān)注
關(guān)注
0文章
338瀏覽量
14308 -
注解
+關(guān)注
關(guān)注
0文章
18瀏覽量
2668
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論