接下來研究一下annotation
在開始看spring的註解以前,我想了想PHP註解及其處理方法,PHP不支持註解語法,只能用註釋來模擬,而PHP的反射類是能夠拿到類和方法以及屬性的註釋信息,再經過解析註釋信息拿到相應的註解。解析出註解以後,調用對應的註解處理程序。這種方式的弊端我想應該是沒法在PHP編譯階段發現錯誤,通常要到運行時,解析註解的階段才能發現錯誤;還有就是沒有統一的註解格式,徹底要看解析註解的邏輯是什麼樣的,形成碎片化,增長學習成本。java
我想spring也應該是同樣的道理,經過反射拿到註解信息,再調用對應的註解處理程序。但JAVA的優點是原生支持註解且格式統一,因此若是有語法錯誤,編譯時就能發現。web
在學習一個新概念以前,老是要先想一想,爲何須要它,它解決了什麼問題?註解也同樣,爲何須要註解,註解解決了什麼問題。spring
我嘗試用本身的理解去說一下,不必定準確,僅供參考。註解提供了一種非侵入式的,賦予類、方法或屬性能力的能力。你只須要添加幾種註解,就能夠爲程序提供強大的能力。而當你不須要它時,只須要刪除相應的註解便可,一切就如同春雨,潤物細無聲。express
想明白了這些,我去看看上個章節DemoApplication.java
,看看它用到了哪些註解。apache
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @RestController public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @GetMapping("/hello") public String hello(@RequestParam(value = "name", defaultValue = "World") String name) { return String.format("Hello %s!", name); } }
用到了4個註解,到目前爲止,我對JAVA如何定義註解一竅不通,但我徹底能夠合理地猜想它們的做用是什麼。@SpringBootApplication
,讓這個類成爲SpringBootApplication
的子類。@RestController
,讓這個類成爲Restful控制器。@GetMapping
,設置一條路由,當系統匹配到這條路由時,轉發給DemoApplication的hello方法進行處理。@RequestParam
,規定好接收參數的KEY和默認值。app
固然,這只是個人猜想,接下來,我要更加深刻到源代碼,看看註解的定義是什麼樣的,ctrl+click看看@RestController
註解。less
/* * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.web.bind.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; import org.springframework.stereotype.Controller; /** * A convenience annotation that is itself annotated with * {@link Controller @Controller} and {@link ResponseBody @ResponseBody}. * <p> * Types that carry this annotation are treated as controllers where * {@link RequestMapping @RequestMapping} methods assume * {@link ResponseBody @ResponseBody} semantics by default. * * <p><b>NOTE:</b> {@code @RestController} is processed if an appropriate * {@code HandlerMapping}-{@code HandlerAdapter} pair is configured such as the * {@code RequestMappingHandlerMapping}-{@code RequestMappingHandlerAdapter} * pair which are the default in the MVC Java config and the MVC namespace. * * @author Rossen Stoyanchev * @author Sam Brannen * @since 4.0 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Controller @ResponseBody public @interface RestController { /** * The value may indicate a suggestion for a logical component name, * to be turned into a Spring bean in case of an autodetected component. * @return the suggested component name, if any (or empty String otherwise) * @since 4.0.1 */ @AliasFor(annotation = Controller.class) String value() default ""; }
很清晰,定義註解的方法爲:ide
public @interface 註解名 { /* */ }
Target
註解,用來定義修飾範圍工具
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { /** * Returns an array of the kinds of elements an annotation type * can be applied to. * @return an array of the kinds of elements an annotation type * can be applied to */ ElementType[] value(); }
再深刻看看ElementType
,註釋把註解的範圍已經描述得很是清楚了。性能
public enum ElementType { /** Class, interface (including annotation type), or enum declaration */ TYPE, /** Field declaration (includes enum constants) */ FIELD, /** Method declaration */ METHOD, /** Formal parameter declaration */ PARAMETER, /** Constructor declaration */ CONSTRUCTOR, /** Local variable declaration */ LOCAL_VARIABLE, /** Annotation type declaration */ ANNOTATION_TYPE, /** Package declaration */ PACKAGE, /** * Type parameter declaration * * @since 1.8 */ TYPE_PARAMETER, /** * Use of a type * * @since 1.8 */ TYPE_USE, /** * Module declaration. * * @since 9 */ MODULE }
一個一個來看:Element.Type
,用來修飾類、接口(包括註解)、枚舉類型。Element.FIELD
,用來修飾類屬性,包括枚舉常量。Element.METHOD
,用來修飾類方法Element.PARAMETER
,用來修飾方法的形參Element.CONSTRUCTOR
,用來修飾構造方法Element.LOCAL_VARIABLE
,用來修飾本地變量Element.ANNOTATION_TYPE
,用來修飾註解Element.PACKAGE
,用來修飾包Element.TYPE_PARAMETER
,用來修飾類型參數,就是Class<@MyAnnotation T>
Element.TYPE_USE
,用來修飾類型,這能夠修飾任意類型,包括上面Element.TYPE_PARAMETER能修飾的,它均可以修飾Element.MODULE
,用來修飾模塊
除了Element.TYPE_PARAMETER
和Element.TYPE_USE
不能一眼看出來是修飾啥,其它的都很好理解。
看完了這些,咱們再返回頭看Target
註解,這個註解的@Target
,也就是修飾範圍,就是註解,這是個修飾註解的註解。
再看另外一個註解Retention
,這也是個修飾註解的註解,再着重看一下RetentionPolicy
.
public enum RetentionPolicy { /** * Annotations are to be discarded by the compiler. */ SOURCE, /** * Annotations are to be recorded in the class file by the compiler * but need not be retained by the VM at run time. This is the default * behavior. */ CLASS, /** * Annotations are to be recorded in the class file by the compiler and * retained by the VM at run time, so they may be read reflectively. * * @see java.lang.reflect.AnnotatedElement */ RUNTIME }
一個一個看RetentionPolicy.SOURCE
,這類註解會被編譯器丟掉,是@Override
這類註解的保留策略RetentionPolicy.CLASS
,這類註解會被編譯器記錄下來,可是在虛擬機運行階段是不會保留的,這也是Retention
註解的默認行爲RetentionPolicy.RUNTIME
,這類註解會一直保留,因此可使用反射類讀取信息
看懂了這兩個註解,再去一一分析其它註解,問題就不大。接下來就是分析一下註解處理器是怎麼工做的,以SpringCacheAnnotationParser
爲例。
@Nullable private Collection<CacheOperation> parseCacheAnnotations( DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) { Collection<? extends Annotation> anns = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) : AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS)); if (anns.isEmpty()) { return null; } final Collection<CacheOperation> ops = new ArrayList<>(1); anns.stream().filter(ann -> ann instanceof Cacheable).forEach( ann -> ops.add(parseCacheableAnnotation(ae, cachingConfig, (Cacheable) ann))); anns.stream().filter(ann -> ann instanceof CacheEvict).forEach( ann -> ops.add(parseEvictAnnotation(ae, cachingConfig, (CacheEvict) ann))); anns.stream().filter(ann -> ann instanceof CachePut).forEach( ann -> ops.add(parsePutAnnotation(ae, cachingConfig, (CachePut) ann))); anns.stream().filter(ann -> ann instanceof Caching).forEach( ann -> parseCachingAnnotation(ae, cachingConfig, (Caching) ann, ops)); return ops; }
經過工具類獲取Cacheable
註解的修飾對象,作一些基本的判斷,再迭代進行分類處理。
上面的語法我也不太熟悉,但大體仍是能看出來什麼意思,這裏要感嘆一聲,命名真的是相當重要,寫得好的代碼讀起來就跟讀英文似的。筆者第一次看見Collection<? extends Annotation>
就能秒懂什麼意思,好的代碼自帶教程,看源碼就能收穫良多。
下一章,我將會去大體地瀏覽一下Spring官方文檔,把一些示例跑起來,再仔細地分析代碼,順便熟悉JAVA的語法。