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中引入的注解如下

Spring注解编程(一)---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);

这是一个简单的使用 注解 和 过滤器 来进行重复数据提交验证的例子。

转载于:https://my.oschina.net/u/3737136/blog/1924720