0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Java注解及其底層原理解析2

jf_78858299 ? 來源:小牛呼嚕嚕 ? 作者:小牛呼嚕嚕 ? 2023-02-09 14:18 ? 次閱讀

最后我們寫一個測試類

public class TestAn {
    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {

        //獲取Person class 實例
        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時,一般會先去校驗這個注解存不存在:

if(c1.isAnnotationPresent(MyAnnotation.class)) {
    //存在 MyAnnotation 注解
}else {
    //不存在 MyAnnotation 注解
}

我們發(fā)現(xiàn)反射真的很強大,不僅可以讀取類的屬性、方法、構(gòu)造器等信息,還可以讀取類的注解相關(guān)信息。

那反射是如何實現(xiàn)工作的?

我們來看下源碼:

c1.getAnnotation(MyAnnotation.class);通過idea點進去查看源碼,把重點的給貼出來,其他的就省略了

Map<Classextends Annotation>, Annotation> declaredAnnotations =
            AnnotationParser.parseAnnotations(getRawAnnotations(), getConstantPool(), this);

parseAnnotations()去 分析注解 ,其第一個參數(shù)是 獲取原始注解,第二個參數(shù)是獲取常量池內(nèi)容

public static Annotation annotationForMap(final Classextends 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)建動態(tài)代理,此處var0參數(shù)是由常量池獲取的數(shù)據(jù)轉(zhuǎn)換而來。

我們監(jiān)聽此處的var0:

圖片

image-20220616225518195

可以推斷出注解相關(guān)的信息 是存放在常量池中的

我們來總結(jié)一下,反射調(diào)用getAnnotations(MyAnnotation.class)方法的背后主要操作:

解析注解parseAnnotations()的時候 從該注解類的常量池中取出注解相關(guān)的信息,將其轉(zhuǎn)換格式后,通過newProxyInstance(注解的類加載器,注解的class實例 ,AnotationInvocationHandler實例)來創(chuàng)建代理對象,作為參數(shù)傳進去,最后返回一個代理實例。

其中AnotationInvocationHandler類是一個典型的動態(tài)代理類, 這邊先挖個坑,暫不展開,不然這篇文章是寫不完了

關(guān)于動態(tài)代理類我們只需先知道: 對象的執(zhí)行方法,交給代理來負責

class AnnotationInvocationHandler implements InvocationHandler, Serializable {
    ...
    private final Map<String, Object> memberValues;//存放該注解所有屬性的值
    private transient volatile Method[] memberMethods = null;

    AnnotationInvocationHandler(Classextends Annotation> var1, Map<String, Object> var2) {
    ...
    }

    public Object invoke(Object var1, Method var2, Object[] var3) {
    ...
     //調(diào)用委托類對象的方法,具體等等一些操作
    }
    ...
}

反射調(diào)用getAnnotations(MyAnnotation.class),返回一個代理實例,我們可以通過這個實例來操作該注解

示例:注解 模擬訪問權(quán)限控制

當我們引入springsecurity來做安全框架,然后只需添加@PreAuthorize**(**"hasRole('Admin')"**)**注解,就能實現(xiàn)權(quán)限的控制,簡簡單單地一行代碼,就優(yōu)雅地實現(xiàn)了權(quán)限控制,覺不覺得很神奇?讓我們一起模擬一個出來吧

@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實例
        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

尾語

注解 是一種 標記、標簽 來修飾代碼, 但它不是代碼本身的一部分,即 注解本身對代碼邏輯沒有任何影響 ,如何使用注解完全取決于我們開發(fā)者Java反射來讀取和使用。

我們發(fā)現(xiàn)反射真的很強大,不僅可以讀取類的屬性、方法、構(gòu)造器等信息,還可以讀取類的注解相關(guān)信息,以后還會經(jīng)常遇到它。

注解一般用于

  • 編譯器可以利用注解來探測錯誤和檢查信息,像@override檢查是否重寫
  • 適合工具類型的軟件用的,避免繁瑣的代碼,生成代碼配置,比如jpa自動生成sql,日志注解,權(quán)限控制
  • 程序運行時的處理:某些注解可以在程序運行的時候接受代碼的讀取,比如我們可以自定義注解

平時我們只知道如何使用注解,卻不知道其是如何起作用的,理所當然的往往是我們所忽視的。

參考資料

《Java核心技術(shù) 卷一》

https://blog.csdn.net/qq_20009015/article/details/106038023

https://zhuanlan.zhihu.com/p/258429599

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2943

    瀏覽量

    104096
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    335

    瀏覽量

    14259
  • 注解
    +關(guān)注

    關(guān)注

    0

    文章

    18

    瀏覽量

    2661
收藏 人收藏

    評論

    相關(guān)推薦

    如何通過注解來優(yōu)化我們的Java代碼

    Java注解可以說是我們編碼過程中最常用的。本篇文章將給大家介紹Java注解的概念、作用以及如何使用注解來提升代碼的可讀性和靈活性,并介紹如
    的頭像 發(fā)表于 09-30 11:39 ?509次閱讀

    java經(jīng)典面試題深度解析

    免費視頻教程:java經(jīng)典面試題深度解析對于很多初學(xué)者來說,學(xué)好java在后期面試的階段都沒什么經(jīng)驗,為了讓大家更好的了解面試相關(guān)知識,今天在這里給大家分享了一個java經(jīng)典面試題深度
    發(fā)表于 06-20 15:16

    詳細介紹了Java泛型、注解、并發(fā)編程

    介紹了Java泛型、注解、并發(fā)編程、數(shù)據(jù)傳輸與序列化、高效IO、容器集合、反射與類加載以及JVM重點知識線程、內(nèi)存模型、JVM運行時內(nèi)存、垃圾回收與算法、Java中四種引用類型、GC 分代收集算法
    發(fā)表于 08-20 06:09

    HarmonyOS注解的使用方法分享

    定義我們的注解自定義注解1、聲明注解功能:檢測類中是否有規(guī)范的get方法新建java libray的module,命名為annotation,創(chuàng)建
    發(fā)表于 03-28 14:04

    微信ANR原理解析與解決方案

    理解析 CSDN 博客專家@三精-大精wing 對于導(dǎo)致微信 ANR 的根本原因進行了解析(以下為授權(quán)發(fā)布): 本文目的在于學(xué)習(xí)研究Android技術(shù),若有侵犯,聯(lián)系作者將及時刪除。 首先,微信
    發(fā)表于 09-26 17:09 ?0次下載
    微信ANR原<b class='flag-5'>理解析</b>與解決方案

    分析java注解基本概念

    什么是注解(Annotation): Annotation(注解)就是Java提供了一種元程序中的元素關(guān)聯(lián)任何信息和著任何元數(shù)據(jù)(metadata)的途徑和方法。Annotion(注解
    發(fā)表于 09-27 14:53 ?0次下載

    Java底層實現(xiàn),CPU還有10個術(shù)語!

    Java底層實現(xiàn)——CPU的10個術(shù)語
    的頭像 發(fā)表于 03-28 14:14 ?6113次閱讀

    注解定義Bean及開發(fā)

    注解本質(zhì)是一個繼承了Annotation 的特殊接口,其具體實現(xiàn)類是Java 運行時生成的動態(tài)代理類。
    發(fā)表于 08-02 10:26 ?402次閱讀

    Java注解及其底層理解析 1

    什么是注解? 當我們開發(fā)SpringBoot項目,我們只需對啟動類加上`@SpringBootApplication`,就能自動裝配,不需要編寫冗余的xml配置。當我們?yōu)轫椖刻砑觢ombok
    的頭像 發(fā)表于 02-09 14:18 ?656次閱讀
    <b class='flag-5'>Java</b><b class='flag-5'>注解</b><b class='flag-5'>及其</b><b class='flag-5'>底層</b>原<b class='flag-5'>理解析</b> 1

    容器配置及Spring Boot注解

    Autowired注解用于標記Spring將要解析和注入的依賴項。此注解可以作用在構(gòu)造函數(shù)、字段和setter方法上。
    的頭像 發(fā)表于 04-07 11:45 ?500次閱讀
    容器配置及Spring Boot<b class='flag-5'>注解</b>

    SpringBoot的核心注解2

    今天跟大家來探討下SpringBoot的核心注解@SpringBootApplication以及run方法,理解下springBoot為什么不需要XML,達到零配置
    的頭像 發(fā)表于 04-07 14:34 ?1870次閱讀
    SpringBoot的核心<b class='flag-5'>注解</b><b class='flag-5'>2</b>

    JAVA注解是怎么做到的(上)

    。它可以聲明在包、類、字段、方法、局部變量、方法參數(shù)等的前面,用來對這些元素進行說明,注釋。那么你知道JDK什么是元注解嗎?注解有哪些分類嗎?以及注解Java中最本質(zhì)究竟是什么東西,
    的頭像 發(fā)表于 05-11 10:57 ?573次閱讀

    JAVA注解是怎么做到的(下)

    。它可以聲明在包、類、字段、方法、局部變量、方法參數(shù)等的前面,用來對這些元素進行說明,注釋。那么你知道JDK什么是元注解嗎?注解有哪些分類嗎?以及注解Java中最本質(zhì)究竟是什么東西,
    的頭像 發(fā)表于 05-11 10:57 ?482次閱讀
    <b class='flag-5'>JAVA</b>中<b class='flag-5'>注解</b>是怎么做到的(下)

    3分鐘純Java注解搭個管理系統(tǒng)

    Erupt一個通用后臺管理框架,據(jù)說有 超低代碼量 、 零前端代碼 、零 CURD操作 、無需建表 ,純Java注解開發(fā) 等特色,號稱三分鐘就可以搭建一個完整的后臺管理系統(tǒng)。
    的頭像 發(fā)表于 07-28 11:27 ?905次閱讀
    3分鐘純<b class='flag-5'>Java</b><b class='flag-5'>注解</b>搭個管理系統(tǒng)

    springmvc常用5種注解

    SpringMVC是一種基于Java的Web框架,使用注解可以更加方便靈活地開發(fā)和管理控制器,實現(xiàn)請求的映射和處理。在SpringMVC中,有許多常用的注解,本文將詳細介紹其中的五種注解
    的頭像 發(fā)表于 11-22 16:51 ?647次閱讀