註解:Annotation….java
註解其實就是代碼中的特殊標記,這些標記能夠在編譯、類加載、運行時被讀取,並執行相對應的處理。web
傳統的方式,咱們是經過配置文件(xml文件)來告訴類是如何運行的。數組
有了註解技術之後,咱們就能夠經過註解告訴類如何運行安全
例如:咱們之前編寫Servlet的時候,須要在web.xml文件配置具體的信息ruby
咱們使用了註解之後,能夠直接在Servlet源代碼上,增長註解…Servlet就被配置到Tomcat上了。也就是說,註解能夠給類、方法上注入信息。markdown
明顯地能夠看出,這樣是很是直觀的,而且Servlet規範是推崇這種配置方式的。函數
在java.lang包下存在着5個基本的Annotation,其中有3個Annotation咱們是很是常見的了。工具
重寫註解this
若是咱們使用IDE重寫父類的方法,咱們就能夠看見它了。那它有什麼用呢??spa
@Overried是告訴編譯器要檢查該方法是實現父類的…能夠幫咱們避免一些低級的錯誤…
好比,咱們在實現equals()方法的時候,把euqals()打錯了,那麼編譯器就會發現該方法並非實現父類的,與註解@Overried衝突,因而就會給予錯誤。
過期註解
該註解也很是常見,Java在設計的時候,可能以爲某些方法設計得很差,爲了兼容之前的程序,是不能直接把它拋棄的,因而就設置它爲過期。
Date對象中的toLocalString()就被設置成過期了
@Deprecated public String toLocaleString() { DateFormat formatter = DateFormat.getDateTimeInstance(); return formatter.format(this); }
當咱們在程序中調用它的時候,在IDE上會出現一條橫槓,說明該方法是過期的。
抑制編譯器警告註解
該註解在咱們寫程序的時候並非很常見,咱們能夠用它來讓編譯器不給予咱們警告
當咱們在使用集合的時候,若是沒有指定泛型,那麼會提示安全檢查的警告
若是咱們在類上添加了@SuppressWarnings這個註解,那麼編譯器就不會給予咱們警告了
Java 7「堆污染」警告
什麼是堆污染呢??當把一個不是泛型的集合賦值給一個帶泛型的集合的時候,這種狀況就很容易發生堆污染….
這個註解也是用來抑制編譯器警告的註解…用的地方並很少,我也不詳細說明了……有用到的時候再回來填坑吧。
@FunctionalInterface用來指定該接口是函數式接口
Java8的內容,等我回來填坑吧….
上面講解的是java.lang包下的5個註解,咱們是能夠本身來寫註解,給方法或類注入信息。
沒有任何成員變量的註解稱做爲標記註解,@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());
①:注入對象的步驟:獲得想要注入的對象屬性,經過屬性獲得註解的信息,將註解的信息注入到對象上,最後將對象賦給類。
②:註解其實就是兩個做用: