摘要:上面在將注解信息注入到方法中的時(shí)候,我們最后加上了的注解不然就會(huì)報(bào)錯(cuò)了那它是干什么用的呢只能用于修飾其他的,用于指定被修飾的被保留多長(zhǎng)時(shí)間。
前言
今天要講的是注解,對(duì)于本章節(jié),最好是有Servlet基礎(chǔ)的人查閱~因?yàn)閱渭兪荍ava基礎(chǔ)的話,可能用不上注解這個(gè)東西。但如果開(kāi)發(fā)過(guò)Servlet,就對(duì)@WebServlet不會(huì)陌生。
現(xiàn)在的開(kāi)發(fā)都推崇使用注解來(lái)進(jìn)行開(kāi)發(fā),這樣就可以免去寫(xiě)XML配置了,十分方便的一項(xiàng)技術(shù)~
學(xué)習(xí)注解可以更好地理解注解是怎么工作的,看見(jiàn)注解了就可以想到它的運(yùn)行原理了~。
如果有錯(cuò)的地方請(qǐng)大家多多包涵并歡迎在評(píng)論區(qū)指正~
一、什么是注解?注解:Annotation....
注解其實(shí)就是代碼中的特殊標(biāo)記,這些標(biāo)記可以在編譯、類(lèi)加載、運(yùn)行時(shí)被讀取,并執(zhí)行相對(duì)應(yīng)的處理。
二、為什么我們需要用到注解?傳統(tǒng)的方式,我們是通過(guò)配置文件(xml文件)來(lái)告訴類(lèi)是如何運(yùn)行的。
有了注解技術(shù)以后,我們就可以通過(guò)注解告訴類(lèi)如何運(yùn)行
例如:我們以前編寫(xiě)Servlet的時(shí)候,需要在web.xml文件配置具體的信息
我們使用了注解以后,可以直接在Servlet源代碼上,增加注解...Servlet就被配置到Tomcat上了。也就是說(shuō),注解可以給類(lèi)、方法上注入信息。
明顯地可以看出,這樣是非常直觀的,并且Servlet規(guī)范是推崇這種配置方式的。
三、基本Annotation在java.lang包下存在著5個(gè)基本的Annotation,其中有3個(gè)Annotation我們是非常常見(jiàn)的了。
3.1@Overried重寫(xiě)注解
如果我們使用IDE重寫(xiě)父類(lèi)的方法,我們就可以看見(jiàn)它了。那它有什么用呢??
@Overried是告訴編譯器要檢查該方法是實(shí)現(xiàn)父類(lèi)的...可以幫我們避免一些低級(jí)的錯(cuò)誤...
比如,我們?cè)趯?shí)現(xiàn)equals()方法的時(shí)候,把euqals()打錯(cuò)了,那么編譯器就會(huì)發(fā)現(xiàn)該方法并不是實(shí)現(xiàn)父類(lèi)的,與注解@Overried沖突,于是就會(huì)給予錯(cuò)誤。
3.2@Deprecated過(guò)時(shí)注解
該注解也非常常見(jiàn),Java在設(shè)計(jì)的時(shí)候,可能覺(jué)得某些方法設(shè)計(jì)得不好,為了兼容以前的程序,是不能直接把它拋棄的,于是就設(shè)置它為過(guò)時(shí)。
Date對(duì)象中的toLocalString()就被設(shè)置成過(guò)時(shí)了
@Deprecated public String toLocaleString() { DateFormat formatter = DateFormat.getDateTimeInstance(); return formatter.format(this); }
當(dāng)我們?cè)诔绦蛑姓{(diào)用它的時(shí)候,在IDE上會(huì)出現(xiàn)一條橫杠,說(shuō)明該方法是過(guò)時(shí)的。
3.3@SuppressWarnings抑制編譯器警告注解
該注解在我們寫(xiě)程序的時(shí)候并不是很常見(jiàn),我們可以用它來(lái)讓編譯器不給予我們警告
當(dāng)我們?cè)谑褂眉系臅r(shí)候,如果沒(méi)有指定泛型,那么會(huì)提示安全檢查的警告
如果我們?cè)陬?lèi)上添加了@SuppressWarnings這個(gè)注解,那么編譯器就不會(huì)給予我們警告了
3.4@SafeVarargsJava 7“堆污染”警告
什么是堆污染呢??當(dāng)把一個(gè)不是泛型的集合賦值給一個(gè)帶泛型的集合的時(shí)候,這種情況就很容易發(fā)生堆污染....
這個(gè)注解也是用來(lái)抑制編譯器警告的注解...用的地方并不多,我也不詳細(xì)說(shuō)明了......有用到的時(shí)候再回來(lái)填坑吧。
3.5@FunctionalInterface@FunctionalInterface用來(lái)指定該接口是函數(shù)式接口
用該注解顯示指定該接口是一個(gè)函數(shù)式接口。
四、自定義注解基礎(chǔ)上面講解的是java.lang包下的5個(gè)注解,我們是可以自己來(lái)寫(xiě)注解,給方法或類(lèi)注入信息。
4.1標(biāo)記Annotation沒(méi)有任何成員變量的注解稱(chēng)作為標(biāo)記注解,@Overried就是一個(gè)標(biāo)記注解
//有點(diǎn)像定義一個(gè)接口一樣,只不過(guò)它多了一個(gè)@ public @interface MyAnnotation { }4.2元數(shù)據(jù)Annotation
我們自定義的注解是可以帶成員變量的,定義帶成員變量的注解叫做元數(shù)據(jù)Annotation
在注解中定義成員變量,語(yǔ)法類(lèi)似于聲明方法一樣....
public @interface MyAnnotation { //定義了兩個(gè)成員變量 String username(); int age(); }
注意:在注解上定義的成員變量只能是String、數(shù)組、Class、枚舉類(lèi)、注解
有的人可能會(huì)奇怪,為什么注解上還要定義注解成員變量??聽(tīng)起來(lái)就很復(fù)雜了....
上邊已經(jīng)說(shuō)了,注解的作用就是給類(lèi)、方法注入信息。那么我們經(jīng)常使用XML文件,告訴程序怎么運(yùn)行。XML經(jīng)常會(huì)有嵌套的情況
<書(shū)> <作者>zhongfucheng作者> <價(jià)錢(qián)>22222價(jià)錢(qián)> 書(shū)>
那么,當(dāng)我們?cè)谑褂米⒔獾臅r(shí)候,也可能需要有嵌套的時(shí)候,所以就允許了注解上可以定義成員變量為注解。
4.3使用自定義注解上面我們已經(jīng)定義了一個(gè)注解了,下面我們來(lái)使用它吧
4.3.1常規(guī)使用下面我有一個(gè)add的方法,需要username和age參數(shù),我們通過(guò)注解來(lái)讓該方法擁有這兩個(gè)變量!
//注解擁有什么屬性,在修飾的時(shí)候就要給出相對(duì)應(yīng)的值 @MyAnnotation(username = "zhongfucheng", age = 20) public void add(String username, int age) { }4.3.2默認(rèn)值
當(dāng)然啦,我們可以在注解聲明屬性的時(shí)候,給出默認(rèn)值。那么在修飾的時(shí)候,就可以不用具體指定了。
public @interface MyAnnotation { //定義了兩個(gè)成員變量 String username() default "zicheng"; int age() default 23; }
在修飾的時(shí)候就不需要給出具體的值了
@MyAnnotation() public void add(String username, int age) { }4.3.3注解屬性為value
還有一種特殊的情況,如果注解上只有一個(gè)屬性,并且屬性的名稱(chēng)為value,那么在使用的時(shí)候,我們可以不寫(xiě)value,直接賦值給它就行
public @interface MyAnnotation2 { String value(); }
使用注解,可以不指定value,直接賦值
@MyAnnotation2("zhongfucheng") public void find(String id) { }4.4把自定義注解的基本信息注入到方法上
上面我們已經(jīng)使用到了注解,但是目前為止注解上的信息和方法上的信息是沒(méi)有任何關(guān)聯(lián)的。
我們使用Servlet注解的時(shí)候,僅僅調(diào)用注解,那么注解的就生效了。這是Web容器把內(nèi)部實(shí)現(xiàn)了。我們自己寫(xiě)的自定義注解是需要我們自己來(lái)處理的。
那現(xiàn)在問(wèn)題來(lái)了,我們?cè)趺窗炎⒔馍系男畔⒆⑷氲椒椒ㄉ夏????我?strong>利用的是反射技術(shù)
步驟可分為三部:
反射出該類(lèi)的方法
通過(guò)方法得到注解上具體的信息
將注解上的信息注入到方法上
//反射出該類(lèi)的方法 Class aClass = Demo2.class; Method method = aClass.getMethod("add", String.class, int.class); //通過(guò)該方法得到注解上的具體信息 MyAnnotation annotation = method.getAnnotation(MyAnnotation.class); String username = annotation.username(); int age = annotation.age(); //將注解上的信息注入到方法上 Object o = aClass.newInstance(); method.invoke(o, username, age);
當(dāng)我們執(zhí)行的時(shí)候,我們發(fā)現(xiàn)會(huì)出現(xiàn)異常...
此時(shí),我們需要在自定義注解上加入這樣一句代碼(下面就會(huì)講到,為什么要加入這句代碼)
@Retention(RetentionPolicy.RUNTIME)
再次執(zhí)行的時(shí)候,我們就會(huì)發(fā)現(xiàn),可以通過(guò)注解來(lái)把信息注入到方法中了。
五、JDK的元Annotation前面我們已經(jīng)介紹了java.lang包下的幾個(gè)基本Annotation了。在JDK中除了java.lang包下有Annotation,在java.lang.annotation下也有幾個(gè)常用的元Annotation。
在annotation包下的好幾個(gè)元Annotation都是用于修飾其他的Annotation定義。
5.1@Retention上面在將注解信息注入到方法中的時(shí)候,我們最后加上了@Retention的注解....不然就會(huì)報(bào)錯(cuò)了..那它是干什么用的呢?
@Retention只能用于修飾其他的Annotation,用于指定被修飾的Annotation被保留多長(zhǎng)時(shí)間。
@Retention 包含了一個(gè)RetentionPolicy類(lèi)型的value變量,所以在使用它的時(shí)候,必須要為value成員變量賦值
value變量的值只有三個(gè):
public enum RetentionPolicy { /** * Annotations are to be discarded by the compiler. */ SOURCE, /** * Annotations are to be recorded in the class file by the compiler * but need not be retained by the VM at run time. This is the default * behavior. */ CLASS, /** * Annotations are to be recorded in the class file by the compiler and * retained by the VM at run time, so they may be read reflectively. * * @see java.lang.reflect.AnnotatedElement */ RUNTIME }
java文件有三個(gè)時(shí)期:編譯,class,運(yùn)行。@Retention默認(rèn)是class
前面我們是使用反射來(lái)得到注解上的信息的,因?yàn)锧Retention默認(rèn)是class,而反射是在運(yùn)行時(shí)期來(lái)獲取信息的。因此就獲取不到Annotation的信息了。于是,就得在自定義注解上修改它的RetentionPolicy值
5.2@Target@Target也是只能用于修飾另外的Annotation,它用于指定被修飾的Annotation用于修飾哪些程序單元
@Target是只有一個(gè)value成員變量的,該成員變量的值是以下的:
public enum ElementType { /** Class, interface (including annotation type), or enum declaration */ TYPE, /** Field declaration (includes enum constants) */ FIELD, /** Method declaration */ METHOD, /** Parameter declaration */ PARAMETER, /** Constructor declaration */ CONSTRUCTOR, /** Local variable declaration */ LOCAL_VARIABLE, /** Annotation type declaration */ ANNOTATION_TYPE, /** Package declaration */ PACKAGE }
如果@Target指定的是ElementType.ANNOTATION_TYPE,那么該被修飾的Annotation只能修飾Annotaion
5.3@Documented@Documented用于指定被該Annotation修飾的Annotation類(lèi)將被javadoc工具提取成文檔。
該元Annotation用得挺少的....
5.4@Inherited@Inherited也是用來(lái)修飾其他的Annotation的,被修飾過(guò)的Annotation將具有繼承性。。。
例子:
@xxx是我自定義的注解,我現(xiàn)在使用@xxx注解在Base類(lèi)上使用....
使用@Inherited修飾@xxx注解
當(dāng)有類(lèi)繼承了Base類(lèi)的時(shí)候,該實(shí)現(xiàn)類(lèi)自動(dòng)擁有@xxx注解
六、注入對(duì)象到方法或成員變量上 6.1把對(duì)象注入到方法上前面我們已經(jīng)可以使用注解將基本的信息注入到方法上了,現(xiàn)在我們要使用的是將對(duì)象注入到方法上.....
上邊已經(jīng)說(shuō)過(guò)了,注解上只能定義String、枚舉類(lèi)、Double之類(lèi)的成員變量,那怎么把對(duì)象注入到方法上呢?
6.1.2模擬場(chǎng)景:Person類(lèi),定義username和age屬性,擁有uername和age的getter和setter方法
public class Person { private String username; private int age; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
PersonDao類(lèi),PersonDao類(lèi)定義了Person對(duì)象,擁有person的setter和getter方法
public class PersonDao { private Person person; public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } }
現(xiàn)在我要做的就是:使用注解將Person對(duì)象注入到setPerson()方法中,從而設(shè)置了PersonDao類(lèi)的person屬性
public class PersonDao { private Person person; public Person getPerson() { return person; } //將username為zhongfucheng,age為20的Person對(duì)象注入到setPerson方法中 @InjectPerson(username = "zhongfucheng",age = 20) public void setPerson(Person person) { this.person = person; } }
步驟:
①: 自定義一個(gè)注解,屬性是和JavaBean類(lèi)一致的
//注入工具是通過(guò)反射來(lái)得到注解的信息的,于是保留域必須使用RunTime @Retention(RetentionPolicy.RUNTIME) public @interface InjectPerson { String username(); int age(); }
②:編寫(xiě)注入工具
//1.使用內(nèi)省【后邊需要得到屬性的寫(xiě)方法】,得到想要注入的屬性 PropertyDescriptor descriptor = new PropertyDescriptor("person", PersonDao.class); //2.得到要想注入屬性的具體對(duì)象 Person person = (Person) descriptor.getPropertyType().newInstance(); //3.得到該屬性的寫(xiě)方法【setPerson()】 Method method = descriptor.getWriteMethod(); //4.得到寫(xiě)方法的注解 Annotation annotation = method.getAnnotation(InjectPerson.class); //5.得到注解上的信息【注解的成員變量就是用方法來(lái)定義的】 Method[] methods = annotation.getClass().getMethods(); //6.將注解上的信息填充到person對(duì)象上 for (Method m : methods) { //得到注解上屬性的名字【age或name】 String name = m.getName(); //看看Person對(duì)象有沒(méi)有與之對(duì)應(yīng)的方法【setAge(),setName()】 try { //6.1這里假設(shè):有與之對(duì)應(yīng)的寫(xiě)方法,得到寫(xiě)方法 PropertyDescriptor descriptor1 = new PropertyDescriptor(name, Person.class); Method method1 = descriptor1.getWriteMethod();//setAge(), setName() //得到注解中的值 Object o = m.invoke(annotation, null); //調(diào)用Person對(duì)象的setter方法,將注解上的值設(shè)置進(jìn)去 method1.invoke(person, o); } catch (Exception e) { //6.2 Person對(duì)象沒(méi)有與之對(duì)應(yīng)的方法,會(huì)跳到catch來(lái)。我們要讓它繼續(xù)遍歷注解就好了 continue; } } //當(dāng)程序遍歷完之后,person對(duì)象已經(jīng)填充完數(shù)據(jù)了 //7.將person對(duì)象賦給PersonDao【通過(guò)寫(xiě)方法】 PersonDao personDao = new PersonDao(); method.invoke(personDao, person); System.out.println(personDao.getPerson().getUsername()); System.out.println(personDao.getPerson().getAge());
③:總結(jié)一下步驟
其實(shí)我們是這樣把對(duì)象注入到方法中的:
得到想要類(lèi)中注入的屬性
得到該屬性的對(duì)象
得到屬性對(duì)應(yīng)的寫(xiě)方法
通過(guò)寫(xiě)方法得到注解
獲取注解詳細(xì)的信息
將注解的信息注入到對(duì)象上
調(diào)用屬性寫(xiě)方法,將已填充數(shù)據(jù)的對(duì)象注入到方法中
6.2把對(duì)象注入到成員變量上面已經(jīng)說(shuō)了如何將對(duì)象注入到方法上了,那么注入到成員變量上也是非常簡(jiǎn)單的。
步驟:
①:在成員變量上使用注解
public class PersonDao { @InjectPerson(username = "zhongfucheng",age = 20) private Person person; public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } }
②:編寫(xiě)注入工具
//1.得到想要注入的屬性 Field field = PersonDao.class.getDeclaredField("person"); //2.得到屬性的具體對(duì)象 Person person = (Person) field.getType().newInstance(); //3.得到屬性上的注解 Annotation annotation = field.getAnnotation(InjectPerson.class); //4.得到注解的屬性【注解上的屬性使用方法來(lái)表示的】 Method[] methods = annotation.getClass().getMethods(); //5.將注入的屬性填充到person對(duì)象上 for (Method method : methods) { //5.1得到注解屬性的名字 String name = method.getName(); //查看一下Person對(duì)象上有沒(méi)有與之對(duì)應(yīng)的寫(xiě)方法 try { //如果有 PropertyDescriptor descriptor = new PropertyDescriptor(name, Person.class); //得到Person對(duì)象上的寫(xiě)方法 Method method1 = descriptor.getWriteMethod(); //得到注解上的值 Object o = method.invoke(annotation, null); //填充person對(duì)象 method1.invoke(person, o); } catch (IntrospectionException e) { //如果沒(méi)有想對(duì)應(yīng)的屬性,繼續(xù)循環(huán) continue; } } //循環(huán)完之后,person就已經(jīng)填充好數(shù)據(jù)了 //6.把person對(duì)象設(shè)置到PersonDao中 PersonDao personDao = new PersonDao(); field.setAccessible(true); field.set(personDao, person); System.out.println(personDao.getPerson().getUsername());七、總結(jié)
①:注入對(duì)象的步驟:得到想要注入的對(duì)象屬性,通過(guò)屬性得到注解的信息,通過(guò)屬性的寫(xiě)方法將注解的信息注入到對(duì)象上,最后將對(duì)象賦給類(lèi)。
②:注解其實(shí)就是兩個(gè)作用:
讓編譯器檢查代碼
將數(shù)據(jù)注入到方法、成員變量、類(lèi)上
③:在JDK中注解分為了
基本Annotation
在lang包下,用于常用于標(biāo)記該方法,抑制編譯器警告等
元Annotaion
在annotaion包下,常用于修飾其他的Annotation定義
如果文章有錯(cuò)的地方歡迎指正,大家互相交流。習(xí)慣在微信看技術(shù)文章,想要獲取更多的Java資源的同學(xué),可以關(guān)注微信公眾號(hào):Java3y
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/68912.html
摘要:開(kāi)啟自動(dòng)配置功能后文詳解這個(gè)注解,學(xué)過(guò)的同學(xué)應(yīng)該對(duì)它不會(huì)陌生,就是掃描注解,默認(rèn)是掃描當(dāng)前類(lèi)下的。簡(jiǎn)單來(lái)說(shuō),這個(gè)注解可以幫助我們自動(dòng)載入應(yīng)用程序所需要的所有默認(rèn)配置。簡(jiǎn)單理解這二者掃描的對(duì)象是不一樣的。 前言 只有光頭才能變強(qiáng)。 文本已收錄至我的GitHub倉(cāng)庫(kù),歡迎Star:https://github.com/ZhongFuCheng3y/3y 回顧前面Spring的文章(以學(xué)習(xí)...
摘要:是一種特殊的增強(qiáng)切面切面由切點(diǎn)和增強(qiáng)通知組成,它既包括了橫切邏輯的定義也包括了連接點(diǎn)的定義。實(shí)際上,一個(gè)的實(shí)現(xiàn)被拆分到多個(gè)類(lèi)中在中聲明切面我們知道注解很方便,但是,要想使用注解的方式使用就必須要有源碼因?yàn)槲覀円? 前言 只有光頭才能變強(qiáng) 上一篇已經(jīng)講解了Spring IOC知識(shí)點(diǎn)一網(wǎng)打盡!,這篇主要是講解Spring的AOP模塊~ 之前我已經(jīng)寫(xiě)過(guò)一篇關(guān)于AOP的文章了,那篇把比較重要的知...
摘要:?jiǎn)?dòng)原理和執(zhí)行原理分析一的啟動(dòng)原理我們打開(kāi),注意看下面兩個(gè)依賴(lài)我們第一步是繼承了父項(xiàng)目,然后在添加啟動(dòng)器的依賴(lài),項(xiàng)目就會(huì)自動(dòng)給我們導(dǎo)入關(guān)于項(xiàng)目所需要的配置和。 上一篇我們看到,我們很輕松的完成了項(xiàng)目的構(gòu)建,那么SpringBoot是如何做到的呢,在使用的使用又有哪些通用配置和注意事項(xiàng)呢? 其實(shí)SpringBoot給我們做了大量的自動(dòng)配置功能,我們只需要引入對(duì)應(yīng)的啟動(dòng)器就可以直接使用,作...
摘要:如果說(shuō)要使用數(shù)據(jù)校驗(yàn),我十分相信小伙伴們都能夠使用,但估計(jì)大都是有個(gè)前提的環(huán)境。具體使用可參考小家讓支持對(duì)平鋪參數(shù)執(zhí)行數(shù)據(jù)校驗(yàn)?zāi)J(rèn)使用只能對(duì)進(jìn)行校驗(yàn)級(jí)聯(lián)校驗(yàn)什么叫級(jí)聯(lián)校驗(yàn),其實(shí)就是帶校驗(yàn)的成員里存在級(jí)聯(lián)對(duì)象時(shí),也要對(duì)它完成校驗(yàn)。 每篇一句 NBA里有兩大笑話:一是科比沒(méi)天賦,二是詹姆斯沒(méi)技術(shù) 相關(guān)閱讀 【小家Java】深入了解數(shù)據(jù)校驗(yàn):Java Bean Validation 2.0(...
閱讀 1409·2021-09-28 09:43
閱讀 4281·2021-09-04 16:41
閱讀 1946·2019-08-30 15:44
閱讀 3792·2019-08-30 15:43
閱讀 804·2019-08-30 14:21
閱讀 2058·2019-08-30 11:00
閱讀 3350·2019-08-29 16:20
閱讀 1949·2019-08-29 14:21