Spring註解編程(一)---Java中的註解 原

Spring中的註解大概可以分爲兩大類:

1)spring的bean容器相關的註解,或者說bean工廠相關的註解;

2)springmvc相關的註解。

spring的bean容器相關的註解,先後有:@Required, @Autowired, @PostConstruct, @PreDestory,還有Spring3.0開始支持的JSR-330標準javax.inject.*中的註解(@Inject, @Named, @Qualifier, @Provider, @Scope, @Singleton).

springmvc相關的註解有:@Controller, @RequestMapping, @RequestParam, @ResponseBody等等。

要理解Spring中的註解,先要理解Java中的註解。

1. Java中的註解

 Java中1.5中開始引入註解,Java中引入的註解如下

@Override

我們最熟悉的應該是:@Override, 它的定義如下:

/**
 * Indicates that a method declaration is intended to override a
 * method declaration in a supertype. If a method is annotated with
 * this annotation type compilers are required to generate an error
 * message unless at least one of the following conditions hold:
 *
 * <ul><li>
 * The method does override or implement a method declared in a
 * supertype.
 * </li><li>
 * The method has a signature that is override-equivalent to that of
 * any public method declared in {@linkplain Object}.
 * </li></ul>
 *
 * @author  Peter von der Ah&eacute;
 * @author  Joshua Bloch
 * @jls 9.6.1.4 @Override
 * @since 1.5
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

從註釋,我們可以看出,@Override的作用是,提示編譯器,使用了@Override註解的方法必須override父類或者java.lang.Object中的一個同名方法。我們看到@Override的定義中使用到了 @Target, @Retention,它們就是所謂的“元註解”——就是定義註解的註解,或者說註解註解的註解(暈了...)。

@Retention

/**
 * Indicates how long annotations with the annotated type are to
 * be retained.  If no Retention annotation is present on
 * an annotation type declaration, the retention policy defaults to
 * {@code RetentionPolicy.CLASS}.
 *
 * <p>A Retention meta-annotation has effect only if the
 * meta-annotated type is used directly for annotation.  It has no
 * effect if the meta-annotated type is used as a member type in
 * another annotation type.
 *
 * @author  Joshua Bloch
 * @since 1.5
 * @jls 9.6.3.2 @Retention
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

@Retention用於提示註解被保留多長時間,有三種取值:

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被編譯器保留在編譯後的類文件級別,但是被虛擬機丟棄;
RetentionPolicy.RUNTIME保留至運行時,可以被反射讀取;

@Target:

/**
 * Indicates the contexts in which an annotation type is applicable. The
 * declaration contexts and type contexts in which an annotation type may be
 * applicable are specified in JLS 9.6.4.1, and denoted in source code by enum
 * constants of {@link ElementType java.lang.annotation.ElementType}.
 *
 * <p>If an {@code @Target} meta-annotation is not present on an annotation type
 * {@code T} , then an annotation of type {@code T} may be written as a
 * modifier for any declaration except a type parameter declaration.
 *
 * <p>If an {@code @Target} meta-annotation is present, the compiler will enforce
 * the usage restrictions indicated by {@code ElementType}
 * enum constants, in line with JLS 9.7.4.
 *
 * <p>For example, this {@code @Target} meta-annotation indicates that the
 * declared type is itself a meta-annotation type.  It can only be used on
 * annotation type declarations:
 * <pre>
 *    &#064;Target(ElementType.ANNOTATION_TYPE)
 *    public &#064;interface MetaAnnotationType {
 *        ...
 *    }
 * </pre>
 *
 * <p>This {@code @Target} meta-annotation indicates that the declared type is
 * intended solely for use as a member type in complex annotation type
 * declarations.  It cannot be used to annotate anything directly:
 * <pre>
 *    &#064;Target({})
 *    public &#064;interface MemberType {
 *        ...
 *    }
 * </pre>
 *
 * <p>It is a compile-time error for a single {@code ElementType} constant to
 * appear more than once in an {@code @Target} annotation.  For example, the
 * following {@code @Target} meta-annotation is illegal:
 * <pre>
 *    &#064;Target({ElementType.FIELD, ElementType.METHOD, ElementType.FIELD})
 *    public &#064;interface Bogus {
 *        ...
 *    }
 * </pre>
 *
 * @since 1.5
 * @jls 9.6.4.1 @Target
 * @jls 9.7.4 Where Annotations May Appear
 */
@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();
}

 @Target用於提示該註解使用的地方,取值有:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    //類,接口,註解,enum
    TYPE,

    /** Field declaration (includes enum constants) */
    //屬性域
    FIELD,

    /** Method declaration */
    //方法
    METHOD,

    /** Formal parameter declaration */
    //參數,1.8後用TYPE_PARAMETER
    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
}

所以:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

表示 @Override 只能使用在方法上,保留在源碼級別,被編譯器處理,然後拋棄掉。

還有一個經常使用的元註解 @Documented :

/**
 * Indicates that annotations with a type are to be documented by javadoc
 * and similar tools by default.  This type should be used to annotate the
 * declarations of types whose annotations affect the use of annotated
 * elements by their clients.  If a type declaration is annotated with
 * Documented, its annotations become part of the public API
 * of the annotated elements.
 *
 * @author  Joshua Bloch
 * @since 1.5
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

表示註解是否能被 javadoc 處理並保留在文檔中。

@Inherited

/**
 * Indicates that an annotation type is automatically inherited.  If
 * an Inherited meta-annotation is present on an annotation type
 * declaration, and the user queries the annotation type on a class
 * declaration, and the class declaration has no annotation for this type,
 * then the class's superclass will automatically be queried for the
 * annotation type.  This process will be repeated until an annotation for this
 * type is found, or the top of the class hierarchy (Object)
 * is reached.  If no superclass has an annotation for this type, then
 * the query will indicate that the class in question has no such annotation.
 *
 * <p>Note that this meta-annotation type has no effect if the annotated
 * type is used to annotate anything other than a class.  Note also
 * that this meta-annotation only causes annotations to be inherited
 * from superclasses; annotations on implemented interfaces have no
 * effect.
 *
 * @author  Joshua Bloch
 * @since 1.5
 * @jls 9.6.3.3 @Inherited
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

指示註解類型被自動繼承。如果在註解類型聲明中存在 Inherited 元註解,並且用戶在某一類聲明中查詢該註解類型,同時該類聲明中沒有此類型的註解,則將在該類的超類中自動查詢該註解類型。此過程會重複進行,直到找到此類型的註解或到達了該類層次結構的頂層 (Object) 爲止。如果沒有超類具有該類型的註解,則查詢將指示當前類沒有這樣的註解。 

    注意,如果使用註解類型註解類以外的任何事物,此元註解類型都是無效的。還要注意,此元註解僅促成從超類繼承註解;對已實現接口的註解無效。 

    即一個類中,沒有@Father的註解,但是這個類的父類有@Father註解,且@Father註解被@Inherited註解,則在使用反射獲取子類@Father註解時,是可以獲取到父類的@Father註解的。如果一個使用了@Inherited修飾的annotation類型被用於一個class,則這個annotation將被用於該class的子類。

@native

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Native {
}

僅僅用來標記native的屬性,只對屬性有效,且只在代碼中使用,一般用於給IDE工具做提示用。

@Repeatable

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    /**
     * Indicates the <em>containing annotation type</em> for the
     * repeatable annotation type.
     * @return the containing annotation type
     */
    Class<? extends Annotation> value();
}

可重複註解的註解,允許在同一申明類型(類,屬性,或方法)的多次使用同一個註解。

2.Annotation

所有註解默認都實現了這個接口,實現是由編譯器完成的,編寫自己的接口的方法:

使用@interface自定義註解時,自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其他細節。在定義註解時,不能繼承其他的註解或接口。@interface用來聲明一個註解,其中的每一個方法實際上是聲明瞭一個配置參數。方法的名稱就是參數的名稱,返回值類型就是參數的類型(返回值類型只能是基本類型、Class、String、enum)。可以通過default來聲明參數的默認值。同時value屬性是一個註解的默認屬性,只有value屬性時是可以不顯示賦值的。

  定義註解格式:

  public @interface 註解名 {定義體}

  使用註解格式:

  @註解名(key=value, key=value)

  註解參數的可支持數據類型: 

  1.所有基本數據類型(int,float,boolean,byte,double,char,long,short)
  2.String類型
  3.Class類型
  4.enum類型
  5.Annotation類型
  6.以上所有類型的數組

  Annotation類型裏面的參數該怎麼設定: 
  第一,只能用public或默認(default)這兩個訪問權修飾.例如,String value();這裏把方法設爲defaul默認類型;   
  第二,參數成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數據類型和 String,Enum,Class,Annotation等數據類型,以及這一些類型的數組.例如,String value();這裏的參數成員就爲String;數組類型類似於String[] value();
  第三,如果只有一個參數成員,最好把參數名稱設爲"value",後加小括號.或者只有一個參數沒有默認值,其他都有,也可以把這個參數名稱設爲"value",這樣使用註解時就不用顯式聲明屬性了。

  第四,如果一個參數成員類型爲數組,如果 String[] array();傳值方式爲array={"a","b"},若只有一個值,則可以直接令array="a",會自動生成一個只包含a的數組。若沒有值,則array={}。都是可以的。 

  註解元素的默認值:
    註解元素必須有確定的值,要麼在定義註解的默認值中指定,要麼在使用註解時指定,非基本類型的註解元素的值不可爲null。因此, 使用空字符串或0作爲默認值是一種常用的做法。這個約束使得處理器很難表現一個元素的存在或缺失的狀態,因爲每個註解的聲明中,所有元素都存在,並且都具有相應的值,爲了繞開這個約束,我們只能定義一些特殊的值,例如空字符串或者負數,一次表示某個元素不存在,在定義註解時,這已經成爲一個習慣用法。

  Annotation接口中方法:

  Class<? extends Annotation> annotationType()   返回此 annotation 的註解類型。

  boolean equals(Object obj)    如果指定的對象表示在邏輯上等效於此接口的註解,則返回 true。

  String toString()  返回此 annotation 的字符串表示形式。

  所有Annotation類中的Class<?> getClass()。

3. 使用 元註解 來自定義註解 和 處理自定義註解

有了元註解,那麼我就可以使用它來自定義我們需要的註解。結合自定義註解和AOP或者過濾器,是一種十分強大的武器。比如可以使用註解來實現控制用戶重複數據提交。下面是一個關於重複數據提交驗證註解的實現:

/**
 *
 * 一個用戶 相同url 同時提交 相同數據 驗證
 * @author lpf
 * @create 2018-07-09 10:33
 **/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SameUrlData {
}

我們自定義了一個註解 @SameUrlData , 可以被用於 方法  上,註解一直保留到運行期,可以被反射讀取到。該註解的含義是:被 @SameUrlData 註解的方法,不允許用戶重複提交數據。下面就是對註解進行處理了:

/**
 * 一個用戶 相同url 同時提交 相同數據 驗證
 * 主要通過 session中保存到的url 和 請求參數。
 * 如果和上次相同,則是重複提交表單
 *
 * @author lpf
 * @create 2018-07-09 10:35
 **/
public class SameUrlDataInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            SameUrlData annotation = method.getAnnotation(SameUrlData.class);
            if (annotation != null) {
                if (repeatDataValidator(request)) {//如果重複相同數據
                    PrintWriter p = response.getWriter();
                    p.write("不能重複提交數據");
                    p.flush();
                    p.close();
                    return false;
                }
                else{
                    return true;
                }
            }
            return true;
        } else {
            return super.preHandle(request, response, handler);
        }
    }

    /**
     * 驗證同一個url數據是否相同提交  ,相同返回true
     *
     * @param httpServletRequest
     * @return
     */
    public boolean repeatDataValidator(HttpServletRequest httpServletRequest) {
        String params = JsonMapper.toJsonString(httpServletRequest.getParameterMap());
        String url = httpServletRequest.getRequestURI();
        Map<String, String> map = new HashMap<String, String>();
        map.put(url, params);
        String nowUrlParams = map.toString();//

        //上一次請求時間
        Object preRequestTime = httpServletRequest.getSession().getAttribute("preRequestTime");
        Object preUrlParams = httpServletRequest.getSession().getAttribute("repeatData");
        if (preUrlParams == null) {//如果上一個數據爲null,表示還沒有訪問頁面
            httpServletRequest.getSession().setAttribute("repeatData", nowUrlParams);
            httpServletRequest.getSession().setAttribute("preRequestTime", new Date().getTime());
            return false;
        } else {//否則,已經訪問過頁面
            Date thisDate = new Date();
            // 超過30秒鐘
            if(thisDate.getTime() - (Long) preRequestTime>30*1000){
                httpServletRequest.getSession().setAttribute("preRequestTime", thisDate.getTime());
                return false;
            }else{
                if (preUrlParams.toString().equals(nowUrlParams)) {//如果上次url+數據和本次url+數據相同,則表示城府添加數據
                    return true;
                } else {//如果上次 url+數據 和本次url加數據不同,則不是重複提交
                    httpServletRequest.getSession().setAttribute("repeatData", nowUrlParams);
                    httpServletRequest.getSession().setAttribute("preRequestTime", thisDate.getTime());
                    return false;
                }
            }


        }
    }

}

上面我們定義了一個攔截器,首先使用反射來判斷方法上是否被 @SameUrlData 註解:

HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
SameUrlData annotation = method.getAnnotation(SameUrlData.class);

這是一個簡單的使用 註解 和 過濾器 來進行重複數據提交驗證的例子。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章