欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文搞懂Spring中的注解與反射

 更新時間:2022年06月08日 10:53:28   作者:Apluemxa  
這篇文章主要為大家介紹了Spring中的注解與反射的原理與實(shí)現(xiàn),文中的示例代碼講解詳細(xì),對我們了解Spring有一定的幫助,需要的可以參考一下

前言

注解(Annotation)不是程序,但可以對程序作出解釋,也可以被其它程序(如編譯器)讀取。

注解的格式:以@注釋名在代碼中存在,還可以添加一些參數(shù)值例如@SuppressWarnings(value="unchecked")。

注解可在package、class、method、field等上面使用,作用是為它們添加了額外的輔助信息,從而可以通過反射機(jī)制實(shí)現(xiàn)對這些元數(shù)據(jù)的訪問。

一、內(nèi)置(常用)注解

1.1@Overrode

表示某方法旨在覆蓋超類中的方法聲明,該方法將覆蓋或?qū)崿F(xiàn)在超類中聲明的方法。

1.2@RequestMapping

@RequestMapping注解的主要用途是將Web請求與請求處理類中的方法進(jìn)行映射,注意有以下幾個屬性:

  • value:映射的請求URL或者其別名
  • value:映射的請求URL或者其別名
  • params:根據(jù)HTTP參數(shù)的存在、缺省或值對請求進(jìn)行過濾

1.3@RequestBody

@RequestBody在處理請求方法的參數(shù)列表中使用,它可以將請求主體中的參數(shù)綁定到一個對象中,請求主體參數(shù)是通過HttpMessageConverter傳遞的,根據(jù)請求主體中的參數(shù)名與對象的屬性名進(jìn)行匹配并綁定值。此外,還可以通過@Valid注解對請求主體中的參數(shù)進(jìn)行校驗(yàn)。

1.4@GetMapping

@GetMapping注解用于處理HTTP GET請求,并將請求映射到具體的處理方法中。具體來說,@GetMapping是一個組合注解,它相當(dāng)于是@RequestMapping(method=RequestMethod.GET)的快捷方式。

1.5@PathVariable

@PathVariable注解是將方法中的參數(shù)綁定到請求URI中的模板變量上??梢酝ㄟ^@RequestMapping注解來指定URI的模板變量,然后使用@PathVariable注解將方法中的參數(shù)綁定到模板變量上。

1.6@RequestParam

@RequestParam注解用于將方法的參數(shù)與Web請求的傳遞的參數(shù)進(jìn)行綁定。使用@RequestParam可以輕松的訪問HTTP請求參數(shù)的值。

1.7@ComponentScan

@ComponentScan注解用于配置Spring需要掃描的被組件注解注釋的類所在的包??梢酝ㄟ^配置其basePackages屬性或者value屬性來配置需要掃描的包路徑。value屬性是basePackages的別名。

1.8@Component

@Component注解用于標(biāo)注一個普通的組件類,它沒有明確的業(yè)務(wù)范圍,只是通知Spring被此注解的類需要被納入到Spring Bean容器中并進(jìn)行管理。

1.9@Service

@Service注解是@Component的一個延伸(特例),它用于標(biāo)注業(yè)務(wù)邏輯類。與@Component注解一樣,被此注解標(biāo)注的類,會自動被Spring所管理。

1.10@Repository

@Repository注解也是@Component注解的延伸,與@Component注解一樣,被此注解標(biāo)注的類會被Spring自動管理起來,@Repository注解用于標(biāo)注DAO層的數(shù)據(jù)持久化類。

二、元注解

4個元個元注解分別是:@Target、@Retention、@Documented、@Inherited 。

再次強(qiáng)調(diào)下元注解是java API提供,是專門用來定義注解的注解。

@Target

描述注解能夠作用的位置,ElementType取值:

  • ElementType.TYPE,可以作用于類上
  • ElementType.METHOD,可以作用于方法上
  • ElementType.FIELD,可以作用在成員變量上

@Retention

表示需要在什么級別保存該注釋信息(生命周期):

RetentionPolicy.RUNTIME:內(nèi)存中的字節(jié)碼,VM將在運(yùn)行時也保留注解,因此可以通過反射機(jī)制讀取注解的信息

@Documented

描述注解是否被抽取到api文檔中。

@Inherited

描述注解是否被子類繼承。

三、自定義注解

學(xué)習(xí)自定義注解對于理解Spring框架十分有好處,即使在實(shí)際項(xiàng)目中可能不需要使用自定義注解,但可以幫助我們掌握Spring的一些底層原理,從而提高對整體項(xiàng)目的把握。

/**
 * 自定義注解
 * @author Created by zhuzqc on 2022/5/31 23:03
 */
public class CustomAnnotation {

    /**
     * 注解中可以為參數(shù)賦值,如果沒有默認(rèn)值,那么必須為注解的參數(shù)賦值
     * */
    @MyAnnotation(value = "解釋")
    public void test(){
    }
}
/**
 * @author zhuzqc
 */
//自定義注解必須的元注解target,指明注解的作用域(此處指明的是在類和方法上起作用)
@Target({ElementType.TYPE,ElementType.METHOD})
//元注解retention聲明該注解在何時起作用(此處指明的是在運(yùn)行時起作用)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{

    //注解中需聲明參數(shù),格式為:參數(shù)類型 + 參數(shù)名();
    String value() default "";

}

四、反射機(jī)制概述

4.1動態(tài)語言與靜態(tài)語言

4.1.1動態(tài)語言

是一種在運(yùn)行時可以改變其結(jié)構(gòu)的語言,例如新的函數(shù)、對象甚至代碼可以被引進(jìn),已有的函數(shù)可以被刪除或是進(jìn)行其它結(jié)構(gòu)上的變化。

主要的動態(tài)語言有:Object-C、C#、PHP、Python、JavaScript 等。

以 JavaScript 語言舉例:

/**
 * 由于未指定var的具體類型,函數(shù)在運(yùn)行時間可以改變var的類型
 * */
function f(){
    var x = "var a = 3; var b = 5; alert(a+b)";
    eval(x)
}

4.2.2靜態(tài)語言

與動態(tài)語言相對的、運(yùn)行時結(jié)構(gòu)不可變的語言就是靜態(tài)語言,如 Java、C、C++ 等。

Java 不是動態(tài)語言,但 Java 可以稱為”準(zhǔn)動態(tài)語言“。即 Java 有一定的動態(tài)性,可以利用反射機(jī)制獲得類似于動態(tài)語言的特性,從而使得 Java 語言在編程時更加靈活。

4.2Java Reflection(Java 反射)

Reflection(反射)是 Java 被視為準(zhǔn)動態(tài)語言的關(guān)鍵:反射機(jī)制允許程序在執(zhí)行期間借助 Reflection API 獲取任何類的內(nèi)部信息,并能直接操作任意對象的內(nèi)部屬性及方法。

Class c = Class.forName("java.lang.String")

加載完類后,在堆內(nèi)存的方法區(qū)就產(chǎn)生了一個Class類型的對象(一個類只有一個Class對象),這個類就包含了完整的類的結(jié)構(gòu)信息。我沒可以通過這個對象,像鏡子一樣看到類的結(jié)構(gòu),這個過程形象地被稱之為反射。

通過代碼更易于理解:

/**
 * 反射的概念
 * @author Created by zhuzqc on 2022/6/1 17:40
 */
public class ReflectionTest extends Object{
    public static void main(String[] args) throws ClassNotFoundException {
        //通過反射獲取類的Class對象
        Class c = Class.forName("com.dcone.zhuzqc.demo.User");
        //一個類在內(nèi)存中只有唯一個Class對象
        System.out.println(c.hashCode());

    }
}

/**
 * 定義一個實(shí)體類entity
 * */
@Data
class User{
    private String userName;
    private Long userId;
    private Date loginTime;
}

由于該類繼承 Object,在 Object 類中有 getClass() 方法,該方法被所有子類繼承:

@HotSpotIntrinsicCandidate
public final native Class<?> getClass();

注:該方法的返回值類型是一個 Class 類,該類是 Java 反射的源頭。

反射的優(yōu)點(diǎn)運(yùn)行期類型的判斷、動態(tài)加載類、提高代碼靈活度。

4.2.1反射機(jī)制主要功能

  • 在運(yùn)行時判斷、調(diào)用任意一個類的對象信息(成員變量和方法等);
  • 在運(yùn)行時獲取泛型信息;
  • 在運(yùn)行時處理注解;
  • 生成動態(tài)代理。

4.2.2主要API

  • java.lang.Class:代表一個類
  • java.lang.reflect.Field:代表類的成員變量
  • java.lang.reflect.Method:代表類的方法
  • java.lang.reflect.Constructor:代表類的構(gòu)造器

五、理解Class類并獲取Class實(shí)例

5.1Class類

前面提到,反射后可以得到某個類的屬性、方法和構(gòu)造器、實(shí)現(xiàn)的接口。

  • 對于每個類而言,JRE都為其保留一個不變的 Class 類型的對象;
  • 一個加載的類在 JVM 中只會有一個 Class 實(shí)例;
  • Class 類是Reflection的根源,想要通過反射獲得任何動態(tài)加載的、運(yùn)行的類,都必須先獲取相應(yīng)的 Class 對象。

5.2獲取Class類實(shí)例

有以下5種方式可以獲取Class類的實(shí)例:

1.若已知具體的類,可以通過類的class屬性獲取,該fang'shi最為安全可靠,且程序性能最高。

//類的class屬性
Class classOne = User.class;

2. 已知某個類的實(shí)例,通過調(diào)用該實(shí)例的getClass方法獲取Class對象。 

   //已有類對象的getClass方法
   Class collatz = user.getClass();

3.已知一個類的全類名,且該類在類路徑下,可以通過靜態(tài)方法forName()獲取。

Class c = Class.forName("com.dcone.zhuzqc.demo.User");

4.內(nèi)置基本數(shù)據(jù)類型可以直接使用類名.Type獲取。

//內(nèi)置對象才有的TYPE屬性,較大的局限性
Class<Integer> type = Integer.TYPE;

5.利用ClassLoader(類加載器)獲取。

5.3可獲得Class對象的類型

1.class:外部類、成員(成員內(nèi)部類,靜態(tài)內(nèi)部類),局部內(nèi)部類,匿名內(nèi)部類;

//類可以反射
    Class c1 = Person.class;

2.interface:所有接口;

//接口可以反射
     Class c2 = Comparable.class;

3.[]:數(shù)組;

//數(shù)組可以反射
     Class c3 = String[].class;
     Class c4 = int[][].class;

4.enum:枚舉;

//枚舉可以反射
     Class c6 = ElementType.class;

5.annotation:注解(@interface);

//注解可以反射
     Class c5 = Data.class;

6.基本數(shù)據(jù)類型;

//基本數(shù)據(jù)類型(包裝類)可以反射
     Class c7 = int.class;
     Class c8 = Integer.class;

7.void。

//void可以反射
     Class c9 = void.class;

六、類的加載與ClassLoader

6.1類的加載過程

當(dāng)程序主動使用某個類時,如果該類還未被加載到內(nèi)存中,則系統(tǒng)會通過如下3個步驟來對該類進(jìn)行初始化。

1.類的加載(Load):將類的 class 文件字節(jié)碼內(nèi)容讀入內(nèi)存,并將這些靜態(tài)數(shù)據(jù)轉(zhuǎn)換成方法區(qū)運(yùn)行時的數(shù)據(jù)結(jié)構(gòu),同時創(chuàng)建一個java.lang.Class對象,此過程由類加載器完成;

2.類的鏈接(Link):將類的二進(jìn)制數(shù)據(jù)合并到 JRE 中,確保加載的類信息符合 JVM 規(guī)范,同時 JVM 將常量池內(nèi)的引用替換為地址。

3.類的初始化(Initialize):JVM 負(fù)責(zé)對類進(jìn)行初始化,分為類的主動引用和被動引用。

類的主動引用

  • 虛擬器啟動時,先初始化main方法所在的類;
  • new 類的對象;
  • 調(diào)用類的靜態(tài)(static)成員和靜態(tài)(static)方法;
  • 使用java.lang.reflect包的方法對類進(jìn)行反射調(diào)用;
  • 如果該類的父類沒有被初始化,則會先初始化它的父類。

類的被動引用

  • 當(dāng)訪問到一個靜態(tài)域時,只有真正聲明這個域的類才會被初始化;
  • 通過數(shù)組定義類的引用,不會觸發(fā)此類的初始化;
  • 引用常量不會觸發(fā)此類的初始化

6.2類加載器

JVM支持兩種類型的類加載器,分別為引導(dǎo)類加載器(BootstrapClassLoader)和自定義類加載器(User-Defined ClassLoader)。

從概念上來講,自定義類加載器一般指的是程序中由開發(fā)人員自定義的一類,類加載器。

但是Java虛擬機(jī)規(guī)范卻沒有這么定義,而是將所有派生于抽象類ClassLoader的類加載器都劃分為自定義類加載器。

無論類加載器的類型如何劃分,在程序中我們最常見的類加載器始終只有3個,具體如下圖所示:

類加載器

所以具體為引導(dǎo)類加載器(BootstrapClassLoader)和自定義類加載器(包括ExtensionClassLoader、Application ClassLoader(也叫System ClassLoader)、User Defined ClassLoader)。

public class Test03 {
    public static void main(String[] args) {
        //獲取系統(tǒng)類的加載器
        ClassLoader sysLoader = ClassLoader.getSystemClassLoader();
        System.out.println(sysLoader);

        //獲取系統(tǒng)類的父類加載器
        ClassLoader parent = sysLoader.getParent();
        System.out.println(parent);
    }
}

七、獲取運(yùn)行時類的完整對象

通過反射獲取運(yùn)行時類的完整結(jié)構(gòu):Field、Method、Constructor、Superless、Interface、Annotation等。

即:實(shí)現(xiàn)的全部接口、所繼承的父類、全部的構(gòu)造器、全部的方法、全部的成員變量(局部變量)、注解等。

/**
 * @author Created by zhuzqc on 2022/6/5 0:16
 */
public class Test04 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c1 = Class.forName("com.dcone.zhuzqc.demo.User");
        //獲取所有屬性
        Field field[];
        field = c1.getDeclaredFields();
        for (Field f:field){
            System.out.println(f);
        }
        //獲得類的方法
        Method method[];
        method = c1.getDeclaredMethods();
        for (Method m:method){
            System.out.println(m);
        }
    }
}

八、反射獲取泛型信息

Java 中采用泛型擦除的機(jī)制來引入泛型,Java 中的泛型僅僅是給編譯器 javac 使用的,目的是確保數(shù)據(jù)的安全性以及免去強(qiáng)制類型轉(zhuǎn)換的問題。一旦編譯完成,所有和泛型相關(guān)的類型全部擦除。

在Java中可以通過反射獲取泛型信息的場景有如下三個:

  • (1)成員變量的泛型
  • (2)方法參數(shù)的泛型
  • (3)方法返回值的泛型

在Java中不可以通過反射獲取泛型信息的場景有如下兩個:

  • (1)類或接口聲明的泛型
  • (2)局部變量的泛型

要獲取泛型信息,必須要注意ParameterizedType類,該類中的getActualTypeArguments()方法可以有效獲取泛型信息。

下面以獲取成員方法參數(shù)的泛型類型信息為例:

public class Demo {
    public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException {
 
        // 獲取成員方法參數(shù)的泛型類型信息
        getMethodParametricGeneric();
    }
 /**
     * 獲取方法參數(shù)的泛型類型信息
     *
     * @throws NoSuchMethodException
     */
    public static void getMethodParametricGeneric() throws NoSuchMethodException {
        // 獲取MyTestClass類中名為"setList"的方法
        Method setListMethod = MyClass.class.getMethod("setList", List.class);
        // 獲取該方法的參數(shù)類型信息(帶有泛型)
        Type[] genericParameterTypes = setListMethod.getGenericParameterTypes();
        // 但我們實(shí)際上需要獲取返回值類型中的泛型信息,所以要進(jìn)一步判斷,即判斷獲取的返回值類型是否是參數(shù)化類型ParameterizedType
        for (Type genericParameterType : genericParameterTypes) {
            ParameterizedType parameterizedType = (ParameterizedType) genericParameterType;
            // 獲取成員方法參數(shù)的泛型類型信息
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                Class realType = (Class) actualTypeArgument;
                System.out.println("成員方法參數(shù)的泛型信息:" + realType);
            }
        }
    }

九、反射獲取注解信息

在開發(fā)中可能會遇到這樣的場景:獲取類的屬性釋義,這些釋義定義在類屬性的注解中。

/**
 * 定義一個實(shí)體類entity
 * */
@Data
class User{
    @ApiModelProperty(value = "姓名")
    private String userName;

    @ApiModelProperty(value = "用戶id")
    private Long userId;

    @ApiModelProperty(value = "登錄時間")
    private Date loginTime;
}

那么可以如何獲取注解中的屬性信息呢?

解決方案:

這里我們使用反射,以及java.lang下的兩個方法:

//如果指定類型的注釋存在于此元素上,  方法返回true 
java.lang.Package.isAnnotationPresent(Class<? extends Annotation> annotationClass) 
//如果是該類型的注釋, 方法返回該元素的該類型的注釋
java.lang.Package.getAnnotation(Class< A > annotationClass) 
    public static void main(String[] args) throws ClassNotFoundException {
        Class c1 = Class.forName("com.dcone.zhuzqc.demo.User");

        if(User.class.isAnnotationPresent(ApiModel.class)){
            System.out.println(User.class.getAnnotation(ApiModel.class).value());
        }
        // 獲取類變量注解
        Field[] fields = User.class.getDeclaredFields();
        for (Field f : fields) {
            if(f.isAnnotationPresent(ApiModelProperty.class)){
                System.out.print(f.getAnnotation(ApiModelProperty.class).name() + ",");
            }
        }
    }

拓展1:獲取方法上的注解

    @Bean("sqlSessionFactory")
    public String test(@RequestBody User user) throws ClassNotFoundException {
        Class c2 = Class.forName("com.dcone.zhuzqc.demo.User");
        // 獲取方法注解:
        Method[] methods = User.class.getDeclaredMethods();
        for(Method m : methods){
            if (m.isAnnotationPresent((Class<? extends Annotation>) User.class)) {
                System.out.println(m.getAnnotation(ApiModelProperty.class).annotationType());
            }
        }
        return "test";
    }

拓展2:獲取方法參數(shù)上的注解

    @Bean("sqlSessionFactory")
    public String test(@RequestBody User user) throws ClassNotFoundException {
        Class c2 = Class.forName("com.dcone.zhuzqc.demo.User");
        // 獲取方法參數(shù)注解
        Method[] methods2 = User.class.getDeclaredMethods();
        for (Method m : methods2) {
            // 獲取方法的所有參數(shù)
            Parameter[] parameters = m.getParameters();
            for (Parameter p : parameters) {
                // 判斷是否存在注解
                if (p.isAnnotationPresent(ApiModelProperty.class)) {
                    System.out.println(p.getAnnotation(ApiModelProperty.class).name());
                }
            }
        }
        return "test";
    }

以上就是一文搞懂Spring中的注解與反射的詳細(xì)內(nèi)容,更多關(guān)于Spring注解 反射的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • IDEA中創(chuàng)建maven項(xiàng)目webapp目錄無法識別即未被標(biāo)識的解決辦法

    IDEA中創(chuàng)建maven項(xiàng)目webapp目錄無法識別即未被標(biāo)識的解決辦法

    在學(xué)習(xí)SpringMVC課程中,基于IDEA新建maven項(xiàng)目模塊后,webapp目錄未被標(biāo)識,即沒有小藍(lán)點(diǎn)的圖標(biāo)顯示,所以本文給大家介紹了IDEA中創(chuàng)建maven項(xiàng)目webapp目錄無法識別即未被標(biāo)識的解決辦法,需要的朋友可以參考下
    2024-03-03
  • java 用redisTemplate 的 Operations存取list集合操作

    java 用redisTemplate 的 Operations存取list集合操作

    這篇文章主要介紹了java 用redisTemplate 的 Operations存取list集合操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java實(shí)現(xiàn)順序表的操作

    Java實(shí)現(xiàn)順序表的操作

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)順序表的基本操作,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • 一文搞懂Java項(xiàng)目中枚舉的定義與使用

    一文搞懂Java項(xiàng)目中枚舉的定義與使用

    枚舉就是用enum修飾是一種Java特殊的類,枚舉是class、底層是繼承了java.lang.Enum類的實(shí)體類。本文將詳解枚舉的定義與使用,需要的可以參考一下
    2022-06-06
  • Netty結(jié)合Protobuf進(jìn)行編解碼的方法

    Netty結(jié)合Protobuf進(jìn)行編解碼的方法

    這篇文章主要介紹了Netty結(jié)合Protobuf進(jìn)行編解碼,通過文檔表述和代碼實(shí)例充分說明了如何進(jìn)行使用和操作,需要的朋友可以參考下
    2021-06-06
  • jxl 導(dǎo)出數(shù)據(jù)到excel的實(shí)例講解

    jxl 導(dǎo)出數(shù)據(jù)到excel的實(shí)例講解

    下面小編就為大家分享一篇jxl 導(dǎo)出數(shù)據(jù)到excel的實(shí)例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-12-12
  • Springboot @Value使用代碼實(shí)例

    Springboot @Value使用代碼實(shí)例

    這篇文章主要介紹了Springboot @Value使用代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-11-11
  • 淺析NIO系列之TCP

    淺析NIO系列之TCP

    NIO即同步非阻塞式IO,它和傳統(tǒng)的BIO比較最大的區(qū)別在于在執(zhí)行accept、connect、read、write操作時是非阻塞的。很有利于實(shí)現(xiàn)用少量線程來處理多個客戶端請求,可以隨時讓線程切換所處理的客戶端,從而可以實(shí)現(xiàn)高并發(fā)服務(wù)器的開發(fā)
    2021-06-06
  • 配置java.library.path加載庫文件問題

    配置java.library.path加載庫文件問題

    這篇文章主要介紹了配置java.library.path加載庫文件問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • Java線程中的ThreadLocal類解讀

    Java線程中的ThreadLocal類解讀

    這篇文章主要介紹了Java線程中的ThreadLocal類解讀,ThreadLocal是一個泛型類,作用是實(shí)現(xiàn)線程隔離,ThreadLocal類型的變量,在每個線程中都會對應(yīng)一個具體對象,對象類型需要在聲明ThreadLocal變量時指定,需要的朋友可以參考下
    2023-11-11

最新評論