java学习之路02-java注解(Annotation)02-自定义注解


通过java注解的第一节学习,我们大致对java的内置注解有了一定的认识,本篇文章主要学习自定义注解。
上节: java学习之路01-java注解(Annotation)01-java内置注解.

注解的组成

通过上一节的学习,我们可以知道每个注解必备的两个组成:注解的作用范围(@Target)和注解的保留策略@Retention
通过查看元注解的源码可以看出每个注解都有1个保留策略1到多个作用范围构成

@Targe源码,其value对应的枚举为ElementType

@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();
}
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
}

@Retention源码,其value对应的枚举为RetentionPolicy

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}
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
}

这两个元注解对应枚举的含义上节元注解学习中已经学过,这里不再累赘

使用自定义注解

AnnotatedElement

在使用自定义注解前,我们先了解一下java.lang.reflect.AnnotatedElement这个接口。

先看一下该接口的方法,注解的处理是通过java反射来处理的:

default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
        return getAnnotation(annotationClass) != null;
 }

指定类型的注解出现在当前对象上,则返回true,否则将返回false。

<T extends Annotation> T getAnnotation(Class<T> annotationClass);

指定类型的注解出现在当前对象上,则返回对应的注解,否则将返回null。

Annotation[] getAnnotations();

返回直接出现在当前对象上的注解(不包括继承的注解),没有则返回空数组。

常用的Class, Method, Field等都实现了该接口,也就是说我们可以通过java 的反射拿到Class, Method, Field类等,就可以处理到注解在上面的注解了。

注意:只有注解的保留策略为RetentionPolicy.RUNTIME时,才可以通过反射获取注解并且做响应的处理。

基本用法

写两个个简单的demo学习下通过反射获取注解:

//定义自定义注解
//使用范围为类,方法,构造器
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR})
//保留策略为运行期间
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {

    String value() default "1";

}

@TestAnnotation(value = "3")
class Test {

    //如果注解的成员只有一个value,则可以省略前面的"参数名="
    @TestAnnotation("2")
    public Test() {
    }

    //如果注解的成员有默认值,则可以不用复制
    @TestAnnotation
    //保留策略为源码级别的注解
    @Override
    public int hashCode() {
        return 0;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        //通过获取指定注解的方式获取类上的注解
        TestAnnotation annotationClass = Test.class.getAnnotation(TestAnnotation.class);
        System.out.println(annotationClass.value());
        //通过获取注解数组的方式获取构造器上的注解
        Annotation[] annotationConstructors = Test.class.getConstructor().getAnnotations();
        for (Annotation annotation : annotationConstructors) {
            System.out.println(((TestAnnotation) annotation).value());
        }
        //通过获取注解数组的方式获取方法上的注解
        Annotation[] annotationMethods = Test.class.getMethod("hashCode").getAnnotations();
        if (annotationMethods.length > 1) {
            for (Annotation annotation : annotationMethods) {
                System.out.println(annotation);
            }
        } else {
            System.out.println(((TestAnnotation) annotationMethods[0]).value());
        }
    }

}

运行结果:

在这里插入图片描述

//定义自定义注解
//使用范围为类
@Target({ElementType.TYPE})
//保留策略为运行期间
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {

    String a() default "a";

    String b() default "b";

}

@TestAnnotation(a = "aa")
class Test {

    public static void main(String[] args) throws NoSuchMethodException {
        //通过获取指定注解的方式获取类上的注解
        TestAnnotation annotationClass = Test.class.getAnnotation(TestAnnotation.class);
        System.out.println(annotationClass.a());
        System.out.println(annotationClass.b());
    }

}

运行结果:

在这里插入图片描述

应用场景

在日常项目开发中,一般自定义注解配合AOP或者拦截器使用。
比如cookie拦截,接口权限控制,日志打印等,都可以通过自定义注解实现。

举个栗子:

  • cookie拦截:
    定义注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginAnnotation {

}

实现拦截器

	@Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o){

        //获取method
        HandlerMethod handlerMethod = (HandlerMethod)o;
        //获取method上的loginAnnotation注解
        LoginAnnotation loginAnnotation = handlerMethod.getMethod().getAnnotation(LoginAnnotation.class);

        if(loginAnnotation != null) {
            //如果有loginAnnotation注解,则说明需要cookie拦截
            if(//todo...cookie存在){
                //允许访问接口
                return true;
            }else {
                //未登录 则不允许访问
                return false;
            }
        }

        //没有loginAnnotation注解,则说明不需要cookie拦截(用户不需要登陆)
        return true;
    }

在登陆,注册等不需要登陆的url上,不加该注解,则不会进入校验cookie阶段;如果修改个人信息,查看信息等需要登陆的url上,加该注解,则会检验cookie,验证是否登陆。

  • 接口权限控制
    定义注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface JurisdictionAnnotation {

    //定义接口权限code
    String jurisdictionCode() default "0";

}

定义权限级别枚举

enum JurisdictionEnum {

    USER(0, "普通用户"),
    ADMIN(1, "管理员"),
    ;

    @Getter
    private Integer code;

    @Getter
    private String desc;

    JurisdictionEnum(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }
}

实现拦截器

	@Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o){

        //获取method
        HandlerMethod handlerMethod = (HandlerMethod)o;
        //获取method上的loginAnnotation注解
        JurisdictionAnnotation jurisdictionAnnotation = handlerMethod.getMethod().getAnnotation(JurisdictionAnnotation.class);

        if(jurisdictionAnnotation != null) {
            //如果有jurisdictionAnnotation注解,则说明需要判断权限
            //找出该接口需要的权限级别
            String code = jurisdictionAnnotation.jurisdictionCode();
            if(//todo...判断当前用户的权限级别是否与接口的权限级别一致){
                //允许访问接口
                return true;
            }else {
                //权限不足 则不允许访问
                return false;
            }
        }

        //没有jurisdictionAnnotation注解,则说明不需要权限控制
        return true;
    }
  • 日志打印
    定义注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnnotation {

    //定义打印日志的一些参数 比如日志级别,打印到指定目录等
    String logLevel() default "info";
    String logFileName() default "service";

}

声明切面

@Aspect
class Test {

    //该切点是切在LogAnnotation注解上的
    @Pointcut("@annotation(com.demo.testannotation.LogAnnotation)")
    public void logAspect() {
    }

    @Around("logAspect()")
    public void logAround(ProceedingJoinPoint joinPoint) {
        
        //todo...根据具体日志参数打印入参
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        //todo...根据具体日志参数打印出参

    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值