1. 程式人生 > >Java註解(Annotation):請不要小看我!

Java註解(Annotation):請不要小看我!

map 有意 rop turn 接口 string exceptio support nsh

Java註解是一系列元數據,它提供數據用來解釋程序代碼,但是註解並非是所解釋的代碼本身的一部分。註解對於代碼的運行效果沒有直接影響。

網絡上對註解的解釋過於嚴肅、刻板,這並不是我喜歡的風格。盡管這樣的解釋聽起來非常的專業。

為了緩解大家對“註解”的陌生感,我來說點有意思的。其實我對“註解”這個詞的第一印象並不是Java的註解,而是朱熹的名作《四書章句集註》。為什麽我會有這麽大的腦洞呢?因為當我試著去翻譯Annotation這個單詞的時候,得到的結果是“註釋”而不是“註解”。《四書章句集註》正是朱熹對《大學》、《中庸》、《論語》、《孟子》四書做出的重要的註釋。要知道,該書可是明清以後科舉考試的題庫和標準答案!

註解(Annotation)是在 Java SE 5.0 版本中開始引入的概念,同classinterface一樣,也屬於一種類型。很多開發人員認為註解的地位不高,但其實不是這樣的。像@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 {
HttpServletResponse httpServletResponse = (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:註解只被保留到編譯進行的時候,並不會被加載到 JVM 中。
  • RetentionPolicy.RUNTIME:註解可以保留到程序運行的時候,它會被加載進入到 JVM 中,所以在程序運行時可以獲取到它們。

3)@Documented

Documented就比較容易理解,它和文檔有關。作用就是能夠將註解中的元素包含到 Javadoc 中。

當我們了解了元註解的概念後,再回頭看一下@CrossOrigin的源碼,是不是感覺清晰多了呢?

如果能夠細致地讀一讀源碼中的註釋,你就會看到WebContextFilter類中出現的關鍵字,諸如Access-Control-Allow-OriginAccess-Control-Allow-HeadersAccess-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文檔。
  • 運行時的處理: 某些註解可以在程序運行的時候接受代碼的提取。

別忘了:

技術分享圖片

Java註解(Annotation):請不要小看我!