Java註解(Annotation):請不要小看我!
Java註解是一系列元資料,它提供資料用來解釋程式程式碼,但是註解並非是所解釋的程式碼本身的一部分。註解對於程式碼的執行效果沒有直接影響。
網路上對註解的解釋過於嚴肅、刻板,這並不是 我 喜歡的風格。儘管這樣的解釋聽起來非常的專業。
為了緩解大家對“註解”的陌生感,我來說點有意思的。其實我對“註解”這個詞的第一印象並不是Java的註解,而是朱熹的名作《四書章句集註》。為什麼我會有這麼大的腦洞呢?因為當我試著去翻譯 Annotation
這個單詞的時候,得到的結果是“註釋”而不是“註解”。《四書章句集註》正是朱熹對《大學》、《中庸》、《論語》、《孟子》四書做出的重要的註釋。要知道,該書可是明清以後科舉考試的題庫和標準答案!
註解( Annotation
)是在 Java SE 5.0 版本中開始引入的概念,同 class
和 interface
一樣,也屬於一種型別。很多開發人員認為註解的地位不高,但其實不是這樣的。像 @Transactional
、 @Service
、 @RestController
、 @RequestMapping
、 @CrossOrigin
等等這些註解的使用頻率越來越高。
01、為什麼要使用註解呢?
為什麼要使用註解呢?讓我們從另外一個問題說起。
“跨域”這兩個字就像一塊狗皮膏藥黏在每一個前端開發者的身上;我也不例外,雖然我並不是一個純粹的前端開發者。
跨域問題的出現,源於瀏覽器的同源策略——限制一個源載入的指令碼去訪問另外一個源的資源,可有效地隔離潛在的惡意檔案,是一種重要的安全機制。
跨域問題的解決方案也有很多,比如說:
1)JSONP
2)Nginx代理
3)"跨域資源共享"(Cross-origin resource sharing),簡稱 CORS
,可以說是處理跨域問題的標準做法。
記得第一次遇到跨域問題的時候,我特意向一個同學請教了解決方案,他告訴我的答案如下。
第一步,在web.xml新增filter。
<filter> <filter-name>contextfilter</filter-name> <filter-class>com.cmower.filter.WebContextFilter</filter-class> </filter> <filter-mapping> <filter-name>contextfilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
第二步,實現WebContextFilter類。
public class WebContextFilter implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponsehttpServletResponse = (HttpServletResponse) response; httpServletResponse.setHeader("Access-Control-Allow-Origin", "*"); httpServletResponse.setHeader("Access-Control-Allow-Headers", "accept,content-type"); httpServletResponse.setHeader("Access-Control-Allow-Methods", "OPTIONS,GET,POST,DELETE,PUT"); chain.doFilter(request, httpServletResponse); } @Override public void init(FilterConfig arg0) throws ServletException { } }
看到這樣的解決方案,我真的是蠻崩潰的。不就一個跨域問題嘛,用得著這麼多程式碼嗎?
我對這樣的解決方案非常的不滿意。於是下定決心要好好的研究一番,大概花了半天的時間吧,我終於搞清楚了“跨域”問題,以及它的標準解決方案 CORS
。並且找到了一個極為簡潔的解決方案—— @CrossOrigin
,只要在Controller類上加上這個註解,就可以輕鬆地解決跨域問題。
程式碼如下。
@RestController @RequestMapping("course") @CrossOrigin public class CourseController { }
如果沒有找到 @CrossOrigin
這個註解,我真的就要按照同學提供的方案去解決跨域的問題了。但那樣做就好像,我們賣掉家裡的小汽車,然後出行的時候駕一輛馬車一樣。
這也正是我想告訴你的,為什麼要使用註解的原因:它讓我們的程式碼看起來更簡潔,更有時代的進步感。
02、該如何定義註解呢?
註解需要通過 @interface
關鍵字(形式和介面非常的相似,只是前面多了一個 @
)進行定義。我們可以開啟 @CrossOrigin
的原始碼來看一下。
@Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CrossOrigin { /** * List of allowed origins, e.g. {@code "http://domain1.com"}. * <p>These values are placed in the {@code Access-Control-Allow-Origin} * header of both the pre-flight response and the actual response. * {@code "*"} means that all origins are allowed. * <p>If undefined, all origins are allowed. * @see #value */ @AliasFor("value") String[] origins() default {}; /** * List of request headers that can be used during the actual request. * <p>This property controls the value of the pre-flight response's * {@code Access-Control-Allow-Headers} header. * {@code "*"}means that all headers requested by the client are allowed. * <p>If undefined, all requested headers are allowed. */ String[] allowedHeaders() default {}; /** * List of supported HTTP request methods, e.g. * {@code "{RequestMethod.GET, RequestMethod.POST}"}. * <p>Methods specified here override those specified via {@code RequestMapping}. * <p>If undefined, methods defined by {@link RequestMapping} annotation * are used. */ RequestMethod[] methods() default {}; }
從上面的程式碼可以看得出來,“註解”真的很“註解”,除了註釋多和“元註解”多之外,真沒有別的了。
“元註解”?什麼是“元註解”呢?
“元註解”是用來註解(動詞)註解(名詞)的註解(名詞)。請感受漢語的博大精深。 @Target
、 @Retention
和 @Documented
就是所謂的元註解。
1)@Target
Target是目標的意思, @Target
指定了註解運用的場景。都有哪些場景值呢?
ElementType.ANNOTATION_TYPE ElementType.CONSTRUCTOR ElementType.FIELD ElementType.LOCAL_VARIABLE ElementType.METHOD ElementType.PACKAGE ElementType.PARAMETER ElementType.TYPE
2)@Retention
Retention這個單詞的意思為保留期。也就是說,當 @Retention
應用到一個註解上的時候,它解釋說明了這個註解的存活時間。來看它的取值範圍。
RetentionPolicy.SOURCE RetentionPolicy.CLASS RetentionPolicy.RUNTIME
3)@Documented
Documented
就比較容易理解,它和文件有關。作用就是能夠將註解中的元素包含到 Javadoc 中。
當我們瞭解了元註解的概念後,再回頭看一下 @CrossOrigin
的原始碼,是不是感覺清晰多了呢?
如果能夠細緻地讀一讀原始碼中的註釋,你就會看到WebContextFilter類中出現的關鍵字,諸如 Access-Control-Allow-Origin
、 Access-Control-Allow-Headers
、 Access-Control-Allow-Methods
。也就是說,當我們通過 @CrossOrigin
對Controller類註解後,SpringMVC就能夠在執行時對這個類自動加上解決跨域問題的過濾器。
03、註解可以反射嗎?
註解是可以通過反射獲取的。
1)可以通過 Class 物件的 isAnnotationPresent()
方法判斷該類是否應用了某個指定的註解。
2)通過 getAnnotation()
方法來獲取註解物件。
3)當獲取到註解物件後,就可以獲取使用註解時定義的屬性值。
示例如下:
@CrossOrigin(origins = "http://qingmiaokeji.com", allowedHeaders = "accept,content-type", methods = { RequestMethod.GET, RequestMethod.POST }) public class TestController { public static void main(String[] args) { Class c = TestController.class; if (c.isAnnotationPresent(CrossOrigin.class)) { CrossOrigin crossOrigin = (CrossOrigin) c.getAnnotation(CrossOrigin.class); System.out.println(Arrays.asList(crossOrigin.allowedHeaders())); System.out.println(Arrays.asList(crossOrigin.methods())); System.out.println(Arrays.asList(crossOrigin.origins())); } } } // 輸出:[accept,content-type] // [GET, POST] // [http://qingmiaokeji.com]
04、註解經常用在哪裡呢?
1) @Transactional
:Spring 為事務管理提供的功能支援。
2) @ Service
:Spring在進行包掃描的時候,會自動將這個類註冊到Spring容器中。
3) @RestController
:是 @ResponseBody
和 @Controller
的組合註解。
也就是說,下面這段程式碼與下下面的程式碼等同。
@RestController public class HelloController { @RequestMapping(value="hello") public String sayHello(){ return "hello"; } }
@Controller @ResponseBody public class HelloController { @RequestMapping(value="hello") public String sayHello(){ return "hello"; } }
4) @RequestMapping
:Spring Web 應用程式中最常用到的註解之一,將 HTTP 請求對映到 MVC 和 REST 控制器的處理方法上。
5) @Select
:MyBatis提供的查詢語句註解。示例如下:
@Select("select * from city") List<City> getCitys();
6)還有很多很多,就不再一一列舉了。
最後
我想說的是,註解有許多用處,主要有:
- 提供資訊給編譯器: 編譯器可以利用註解來探測錯誤和警告資訊。
- 編譯階段時的處理: 軟體工具可以利用註解資訊來生成程式碼、HTML文件。
- 執行時的處理: 某些註解可以在程式執行的時候接受程式碼的提取。
別忘了: