今天要講的是註解,對於本章節,最好是有Servlet基礎的人查閱~由於單純是Java基礎的話,可能用不上註解這個東西。但若是開發過Servlet,就對@WebServlet
不會陌生。java
如今的開發都推崇使用註解來進行開發,這樣就能夠免去寫XML配置了,十分方便的一項技術~web
學習註解能夠更好地理解註解是怎麼工做的,看見註解了就能夠想到它的運行原理了~。數組
若是有錯的地方請你們多多包涵並歡迎在評論區指正~安全
註解:Annotation....微信
註解其實就是代碼中的特殊標記,這些標記能夠在編譯、類加載、運行時被讀取,並執行相對應的處理。函數
傳統的方式,咱們是經過配置文件(xml文件)來告訴類是如何運行的。工具
有了註解技術之後,咱們就能夠經過註解告訴類如何運行學習
例如:咱們之前編寫Servlet的時候,須要在web.xml文件配置具體的信息this
咱們使用了註解之後,能夠直接在Servlet源代碼上,增長註解...Servlet就被配置到Tomcat上了。也就是說,註解能夠給類、方法上注入信息。spa
明顯地能夠看出,這樣是很是直觀的,而且Servlet規範是推崇這種配置方式的。
在java.lang包下存在着5個基本的Annotation,其中有3個Annotation咱們是很是常見的了。
重寫註解
若是咱們使用IDE重寫父類的方法,咱們就能夠看見它了。那它有什麼用呢??
@Overried是告訴編譯器要檢查該方法是實現父類的...能夠幫咱們避免一些低級的錯誤...
好比,咱們在實現equals()方法的時候,把euqals()打錯了,那麼編譯器就會發現該方法並非實現父類的,與註解@Overried衝突,因而就會給予錯誤。
過期註解
該註解也很是常見,Java在設計的時候,可能以爲某些方法設計得很差,爲了兼容之前的程序,是不能直接把它拋棄的,因而就設置它爲過期。
Date對象中的toLocalString()就被設置成過期了
@Deprecated
public String toLocaleString() {
DateFormat formatter = DateFormat.getDateTimeInstance();
return formatter.format(this);
}
複製代碼
當咱們在程序中調用它的時候,在IDE上會出現一條橫槓,說明該方法是過期的。
抑制編譯器警告註解
該註解在咱們寫程序的時候並非很常見,咱們能夠用它來讓編譯器不給予咱們警告
當咱們在使用集合的時候,若是沒有指定泛型,那麼會提示安全檢查的警告
若是咱們在類上添加了@SuppressWarnings這個註解,那麼編譯器就不會給予咱們警告了
Java 7「堆污染」警告
什麼是堆污染呢??當把一個不是泛型的集合賦值給一個帶泛型的集合的時候,這種狀況就很容易發生堆污染....
這個註解也是用來抑制編譯器警告的註解...用的地方並很少,我也不詳細說明了......有用到的時候再回來填坑吧。
@FunctionalInterface用來指定該接口是函數式接口
用該註解顯示指定該接口是一個函數式接口。
上面講解的是java.lang包下的5個註解,咱們是能夠本身來寫註解,給方法或類注入信息。
##4.1標記Annotation ##
沒有任何成員變量的註解稱做爲標記註解,@Overried就是一個標記註解
//有點像定義一個接口同樣,只不過它多了一個@
public @interface MyAnnotation {
}
複製代碼
咱們自定義的註解是能夠帶成員變量的,定義帶成員變量的註解叫作元數據Annotation
在註解中定義成員變量,語法相似於聲明方法同樣....
public @interface MyAnnotation {
//定義了兩個成員變量
String username();
int age();
}
複製代碼
注意:在註解上定義的成員變量只能是String、數組、Class、枚舉類、註解
有的人可能會奇怪,爲何註解上還要定義註解成員變量??聽起來就很複雜了....
上邊已經說了,註解的做用就是給類、方法注入信息。那麼咱們常用XML文件,告訴程序怎麼運行。XML常常會有嵌套的狀況
<書>
<做者>zhongfucheng</做者>
<價錢>22222</價錢>
</書>
複製代碼
那麼,當咱們在使用註解的時候,也可能須要有嵌套的時候,因此就容許了註解上能夠定義成員變量爲註解。
上面咱們已經定義了一個註解了,下面咱們來使用它吧
下面我有一個add的方法,須要username和age參數,咱們經過註解來讓該方法擁有這兩個變量!
//註解擁有什麼屬性,在修飾的時候就要給出相對應的值
@MyAnnotation(username = "zhongfucheng", age = 20)
public void add(String username, int age) {
}
複製代碼
固然啦,咱們能夠在註解聲明屬性的時候,給出默認值。那麼在修飾的時候,就能夠不用具體指定了。
public @interface MyAnnotation {
//定義了兩個成員變量
String username() default "zicheng";
int age() default 23;
}
複製代碼
@MyAnnotation()
public void add(String username, int age) {
}
複製代碼
還有一種特殊的狀況,若是註解上只有一個屬性,而且屬性的名稱爲value,那麼在使用的時候,咱們能夠不寫value,直接賦值給它就行
public @interface MyAnnotation2 {
String value();
}
複製代碼
@MyAnnotation2("zhongfucheng")
public void find(String id) {
}
複製代碼
上面咱們已經使用到了註解,可是目前爲止註解上的信息和方法上的信息是沒有任何關聯的。
咱們使用Servlet註解的時候,僅僅調用註解,那麼註解的就生效了。這是Web容器把內部實現了。咱們本身寫的自定義註解是須要咱們本身來處理的。
那如今問題來了,咱們怎麼把註解上的信息注入到方法上呢???咱們利用的是反射技術
步驟可分爲三部:
//反射出該類的方法
Class aClass = Demo2.class;
Method method = aClass.getMethod("add", String.class, int.class);
//經過該方法獲得註解上的具體信息
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
String username = annotation.username();
int age = annotation.age();
//將註解上的信息注入到方法上
Object o = aClass.newInstance();
method.invoke(o, username, age);
複製代碼
當咱們執行的時候,咱們發現會出現異常...
此時,咱們須要在自定義註解上加入這樣一句代碼**(下面就會講到,爲何要加入這句代碼)**
@Retention(RetentionPolicy.RUNTIME)
複製代碼
再次執行的時候,咱們就會發現,能夠經過註解來把信息注入到方法中了。
前面咱們已經介紹了java.lang包下的幾個基本Annotation了。在JDK中除了java.lang包下有Annotation,在java.lang.annotation下也有幾個經常使用的元Annotation。
在annotation包下的好幾個元Annotation都是用於修飾其餘的Annotation定義。
上面在將註解信息注入到方法中的時候,咱們最後加上了@Retention的註解....否則就會報錯了..那它是幹什麼用的呢?
@Retention只能用於修飾其餘的Annotation,用於指定被修飾的Annotation被保留多長時間。
@Retention 包含了一個RetentionPolicy類型的value變量,因此在使用它的時候,必需要爲value成員變量賦值
value變量的值只有三個:
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文件有三個時期:編譯,class,運行。@Retention默認是class
前面咱們是使用反射來獲得註解上的信息的,由於@Retention默認是class,而反射是在運行時期來獲取信息的。所以就獲取不到Annotation的信息了。因而,就得在自定義註解上修改它的RetentionPolicy值
@Target也是只能用於修飾另外的Annotation,它用於指定被修飾的Annotation用於修飾哪些程序單元
@Target是隻有一個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
@Documented用於指定被該Annotation修飾的Annotation類將被javadoc工具提取成文檔。
該元Annotation用得挺少的....
@Inherited也是用來修飾其餘的Annotation的,被修飾過的Annotation將具備繼承性。。。
例子:
前面咱們已經可使用註解將基本的信息注入到方法上了,如今咱們要使用的是將對象注入到方法上.....
上邊已經說過了,註解上只能定義String、枚舉類、Double之類的成員變量,那怎麼把對象注入到方法上呢?
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;
}
}
複製代碼
public class PersonDao {
private Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
複製代碼
public class PersonDao {
private Person person;
public Person getPerson() {
return person;
}
//將username爲zhongfucheng,age爲20的Person對象注入到setPerson方法中
@InjectPerson(username = "zhongfucheng",age = 20)
public void setPerson(Person person) {
this.person = person;
}
}
複製代碼
步驟:
①: 自定義一個註解,屬性是和JavaBean類一致的
//注入工具是經過反射來獲得註解的信息的,因而保留域必須使用RunTime
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectPerson {
String username();
int age();
}
複製代碼
②:編寫注入工具
//1.使用內省【後邊須要獲得屬性的寫方法】,獲得想要注入的屬性
PropertyDescriptor descriptor = new PropertyDescriptor("person", PersonDao.class);
//2.獲得要想注入屬性的具體對象
Person person = (Person) descriptor.getPropertyType().newInstance();
//3.獲得該屬性的寫方法【setPerson()】
Method method = descriptor.getWriteMethod();
//4.獲得寫方法的註解
Annotation annotation = method.getAnnotation(InjectPerson.class);
//5.獲得註解上的信息【註解的成員變量就是用方法來定義的】
Method[] methods = annotation.getClass().getMethods();
//6.將註解上的信息填充到person對象上
for (Method m : methods) {
//獲得註解上屬性的名字【age或name】
String name = m.getName();
//看看Person對象有沒有與之對應的方法【setAge(),setName()】
try {
//6.1這裏假設:有與之對應的寫方法,獲得寫方法
PropertyDescriptor descriptor1 = new PropertyDescriptor(name, Person.class);
Method method1 = descriptor1.getWriteMethod();//setAge(), setName()
//獲得註解中的值
Object o = m.invoke(annotation, null);
//調用Person對象的setter方法,將註解上的值設置進去
method1.invoke(person, o);
} catch (Exception e) {
//6.2 Person對象沒有與之對應的方法,會跳到catch來。咱們要讓它繼續遍歷註解就行了
continue;
}
}
//當程序遍歷完以後,person對象已經填充完數據了
//7.將person對象賦給PersonDao【經過寫方法】
PersonDao personDao = new PersonDao();
method.invoke(personDao, person);
System.out.println(personDao.getPerson().getUsername());
System.out.println(personDao.getPerson().getAge());
複製代碼
③:總結一下步驟
其實咱們是這樣把對象注入到方法中的:
上面已經說了如何將對象注入到方法上了,那麼注入到成員變量上也是很是簡單的。
步驟:
①:在成員變量上使用註解
public class PersonDao {
@InjectPerson(username = "zhongfucheng",age = 20) private Person person;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
複製代碼
②:編寫注入工具
//1.獲得想要注入的屬性
Field field = PersonDao.class.getDeclaredField("person");
//2.獲得屬性的具體對象
Person person = (Person) field.getType().newInstance();
//3.獲得屬性上的註解
Annotation annotation = field.getAnnotation(InjectPerson.class);
//4.獲得註解的屬性【註解上的屬性使用方法來表示的】
Method[] methods = annotation.getClass().getMethods();
//5.將注入的屬性填充到person對象上
for (Method method : methods) {
//5.1獲得註解屬性的名字
String name = method.getName();
//查看一下Person對象上有沒有與之對應的寫方法
try {
//若是有
PropertyDescriptor descriptor = new PropertyDescriptor(name, Person.class);
//獲得Person對象上的寫方法
Method method1 = descriptor.getWriteMethod();
//獲得註解上的值
Object o = method.invoke(annotation, null);
//填充person對象
method1.invoke(person, o);
} catch (IntrospectionException e) {
//若是沒有想對應的屬性,繼續循環
continue;
}
}
//循環完以後,person就已經填充好數據了
//6.把person對象設置到PersonDao中
PersonDao personDao = new PersonDao();
field.setAccessible(true);
field.set(personDao, person);
System.out.println(personDao.getPerson().getUsername());
複製代碼
①:注入對象的步驟:獲得想要注入的對象屬性,經過屬性獲得註解的信息,經過屬性的寫方法將註解的信息注入到對象上,最後將對象賦給類。
②:註解其實就是兩個做用:
③:在JDK中註解分爲了
若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:Java3y