java 注解详解
先引用一下百度百科的名詞解析:
定義:注解(Annotation),也叫元數據。一種代碼級別的說明。它是JDK1.5及以后版本引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,注釋。
作用分類:
①編寫文檔:通過代碼里標識的元數據生成文檔【生成文檔doc文檔】
② 代碼分析:通過代碼里標識的元數據對代碼進行分析【使用反射】
③編譯檢查:通過代碼里標識的元數據讓編譯器能夠實現基本的編譯檢查【Override】
?
下面開始正文:
概述:
就如上面所說,注解有幾種用途,第一種是編寫文檔,這種注解會在javadoc中存在;
第二種是代碼分析,這需要使用 反射 的api去解析注解;
第三種類是提供編譯時的檢查,如 deprecated 這些,編譯的時候會產生警告信息。
舉幾個例子:
1、下面的例子,注解使用了@Documented注解,這意味著,我們的注解會在 javadoc 生成的文檔中出現,另外一個@Retention的用途下面會說到
package com.ruby;import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy;@Documented @Retention(RetentionPolicy.SOURCE) public @interface ClassPreamble {String author(); }使用:
@ClassPreamble(author = "ruby" ) public class Main ...
?
2、代碼分析(我們拙劣地模擬一下Spring Boot的路由注解)
代碼目錄結構:
RequestMapping注解定義:
package com.ruby.annotation;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RequestMapping {String value() default "/"; }
Home控制器定義:
package com.ruby.controller;import com.ruby.annotation.RequestMapping;public class Home {@RequestMapping("test")public void HelloWorld() {System.out.println("HelloWorld !");} }?
使用方法(命名不是太規范,test一般用于測試文件命名):關鍵是main方法里面通過反射去解析注解的代碼,其他兩個方法只是工具方法(用以獲取包下面所有類)
package com.ruby.test;import com.ruby.annotation.RequestMapping;import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.List;public class TestHome {public static void main(String[] args) throws ClassNotFoundException, IOException, InstantiationException, IllegalAccessException, InvocationTargetException{String route = "test";Class[] classes = getClasses("com.ruby.controller");for (Class cls : classes) {Method[] methods = cls.getMethods();for (Method method : methods) {RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);if (requestMapping != null) {String mapping = requestMapping.value();if (mapping.equals(route)) {method.invoke(cls.newInstance());}}}}}/*** Scans all classes accessible from the context class loader which belong to the given package and subpackages.** @param packageName The base package* @return The classes* @throws ClassNotFoundException* @throws IOException*/private static Class[] getClasses(String packageName)throws ClassNotFoundException, IOException {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();assert classLoader != null;String path = packageName.replace('.', '/');Enumeration<URL> resources = classLoader.getResources(path);List<File> dirs = new ArrayList<File>();while (resources.hasMoreElements()) {URL resource = resources.nextElement();dirs.add(new File(resource.getFile()));}ArrayList<Class> classes = new ArrayList<Class>();for (File directory : dirs) {classes.addAll(findClasses(directory, packageName));}return classes.toArray(new Class[classes.size()]);}/*** Recursive method used to find all classes in a given directory and subdirs.** @param directory The base directory* @param packageName The package name for classes found inside the base directory* @return The classes* @throws ClassNotFoundException*/private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {List<Class> classes = new ArrayList<Class>();if (!directory.exists()) {return classes;}File[] files = directory.listFiles();for (File file : files) {if (file.isDirectory()) {assert !file.getName().contains(".");classes.addAll(findClasses(file, packageName + "." + file.getName()));} else if (file.getName().endsWith(".class")) {classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));}}return classes;} }
3、編譯時檢查:如@SuppressWarnings,如有些廢棄的方法調用之后會有警告信息,你可以使用@SuppressWarnings注解:@SuppressWarnings("deprecation"),這樣編譯的時候就不會產生警告信息了。
?
注解定義
注解定義的方法類似接口定義(上面有例子了),只是在interface關鍵字前面多了個@符號,注解定義上面也還可以有其他注解(下面詳說),注解體里面的元素基本格式都一致,類型名稱 + 變量名 + 括號 + default xxx; 默認值的提供是可選的。類型可以是普通類型,也可以是類啊、異常啊這些。
接下來要說的發圖吧,比較清晰:
?
注解分類:
1、普通注解,如:@Deprecated、@Override、@SuppressWarnings、@SafeVarargs等
2、注解其他注解的注解(比較拗口):記重點了,比較關鍵的是@Retention、@Target
?
別的不說,先發圖,看圖比較清晰:
?
?
先說@Retention,這個很重要,如果我們要在運行時使用定義的注解,一定不要寫錯了,默認是不會到運行時還存在的
使用方法:在注解定義處使用:
@Retention(RetentionPolicy.SOURCE) public @interface ClassPreamble {String author(); }有三個可選的選項:(默認是RetentionPolicy.CLASS)
1、RetentionPolicy.SOURCE,如果使用該選項,我們的注解只會在源代碼存在,編譯之后就沒有了,我們去看編譯之后的 class 文件,會發現注解已經不存在了。但是如果我們不寫@Retention 的話,編譯后的 class 文件還會有該注解,因為默認是RetentionPolicy.CLASS。
2、RetentionPolicy.CLASS(默認),使用該選項,我們的注解會在源代碼和class文件中存在
3、RetentionPolicy.RUNTIME,使用該選項,我們的注解會一直存在,我們可以在運行的時候去獲取注解信息,然后做相應處理。
?
@Document 這個注解會在 javadoc 生成的文檔中出現
?
@Target,這也算是一個比較重要的注解,指明了該注解可以用在什么地方,好比如,你指定了一個注解的Target為ElementType.METHOD,那么這個注解只能用在方法上,而不能用在field或者type上,當然你也可以同時指定多個,如:
@Target({ElementType.FIELD, ElementType.METHOD})可用類型如上圖。
?
@Repeatable 指明該注解是否可以多次使用
?
好了,就這么多了,其他還不是太了解,上面只是看文檔總結的東西,
其實最重要的還是@Retention,如果要在運行時也用到注解信息,那么必須指定? Retention.RUNTIME 參數,否則通過反射去獲取注解是獲取不到的。
?
與此相關的另外的知識就是反射了,有空再補充。
?
轉載于:https://www.cnblogs.com/eleven24/p/8278441.html
總結
- 上一篇: BZOJ2017[USACO 2009
- 下一篇: 利用官方的vue-cli脚手架来搭建Vu