天天看點

Java注解(Annotation)的使用

Java注解是一系列中繼資料,它提供資料用來解釋程式代碼,但是注解并非是所解釋的代碼本身的一部分。注解對于代碼的運作效果沒有直接影響。

           

注解(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 {
        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 {
}
           

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-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();
           

最後

我想說的是,注解有許多用處,主要有:

提供資訊給編譯器:編譯器可以利用注解來探測錯誤和警告資訊。

編譯階段時的處理: 軟體工具可以利用注解資訊來生成代碼、HTML文檔。

運作時的處理: 某些注解可以在程式運作的時候接受代碼的提取。