黑马程序员技术交流社区
标题: java自定义注解的使用 [打印本页]
作者: 专注的一批 时间: 2019-10-23 11:38
标题: java自定义注解的使用
一、定义
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;
}
}
}
}
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) |
黑马程序员IT技术论坛 X3.2 |