一、定义 Java文件叫做Annotation,用@interface表示。 java中提供了四种元注解,用于创建新的注解,分别是:@Retention、@Target、@Document、@Inherited 二、元注解 @Retention 从源代码中可以看出,主要用于提示注解要保留多长时间 package java.lang.annotation; /** * 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(); } 有三种取值: RetentionPolicy.SOURCE 将会被编译器抛弃 RetentionPolicy.CLASS 注解会被编辑器保留在类文件中,但是会被vm抛弃 RetentionPolicy.RUNTIME 注解会被编辑器保留在类文件中,也会被vm保留,所以可以通过反射读取。 package java.lang.annotation; /** * Annotation retention policy. The constants of this enumerated type * describe the various policies for retaining annotations. They are used * in conjunction with the {@link Retention} meta-annotation type to specify * how long annotations are to be retained. * * @author Joshua Bloch * @since 1.5 */ 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 } @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的取值有下面十种: ElementType.TYPE 用于类,接口(包括注解)或者枚举类型 ElementType.FIELD 用于属性字段包括枚举常量 ElementType.METHOD 用于方法级别 ElementType.PARAMETER 用于参数声明 ElementType.CONSTRUCTOR 用于构造函数声明 ElementType.LOCAL_VARIABLE 用于局部变量声明 ElementType.ANNOTATION_TYPE 用于注解类型声明 ElementType.PACKAGE 用于包声明 ElementType.TYPE_PARAMETER 用于泛型声明 ElementType.TYPE_USE 用于任意类型声明 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 } @Documented 将注解包含在Javadoc中 package java.lang.annotation; /** * 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 { } @Inherited 允许子类继承父类 package java.lang.annotation; /** * 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 { } 三、自定义注解的使用 创建一个自定义注解 import java.lang.annotation.*; /** * Created Date: 2019/3/1 * 创建自定义注解 */ @Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TestAnnotation { String value(); } 通过反射获取注解 public class Test { @TestAnnotation(value = "测试方法") public static void main(String args[]){ try { Class c=Test.class; Method[] methods=c.getDeclaredMethods(); for(Method method:methods){ Annotation[] annotations=method.getDeclaredAnnotations(); for(Annotation annotation:annotations){ TestAnnotation testAnnotation= (TestAnnotation) annotation; System.out.println(testAnnotation.value()); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } } } 四、web开发中的运用 在web开发中,权限控制非常重要,所以有些接口会限制必须登录之后才能访问,但是个别接口并没有这种限制。一种方式是把需要过滤的接口或者方法配置在文件中,每次请求时在拦截器中根据请求的路径与配置文件中的对比过滤。其实还有另外一种方式就是通过注解方式。 定义一个注解NoLogin @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface NoLogin { } 标注在方法上 在拦截器中判断方法上是否有NoLogin注解 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception { //支持两种方式过滤 1、注解方式 添加@NoLogin注解 HandlerMethod handlerMethod= (HandlerMethod) o; NoLogin noLogin=handlerMethod.getMethod().getDeclaredAnnotation(NoLogin.class); if(null!=noLogin){ return true; } } 五、java内置的注解 除了上述的四个元注解,java还内置了另外三个注解: @Override 它没有任何的属性,不能存储任何其他信息。它只能作用于方法之上,编译结束后将被丢弃。在java编译器编译成字节码的时候,一旦发现某个方法被这个注解标识过,就会匹配父类中是否存在同一方法,如果不存在就回编译失败。 @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { } @Deprecated 弃用的注解 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { } @SuppressWarnings 压制警告,比如某段代码中存在过时的方法,那么在编译过程中,会有warn警告,如果不想出现类似的警告,可在方法上添加这个注解。这个注解有一个value的值,这个value表示需要外汇返佣http://www.kaifx.cn/压制的警告类型。 @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { /** * The set of warnings that are to be suppressed by the compiler in the * annotated element. Duplicate names are permitted. The second and * successive occurrences of a name are ignored. The presence of * unrecognized warning names is <i>not</i> an error: Compilers must * ignore any warning names they do not recognize. They are, however, * free to emit a warning if an annotation contains an unrecognized * warning name. * * <p> The string {@code "unchecked"} is used to suppress * unchecked warnings. Compiler vendors should document the * additional warning names they support in conjunction with this * annotation type. They are encouraged to cooperate to ensure * that the same names work across multiple compilers. * @return the set of warnings to be suppressed */ String[] value(); } 六、注解的原理 1、java.lang.annotation.Annotation中有这么一句话:The common interface extended by all annotation types 所有的注解都继承于这个接口。怎么理解呢?其实刚才上面例子中的注解可以理解为: public @interface TestAnnotation extends Annotation{ } 注解的本质就是一个继承了 Annotation 接口的接口 为了方便理解和掌握注解,还是以刚才的TestAnnotation注解为例。在idea中配置启动参数,方便查看代理产生的类,参数如下: Dsun.misc.ProxyGenerator.saveGeneratedFiles=true main方法运行结束后,会在/com/sun/proxy目录下生成一个代理类,反编译之后是这样的: 代理类proxy1重写了TestAnnotation的所有方法,包括value()和从Annotation继承来的equals()、hashCode()等方法。 package com.sun.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import org.fy.annotation.TestAnnotation; public final class $Proxy1 extends Proxy implements TestAnnotation { private static Method m1; private static Method m2; private static Method m4; private static Method m0; private static Method m3; public $Proxy1(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final Class annotationType() throws { try { return (Class)super.h.invoke(this, m4, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String value() throws { try { return (String)super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m4 = Class.forName("org.fy.annotation.TestAnnotation").getMethod("annotationType"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); m3 = Class.forName("org.fy.annotation.TestAnnotation").getMethod("value"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } } 在看proxy1构造函数中有一个接口InvocationHandler,这个接口的实例化对象又是谁? java中有一个专门用于注解类型的代理对象AnnotationInvocationHandler,位于sun.reflect.annotation包中。同样是invoke方法用于处理具体的业务。 public Object invoke(Object var1, Method var2, Object[] var3) { String var4 = var2.getName(); Class[] var5 = var2.getParameterTypes(); if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) { return this.equalsImpl(var3[0]); } else if (var5.length != 0) { throw new AssertionError("Too many parameters for an annotation method"); } else { byte var7 = -1; switch(var4.hashCode()) { case -1776922004: if (var4.equals("toString")) { var7 = 0; } break; case 147696667: if (var4.equals("hashCode")) { var7 = 1; } break; case 1444986633: if (var4.equals("annotationType")) { var7 = 2; } } switch(var7) { case 0: return this.toStringImpl(); case 1: return this.hashCodeImpl(); case 2: return this.type; default: Object var6 = this.memberValues.get(var4); if (var6 == null) { throw new IncompleteAnnotationException(this.type, var4); } else if (var6 instanceof ExceptionProxy) { throw ((ExceptionProxy)var6).generateException(); } else { if (var6.getClass().isArray() && Array.getLength(var6) != 0) { var6 = this.cloneArray(var6); } return var6; } } } }
|