国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

[Java] 通過反射,動(dòng)態(tài)修改注解的某個(gè)屬性值

Kyxy / 3478人閱讀

摘要:今天對(duì)此嘗試了一番,發(fā)現(xiàn)通過反射來動(dòng)態(tài)修改注解的屬性值是可以做到的眾所周知,這個(gè)包下面都是的反射類和工具。一個(gè)注解通過指定其生命周期,本文所討論的動(dòng)態(tài)修改注解屬性值,建立在這種情況。

昨晚看到一條問題,大意是樓主希望可以動(dòng)態(tài)得建立多個(gè)Spring 的定時(shí)任務(wù)。

這個(gè)題目我并不是很熟悉,不過根據(jù)題目描述和查閱相關(guān) Spring 創(chuàng)建定時(shí)任務(wù) 的資料,發(fā)現(xiàn)這也許涉及到通過Java代碼動(dòng)態(tài)修改注解的屬性值。

今天對(duì)此嘗試了一番,發(fā)現(xiàn)通過反射來動(dòng)態(tài)修改注解的屬性值是可以做到的:

眾所周知,java/lang/reflect 這個(gè)包下面都是Java的反射類和工具。

Annotation 注解,也是位于這個(gè)包里的。注解自從Java 5.0版本引入后,就成為了Java平臺(tái)中非常重要的一部分,常見的如 @Override@Deprecated

關(guān)于注解更詳細(xì)的信息和使用方法,網(wǎng)上已經(jīng)有很多資料,這里就不再贅述了。

一個(gè)注解通過 @Retention 指定其生命周期,本文所討論的動(dòng)態(tài)修改注解屬性值,建立在 @Retention(RetentionPolicy.RUNTIM) 這種情況。畢竟這種注解才能在運(yùn)行時(shí)(runtime)通過反射機(jī)制進(jìn)行操作。

那么現(xiàn)在我們定義一個(gè) @Foo 注解,它有一個(gè)類型為 Stringvalue 屬性,該注解應(yīng)用再Field上:

/**
 * Created by krun on 2017/9/18.
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Foo {
    String value();
}

再定義一個(gè)普通的Java對(duì)象 Bar,它有一個(gè)私有的String屬性 val,并為它設(shè)置屬性值為"fff"@Foo 注解:

public class Bar {

    @Foo ("fff")
    private String val;
}

接下來在 main 方法中我們來嘗試修改 Bar.val 上的 @Foo注解的屬性值為 "ffffd"

先是正常的獲取注解屬性值:

/**
 * Created by krun on 2017/9/18.
 */
public class Main {
    public static void main(String ...args) throws NoSuchFieldException {
        //獲取Bar實(shí)例
        Bar bar = new Bar();
        //獲取Bar的val字段
        Field field = Bar.class.getDeclaredField("val");
        //獲取val字段上的Foo注解實(shí)例
        Foo foo = field.getAnnotation(Foo.class);
        //獲取Foo注解實(shí)例的 value 屬性值
        String value = foo.value();
        //打印該值
        System.out.println(value); // fff
    }
}

首先,我們要知道注解的值是存在哪里的。

String value = foo.value(); 處下斷點(diǎn),我們跑一下可以發(fā)現(xiàn):

當(dāng)前棧中有這么幾個(gè)變量,不過其中有一點(diǎn)很特別:foo,其實(shí)是個(gè)Proxy實(shí)例。

Proxy也是 java/lang/reflect下的東西,它的作用是為一個(gè)Java類生成一個(gè)代理,就像這樣:

public interface A {
    String func1();
}

public class B implements A {
    
    @Override
    public String func1() { //do something ... }
    
    public String func2() { //do something ... };
}

public static void main(String ...args) {
    B bInstance = new B();
    
    B bProxy = Proxy.newProxyInstance(
        B.class.getClassLoader(),    // B 類的類加載器
        B.class.getInterfaces(), // B 類所實(shí)現(xiàn)的接口,如果你想攔截B類的某個(gè)方法,必須讓這個(gè)方法在某個(gè)接口中聲明并讓B類實(shí)現(xiàn)該接口
        new InvocationHandler() { // 調(diào)用處理器,任何對(duì) B類所實(shí)現(xiàn)的接口方法的調(diào)用都會(huì)觸發(fā)此處理器
            @Override
            public Object invoke (Object proxy, // 這個(gè)是代理的實(shí)例,method.invoke時(shí)不能使用這個(gè),否則會(huì)死循環(huán)
                                  Method method, // 觸發(fā)的接口方法
                                  Object[] args // 此次調(diào)用該方法的參數(shù)
                                  ) throws Throwable {
                System.out.println(String.format("調(diào)用 %s 之前", method.getName()));
                /**
                 * 這里必須使用B類的某個(gè)具體實(shí)現(xiàn)類的實(shí)例,因?yàn)橛|發(fā)時(shí)這里的method只是一個(gè)接口方法的引用,
                 * 也就是說它是空的,你需要為它指定具有邏輯的上下文(bInstance)。
                 */
                Object obj = method.invoke(bInstance, args); 
                System.out.println(String.format("調(diào)用 %s 之后", method.getName()));
                return obj; //返回調(diào)用結(jié)果
            }
        }
    );
}

這樣你就可以攔截這個(gè)Java類的某個(gè)方法調(diào)用,但是你只能攔截到 func1的調(diào)用,想想為什么?

那么注意了:

ClassLoader 這是個(gè)class就會(huì)有,注解也不例外。那么注解和interfaces有什么關(guān)系?

注解本質(zhì)上就是一個(gè)接口,它的實(shí)質(zhì)定義為: interface SomeAnnotation extends Annotation
這個(gè) Annotation 接口位于 java/lang/annotation 包,它的注釋中第一句話就是 The common interface extended by all annotation types.

如此說來,Foo 注解本身只是個(gè)接口,這就意味著它沒有任何代碼邏輯,那么它的 value 屬性究竟是存在哪里的呢?

展開 foo 可以發(fā)現(xiàn):

這個(gè) Proxy 實(shí)例持有一個(gè) AnnotationInvocationHandler,還記得之前提到過如何創(chuàng)建一個(gè) Proxy 實(shí)例么? 第三個(gè)參數(shù)就是一個(gè) InvocationHandler
看名字這個(gè)handler即是Annotation所特有的,我們看一下它的代碼:

class AnnotationInvocationHandler implements InvocationHandler, Serializable {

    private final Class type;
    private final Map memberValues;
    private transient volatile Method[] memberMethods = null;
    
    /* 后續(xù)無關(guān)代碼就省略了,想看的話可以查看 sun/reflect/annotation/AnnotationInvocationHandler */
   
}

我們一眼就可以看到一個(gè)有意思的名字: memberValues,這是一個(gè)Map,而斷點(diǎn)中可以看到這是一個(gè) LinknedHashMapkey為注解的屬性名稱,value即為注解的屬性值。

現(xiàn)在我們找到了注解的屬性值存在哪里了,那么接下來的事就好辦了:

/**
 * Created by krun on 2017/9/18.
 */
public class Main {
    public static void main(String ...args) throws NoSuchFieldException, IllegalAccessException {
        //獲取Bar實(shí)例
        Bar bar = new Bar();
        //獲取Bar的val字段
        Field field = Bar.class.getDeclaredField("val");
        //獲取val字段上的Foo注解實(shí)例
        Foo foo = field.getAnnotation(Foo.class);
        //獲取 foo 這個(gè)代理實(shí)例所持有的 InvocationHandler
        InvocationHandler h = Proxy.getInvocationHandler(foo);
        // 獲取 AnnotationInvocationHandler 的 memberValues 字段
        Field hField = h.getClass().getDeclaredField("memberValues");
        // 因?yàn)檫@個(gè)字段事 private final 修飾,所以要打開權(quán)限
        hField.setAccessible(true);
        // 獲取 memberValues
        Map memberValues = (Map) hField.get(h);
        // 修改 value 屬性值
        memberValues.put("value", "ffffd");
        // 獲取 foo 的 value 屬性值
        String value = foo.value();
        System.out.println(value); // ffffd
    }
}

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/70449.html

相關(guān)文章

  • 第12章 元編程與注解反射 《Kotlin 項(xiàng)目實(shí)戰(zhàn)開發(fā)》

    摘要:第章元編程與注解反射反射是在運(yùn)行時(shí)獲取類的函數(shù)方法屬性父類接口注解元數(shù)據(jù)泛型信息等類的內(nèi)部信息的機(jī)制。本章介紹中的注解與反射編程的相關(guān)內(nèi)容。元編程本質(zhì)上是一種對(duì)源代碼本身進(jìn)行高層次抽象的編碼技術(shù)。反射是促進(jìn)元編程的一種很有價(jià)值的語言特性。 第12章 元編程與注解、反射 反射(Reflection)是在運(yùn)行時(shí)獲取類的函數(shù)(方法)、屬性、父類、接口、注解元數(shù)據(jù)、泛型信息等類的內(nèi)部信息的機(jī)...

    joyqi 評(píng)論0 收藏0
  • Spring入門IOC和AOP學(xué)習(xí)筆記

    摘要:入門和學(xué)習(xí)筆記概述框架的核心有兩個(gè)容器作為超級(jí)大工廠,負(fù)責(zé)管理創(chuàng)建所有的對(duì)象,這些對(duì)象被稱為。中的一些術(shù)語切面切面組織多個(gè),放在切面中定義。 Spring入門IOC和AOP學(xué)習(xí)筆記 概述 Spring框架的核心有兩個(gè): Spring容器作為超級(jí)大工廠,負(fù)責(zé)管理、創(chuàng)建所有的Java對(duì)象,這些Java對(duì)象被稱為Bean。 Spring容器管理容器中Bean之間的依賴關(guān)系,使用一種叫做依賴...

    wenyiweb 評(píng)論0 收藏0
  • 反射(Reflection)

    摘要:裝載類的裝載是通過類加載器完成的,加載器將文件的字節(jié)碼文件裝入的方法區(qū),并且在堆區(qū)創(chuàng)建描述這個(gè)類的對(duì)象。通過指定的對(duì)象來實(shí)例化對(duì)象取得父指定的構(gòu)造類型給傳入?yún)?shù)賦初值實(shí)例化反射操作獲得某個(gè)類的所有的字段,包括父類。 什么是反射 反射就是在運(yùn)行時(shí)把 Java 類中的各種成分映射成相應(yīng)的 Java 類(Method、Annotation等),可以動(dòng)態(tài)得獲取所有的屬性以及動(dòng)態(tài)調(diào)用任意一個(gè)方法...

    LMou 評(píng)論0 收藏0
  • 造個(gè)輪子,我學(xué)到了什么

    摘要:閱讀原文造個(gè)輪子我學(xué)到了什么聽說的最多的是不是不要重復(fù)的造輪子不要被這句話蒙騙了,這句話應(yīng)該還沒說完整,在什么情況下不要造輪子實(shí)際項(xiàng)目中由于工期和質(zhì)量原因,肯定不希望你造輪子,你造輪子花費(fèi)時(shí)間且質(zhì)量不如現(xiàn)有的輪子。 閱讀原文:造個(gè)輪子,我學(xué)到了什么 聽說的最多的是不是不要重復(fù)的造輪子?不要被這句話蒙騙了,這句話應(yīng)該還沒說完整,在什么情況下不要造輪子?實(shí)際項(xiàng)目中由于工期和質(zhì)量原因,肯定不...

    Acceml 評(píng)論0 收藏0
  • Java基礎(chǔ)知識(shí)整理之注解

    摘要:注解提供了一種安全的類似注釋的機(jī)制,用來將任何的信息或元數(shù)據(jù)與程序元素類方法成員變量等進(jìn)行關(guān)聯(lián)。為程序的元素類方法成員變量加上更直觀更明了的說明,這些說明與程序的業(yè)務(wù)邏輯無關(guān),并且提供給指定的工具或框架使用。 什么是注解? Annotation 是 Java5 之后開始引入的新特性,中文為注解。注解提供了一種安全的類似注釋的機(jī)制,用來將任何的信息或元數(shù)據(jù)(metadata)與程序元素(...

    blastz 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<