<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
註解(Annotation)不是程式,但可以對程式作出解釋,也可以被其它程式(如編譯器)讀取。
註解的格式:以@註釋名在程式碼中存在,還可以新增一些引數值例如@SuppressWarnings(value="unchecked")。
註解可在package、class、method、field等上面使用,作用是為它們新增了額外的輔助資訊,從而可以通過反射機制實現對這些後設資料的存取。
表示某方法旨在覆蓋超類中的方法宣告,該方法將覆蓋或實現在超類中宣告的方法。
@RequestMapping註解的主要用途是將Web請求與請求處理類中的方法進行對映,注意有以下幾個屬性:
@RequestBody在處理請求方法的參數列中使用,它可以將請求主體中的引數繫結到一個物件中,請求主體引數是通過HttpMessageConverter傳遞的,根據請求主體中的引數名與物件的屬性名進行匹配並繫結值。此外,還可以通過@Valid註解對請求主體中的引數進行校驗。
@GetMapping註解用於處理HTTP GET請求,並將請求對映到具體的處理方法中。具體來說,@GetMapping是一個組合註解,它相當於是@RequestMapping(method=RequestMethod.GET)的快捷方式。
@PathVariable註解是將方法中的引數繫結到請求URI中的模板變數上。可以通過@RequestMapping註解來指定URI的模板變數,然後使用@PathVariable註解將方法中的引數繫結到模板變數上。
@RequestParam註解用於將方法的引數與Web請求的傳遞的引數進行繫結。使用@RequestParam可以輕鬆的存取HTTP請求引數的值。
@ComponentScan註解用於設定Spring需要掃描的被元件註解註釋的類所在的包。可以通過設定其basePackages屬性或者value屬性來設定需要掃描的包路徑。value屬性是basePackages的別名。
@Component註解用於標註一個普通的元件類,它沒有明確的業務範圍,只是通知Spring被此註解的類需要被納入到Spring Bean容器中並進行管理。
@Service註解是@Component的一個延伸(特例),它用於標註業務邏輯類。與@Component註解一樣,被此註解標註的類,會自動被Spring所管理。
@Repository註解也是@Component註解的延伸,與@Component註解一樣,被此註解標註的類會被Spring自動管理起來,@Repository註解用於標註DAO層的資料持久化類。
4個元個元註解分別是:@Target、@Retention、@Documented、@Inherited 。
再次強調下元註解是java API提供,是專門用來定義註解的註解。
描述註解能夠作用的位置,ElementType取值:
表示需要在什麼級別儲存該註釋資訊(生命週期):
RetentionPolicy.RUNTIME
:記憶體中的位元組碼,VM將在執行時也保留註解,因此可以通過反射機制讀取註解的資訊
描述註解是否被抽取到api檔案中。
描述註解是否被子類繼承。
學習自定義註解對於理解Spring框架十分有好處,即使在實際專案中可能不需要使用自定義註解,但可以幫助我們掌握Spring的一些底層原理,從而提高對整體專案的把握。
/** * 自定義註解 * @author Created by zhuzqc on 2022/5/31 23:03 */ public class CustomAnnotation { /** * 註解中可以為引數賦值,如果沒有預設值,那麼必須為註解的引數賦值 * */ @MyAnnotation(value = "解釋") public void test(){ } } /** * @author zhuzqc */ //自定義註解必須的元註解target,指明註解的作用域(此處指明的是在類和方法上起作用) @Target({ElementType.TYPE,ElementType.METHOD}) //元註解retention宣告該註解在何時起作用(此處指明的是在執行時起作用) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation{ //註解中需宣告引數,格式為:引數型別 + 引數名(); String value() default ""; }
4.1.1動態語言
是一種在執行時可以改變其結構的語言,例如新的函數、物件甚至程式碼可以被引進,已有的函數可以被刪除或是進行其它結構上的變化。
主要的動態語言有:Object-C、C#、PHP、Python、JavaScript 等。
以 JavaScript 語言舉例:
/** * 由於未指定var的具體型別,函數在執行時間可以改變var的型別 * */ function f(){ var x = "var a = 3; var b = 5; alert(a+b)"; eval(x) }
4.2.2靜態語言
與動態語言相對的、執行時結構不可變的語言就是靜態語言,如 Java、C、C++ 等。
Java 不是動態語言,但 Java 可以稱為”準動態語言“。即 Java 有一定的動態性,可以利用反射機制獲得類似於動態語言的特性,從而使得 Java 語言在程式設計時更加靈活。
Reflection(反射)是 Java 被視為準動態語言的關鍵:反射機制允許程式在執行期間藉助 Reflection API 獲取任何類的內部資訊,並能直接操作任意物件的內部屬性及方法。
Class c = Class.forName("java.lang.String")
載入完類後,在堆記憶體的方法區就產生了一個Class型別的物件(一個類只有一個Class物件),這個類就包含了完整的類的結構資訊。我沒可以通過這個物件,像鏡子一樣看到類的結構,這個過程形象地被稱之為反射。
通過程式碼更易於理解:
/** * 反射的概念 * @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"); //一個類在記憶體中只有唯一個Class物件 System.out.println(c.hashCode()); } } /** * 定義一個實體類entity * */ @Data class User{ private String userName; private Long userId; private Date loginTime; }
由於該類繼承 Object,在 Object 類中有 getClass() 方法,該方法被所有子類繼承:
@HotSpotIntrinsicCandidate public final native Class<?> getClass();
注:該方法的返回值型別是一個 Class 類,該類是 Java 反射的源頭。
反射的優點:執行期型別的判斷、動態載入類、提高程式碼靈活度。
4.2.1反射機制主要功能
4.2.2主要API
前面提到,反射後可以得到某個類的屬性、方法和構造器、實現的介面。
有以下5種方式可以獲取Class類的範例:
1.若已知具體的類,可以通過類的class屬性獲取,該fang'shi最為安全可靠,且程式效能最高。
//類的class屬性 Class classOne = User.class;
2. 已知某個類的範例,通過呼叫該範例的getClass方法獲取Class物件。
//已有類物件的getClass方法 Class collatz = user.getClass();
3.已知一個類的全類名,且該類在類路徑下,可以通過靜態方法forName()獲取。
Class c = Class.forName("com.dcone.zhuzqc.demo.User");
4.內建基本資料型別可以直接使用類名.Type獲取。
//內建物件才有的TYPE屬性,較大的侷限性 Class<Integer> type = Integer.TYPE;
5.利用ClassLoader(類載入器)獲取。
1.class:外部類、成員(成員內部類,靜態內部類),區域性內部類,匿名內部類;
//類可以反射 Class c1 = Person.class;
2.interface:所有介面;
//介面可以反射 Class c2 = Comparable.class;
3.[]:陣列;
//陣列可以反射 Class c3 = String[].class; Class c4 = int[][].class;
4.enum:列舉;
//列舉可以反射 Class c6 = ElementType.class;
5.annotation:註解(@interface);
//註解可以反射 Class c5 = Data.class;
6.基本資料型別;
//基本資料型別(包裝類)可以反射 Class c7 = int.class; Class c8 = Integer.class;
7.void。
//void可以反射 Class c9 = void.class;
當程式主動使用某個類時,如果該類還未被載入到記憶體中,則系統會通過如下3個步驟來對該類進行初始化。
1.類的載入(Load):將類的 class 檔案位元組碼內容讀入記憶體,並將這些靜態資料轉換成方法區執行時的資料結構,同時建立一個java.lang.Class物件,此過程由類載入器完成;
2.類的連結(Link):將類的二進位制資料合併到 JRE 中,確保載入的類資訊符合 JVM 規範,同時 JVM 將常數池內的參照替換為地址。
3.類的初始化(Initialize):JVM 負責對類進行初始化,分為類的主動參照和被動參照。
類的主動參照
類的被動參照
JVM支援兩種型別的類載入器,分別為引導類載入器(BootstrapClassLoader)和自定義類載入器(User-Defined ClassLoader)。
從概念上來講,自定義類載入器一般指的是程式中由開發人員自定義的一類,類載入器。
但是Java虛擬機器器規範卻沒有這麼定義,而是將所有派生於抽象類ClassLoader的類載入器都劃分為自定義類載入器。
無論類載入器的型別如何劃分,在程式中我們最常見的類載入器始終只有3個,具體如下圖所示:
類載入器
所以具體為引導類載入器(BootstrapClassLoader)和自定義類載入器(包括ExtensionClassLoader、Application ClassLoader(也叫System ClassLoader)、User Defined ClassLoader)。
public class Test03 { public static void main(String[] args) { //獲取系統類的載入器 ClassLoader sysLoader = ClassLoader.getSystemClassLoader(); System.out.println(sysLoader); //獲取系統類的父類別載入器 ClassLoader parent = sysLoader.getParent(); System.out.println(parent); } }
通過反射獲取執行時類的完整結構:Field、Method、Constructor、Superless、Interface、Annotation等。
即:實現的全部介面、所繼承的父類別、全部的構造器、全部的方法、全部的成員變數(區域性變數)、註解等。
/** * @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 中採用泛型擦除的機制來引入泛型,Java 中的泛型僅僅是給編譯器 javac 使用的,目的是確保資料的安全性以及免去強制型別轉換的問題。一旦編譯完成,所有和泛型相關的型別全部擦除。
在Java中可以通過反射獲取泛型資訊的場景有如下三個:
在Java中不可以通過反射獲取泛型資訊的場景有如下兩個:
要獲取泛型資訊,必須要注意ParameterizedType類,該類中的getActualTypeArguments()方法可以有效獲取泛型資訊。
下面以獲取成員方法引數的泛型型別資訊為例:
public class Demo { public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException { // 獲取成員方法引數的泛型型別資訊 getMethodParametricGeneric(); }
/** * 獲取方法引數的泛型型別資訊 * * @throws NoSuchMethodException */ public static void getMethodParametricGeneric() throws NoSuchMethodException { // 獲取MyTestClass類中名為"setList"的方法 Method setListMethod = MyClass.class.getMethod("setList", List.class); // 獲取該方法的引數型別資訊(帶有泛型) Type[] genericParameterTypes = setListMethod.getGenericParameterTypes(); // 但我們實際上需要獲取返回值型別中的泛型資訊,所以要進一步判斷,即判斷獲取的返回值型別是否是引數化型別ParameterizedType for (Type genericParameterType : genericParameterTypes) { ParameterizedType parameterizedType = (ParameterizedType) genericParameterType; // 獲取成員方法引數的泛型型別資訊 Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { Class realType = (Class) actualTypeArgument; System.out.println("成員方法引數的泛型資訊:" + realType); } } }
在開發中可能會遇到這樣的場景:獲取類的屬性釋義,這些釋義定義在類屬性的註解中。
/** * 定義一個實體類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:獲取方法引數上的註解
@Bean("sqlSessionFactory") public String test(@RequestBody User user) throws ClassNotFoundException { Class c2 = Class.forName("com.dcone.zhuzqc.demo.User"); // 獲取方法引數註解 Method[] methods2 = User.class.getDeclaredMethods(); for (Method m : methods2) { // 獲取方法的所有引數 Parameter[] parameters = m.getParameters(); for (Parameter p : parameters) { // 判斷是否存在註解 if (p.isAnnotationPresent(ApiModelProperty.class)) { System.out.println(p.getAnnotation(ApiModelProperty.class).name()); } } } return "test"; }
以上就是一文搞懂Spring中的註解與反射的詳細內容,更多關於Spring註解 反射的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45