溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java注解(Annotation):請(qǐng)不要小看我!

發(fā)布時(shí)間:2020-07-12 17:46:57 來源:網(wǎng)絡(luò) 閱讀:134 作者:沉默王二 欄目:編程語言

在 Java 中,并不是所有的類型信息都能在編譯階段明確,有一些類型信息需要在運(yùn)行時(shí)才能確定,這種機(jī)制被稱為 RTTI,英文全稱為 Run-Time Type Identification,即運(yùn)行時(shí)類型識(shí)別,有沒有一點(diǎn)“知行合一”的味道?運(yùn)行時(shí)類型識(shí)別主要由Class類實(shí)現(xiàn)。

Java注解是一系列元數(shù)據(jù),它提供數(shù)據(jù)用來解釋程序代碼,但是注解并非是所解釋的代碼本身的一部分。注解對(duì)于代碼的運(yùn)行效果沒有直接影響。

網(wǎng)絡(luò)上對(duì)注解的解釋過于嚴(yán)肅、刻板,這并不是我喜歡的風(fēng)格。盡管這樣的解釋聽起來非常的專業(yè)。

為了緩解大家對(duì)“注解”的陌生感,我來說點(diǎn)有意思的。其實(shí)我對(duì)“注解”這個(gè)詞的第一印象并不是Java的注解,而是朱熹的名作《四書章句集注》。為什么我會(huì)有這么大的腦洞呢?因?yàn)楫?dāng)我試著去翻譯Annotation這個(gè)單詞的時(shí)候,得到的結(jié)果是“注釋”而不是“注解”?!端臅戮浼ⅰ氛侵祆鋵?duì)《大學(xué)》、《中庸》、《論語》、《孟子》四書做出的重要的注釋。要知道,該書可是明清以后科舉考試的題庫和標(biāo)準(zhǔn)答案!

注解(Annotation)是在 Java SE 5.0 版本中開始引入的概念,同classinterface一樣,也屬于一種類型。很多開發(fā)人員認(rèn)為注解的地位不高,但其實(shí)不是這樣的。像@Transactional、@Service、@RestController、@RequestMapping、@CrossOrigin等等這些注解的使用頻率越來越高。

01、為什么要使用注解呢?

為什么要使用注解呢?讓我們從另外一個(gè)問題說起。

“跨域”這兩個(gè)字就像一塊狗皮膏藥黏在每一個(gè)前端開發(fā)者的身上;我也不例外,雖然我并不是一個(gè)純粹的前端開發(fā)者。

跨域問題的出現(xiàn),源于瀏覽器的同源策略——限制一個(gè)源加載的腳本去訪問另外一個(gè)源的資源,可有效地隔離潛在的惡意文件,是一種重要的安全機(jī)制。

Java注解(Annotation):請(qǐng)不要小看我!

跨域問題的解決方案也有很多,比如說:

1)JSONP

2)Nginx代理

3)"跨域資源共享"(Cross-origin resource sharing),簡(jiǎn)稱CORS,可以說是處理跨域問題的標(biāo)準(zhǔn)做法。

記得第一次遇到跨域問題的時(shí)候,我特意向一個(gè)同學(xué)請(qǐng)教了解決方案,他告訴我的答案如下。

第一步,在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>

第二步,實(shí)現(xiàn)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 {
    }

}

看到這樣的解決方案,我真的是蠻崩潰的。不就一個(gè)跨域問題嘛,用得著這么多代碼嗎?

我對(duì)這樣的解決方案非常的不滿意。于是下定決心要好好的研究一番,大概花了半天的時(shí)間吧,我終于搞清楚了“跨域”問題,以及它的標(biāo)準(zhǔn)解決方案CORS。并且找到了一個(gè)極為簡(jiǎn)潔的解決方案——@CrossOrigin,只要在Controller類上加上這個(gè)注解,就可以輕松地解決跨域問題。

代碼如下。

@RestController
@RequestMapping("course")
@CrossOrigin
public class CourseController {
}

如果沒有找到@CrossOrigin這個(gè)注解,我真的就要按照同學(xué)提供的方案去解決跨域的問題了。但那樣做就好像,我們賣掉家里的小汽車,然后出行的時(shí)候駕一輛馬車一樣。

這也正是我想告訴你的,為什么要使用注解的原因:它讓我們的代碼看起來更簡(jiǎn)潔,更有時(shí)代的進(jìn)步感。

02、該如何定義注解呢?

注解需要通過@interface關(guān)鍵字(形式和接口非常的相似,只是前面多了一個(gè)@)進(jìn)行定義。我們可以打開@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 {};
}

從上面的代碼可以看得出來,“注解”真的很“注解”,除了注釋多和“元注解”多之外,真沒有別的了。

“元注解”?什么是“元注解”呢?

“元注解”是用來注解(動(dòng)詞)注解(名詞)的注解(名詞)。請(qǐng)感受漢語的博大精深。@Target@Retention@Documented就是所謂的元注解。

1)@Target

Target是目標(biāo)的意思,@Target指定了注解運(yùn)用的場(chǎng)景。都有哪些場(chǎng)景值呢?

  • ElementType.ANNOTATION_TYPE:可以給注解進(jìn)行注解
  • ElementType.CONSTRUCTOR:可以給構(gòu)造方法進(jìn)行注解
  • ElementType.FIELD:可以給字段進(jìn)行注解
  • ElementType.LOCAL_VARIABLE:可以給局部變量進(jìn)行注解
  • ElementType.METHOD:可以給方法進(jìn)行注解
  • ElementType.PACKAGE:可以給包進(jìn)行注解
  • ElementType.PARAMETER:可以給方法內(nèi)的參數(shù)進(jìn)行注解
  • ElementType.TYPE:可以給類型進(jìn)行注解,比如類、接口和枚舉

2)@Retention

Retention這個(gè)單詞的意思為保留期。也就是說,當(dāng)@Retention應(yīng)用到一個(gè)注解上的時(shí)候,它解釋說明了這個(gè)注解的存活時(shí)間。來看它的取值范圍。

  • RetentionPolicy.SOURCE:注解只在源碼階段保留,在編譯器進(jìn)行編譯時(shí)它將被丟棄忽視。
  • RetentionPolicy.CLASS:注解只被保留到編譯進(jìn)行的時(shí)候,并不會(huì)被加載到 JVM 中。
  • RetentionPolicy.RUNTIME:注解可以保留到程序運(yùn)行的時(shí)候,它會(huì)被加載進(jìn)入到 JVM 中,所以在程序運(yùn)行時(shí)可以獲取到它們。

3)@Documented

Documented就比較容易理解,它和文檔有關(guān)。作用就是能夠?qū)⒆⒔庵械脑匕?Javadoc 中。

當(dāng)我們了解了元注解的概念后,再回頭看一下@CrossOrigin的源碼,是不是感覺清晰多了呢?

如果能夠細(xì)致地讀一讀源碼中的注釋,你就會(huì)看到WebContextFilter類中出現(xiàn)的關(guān)鍵字,諸如Access-Control-Allow-Origin、Access-Control-Allow-HeadersAccess-Control-Allow-Methods。也就是說,當(dāng)我們通過@CrossOrigin對(duì)Controller類注解后,SpringMVC就能夠在運(yùn)行時(shí)對(duì)這個(gè)類自動(dòng)加上解決跨域問題的過濾器。

03、注解可以反射嗎?

注解是可以通過反射獲取的。

1)可以通過 Class 對(duì)象的 isAnnotationPresent() 方法判斷該類是否應(yīng)用了某個(gè)指定的注解。

2)通過 getAnnotation() 方法來獲取注解對(duì)象。

3)當(dāng)獲取到注解對(duì)象后,就可以獲取使用注解時(shí)定義的屬性值。

示例如下:

@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、注解經(jīng)常用在哪里呢?

1)@Transactional:Spring 為事務(wù)管理提供的功能支持。

2)@ Service:Spring在進(jìn)行包掃描的時(shí)候,會(huì)自動(dòng)將這個(gè)類注冊(cè)到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 應(yīng)用程序中最常用到的注解之一,將 HTTP 請(qǐng)求映射到 MVC 和 REST 控制器的處理方法上。

5)@Select:MyBatis提供的查詢語句注解。示例如下:

@Select("select * from city")
List<City> getCitys();

6)還有很多很多,就不再一一列舉了。

05)最后

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

  • 提供信息給編譯器: 編譯器可以利用注解來探測(cè)錯(cuò)誤和警告信息。
  • 編譯階段時(shí)的處理: 軟件工具可以利用注解信息來生成代碼、HTML文檔。
  • 運(yùn)行時(shí)的處理: 某些注解可以在程序運(yùn)行的時(shí)候接受代碼的提取。

上一篇:Java枚舉:小小enum,優(yōu)雅而干凈

下一篇:Java I/O 入門篇

微信搜索「*沉默王×××免費(fèi)視頻**」獲取 500G 高質(zhì)量教學(xué)視頻(已分門別類)。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI