Java自定义注解

Java自定义注解

Java自定义注解一般使用场景为:自定义注解+拦截器或者AOP配合使用,可以用来设计自己的框架,也可以用于开发中的权限校验

一、什么是注解(Annotation)

Java注解是什么,以下是引用自维基百科的内容

Java注解又称Java标注,是JDK5.0版本开始支持加入源代码的特殊语法元数据。ava语言中的类、方法、变量、参数和包等都可以被标注。和Javadoc不同,Java标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java虚拟机可以保留标注内容,在运行时可以获取到标注内容。 当然它也支持自定义Java标注。

二、注解体系图

元注解:java.lang.annotation中提供了元注解,可以使用这些注解来定义自己的注解。主要使用的是Target和Retention注解

可以通过java.lang.reflect.AnnotationElement接口来获取,注解的获取都是通过反射来处理的。如下反射相关的类都实现了AnnotationElement接口

反射处理:

AnnotationElement接口方法:

因此,只要我们通过反射拿到Class, Method, Field类,就能够通过getAnnotation(Class)拿到我们想要的注解并取值。

三、常用元注解

Target:描述了注解修饰的对象范围,取值在java.lang.annotation.ElementType定义,常用包括:

  • METHOD:用于描述方法

  • PACKAGE:用于描述包

  • PARAMETER:用于描述方法变量

  • TYPE:用于描述类、接口或enum类型

Retention:表述注解保留时间的长短,取值在java.lang.annotation.RetentionPolicy中,取值:

  • SOURCE:在源文件中有效,编译过程中会被忽略
  • CLASS:随源文件一起编译在class文件中,运行时忽略
  • RUNTIME:在运行时有效

只有定义为RetentionPolicy.RUNTIME时,我们才能通过注解反射获取注解

四、注解属性定义

注解属性 ( 接口方法 ) 返回值类型要求 :

  • 基本数据类型 : byte , short , int , long , float , double , char , boolean ;

  • 字符串类型 : String ;

  • 枚举类型 : enum ;

  • 注解类型 ;

  • 以上类型的数组形式 ;

可以使用default来进行赋默认值

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AdminLoginToken {
    @AliasFor("admin")
    boolean value() default true;

    @AliasFor("value")
    boolean admin() default true;
}

四、示例-反射获取注解

定义一个注解:

@Target(ElementType.FIELD)//只能定义在字段上
@Retention(RetentionPolicy.RUNTIME)//在运行时有效
public @interface MyAnnotation {
    //字段描述
    String description();
    //字段长度
    int length();
}

注解的使用:

@Data
public class User {
    @MyAnnotation(length = 12,description = "用户名的长度只能为12位")
    private String userName;
}

通过反射获取到注解:

public void test1(){
    User user = new User();
    user.setUserName("张三");

    //获取类模板
    Class<User> userClass = User.class;

    //获取所有字段
    Field[] declaredFields = userClass.getDeclaredFields();
    for (Field field : declaredFields) {
        //判断该字段是否存在MyAnnotation注解
        if(field.isAnnotationPresent(MyAnnotation.class)){
            MyAnnotation myAnnotation = field.getAnnotation(MyAnnotation.class);
            System.out.println("字段:["+field.getName()+"],描述:["+myAnnotation.description()+"],长度:["+myAnnotation.length()+"]");
            }
        }
    }

运行结果

应用场景一:基于Spring的自定义注解+拦截器 实现登录权限校验

我们在实际开发中,可以使用这种方式来进行是否需要进行登录校验;如果方法中加上了@LoginRequired注解表示方法需要登录校验,如果没加则不需要。

定义一个@LoginRequired注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public@interface LoginRequired {
    
}

定义两个简单接口,其中一个添加@LoginRequired注解表示需要登录校验

@RestController
public class UserController {

    @GetMapping("/login1")
    public TransDTO login1(){
        return new TransDTO<>().withMessage("访问login1成功").withCode(HttpStatus.OK.value());
    }
    
    @LoginRequired
    @GetMapping("/login2")
    public TransDTO login2(){
        return new TransDTO<>().withMessage("访问login2成功").withCode(HttpStatus.OK.value());
    }
}

自定义拦截器

@Configuration
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("访问了过滤器!");
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        LoginRequired annotation = handlerMethod.getMethod().getAnnotation(LoginRequired.class);
        if(annotation != null){
            //全局异常处理会进行处理
            throw new BusinessException("访问失败,您没有权限访问!");
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

配置拦截路径

@Configuration
public class InterceptorTrainConfigurer implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
    }
}

全局异常处理

@RestControllerAdvice
public class MyExceptionAdvice {
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.OK)
    public TransDTO handleException(HttpServletRequest request,Exception e){
        e.printStackTrace();
        return new TransDTO().withCode(500).withSuccess(false).withMessage(e.getMessage());
    }
}

基本交互对象

@Data
public class TransDTO<T> {

    private Boolean success = true;
    private T data;
    private String message;
    private int code;

    public TransDTO() {
    }

    public int getCode() {
        return this.code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public TransDTO<T> withCode(int code) {
        this.code = code;
        return this;
    }

    public Boolean getSuccess() {
        return this.success;
    }

    public void setSuccess(Boolean success) {
        this.success = success;
    }

    public T getData() {
        return this.data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getMessage() {
        return this.message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public TransDTO<T> withSuccess(Boolean success) {
        this.success = success;
        return this;
    }

    public TransDTO<T> withMessage(String message) {
        this.message = message;
        return this;
    }

    public TransDTO<T> withData(T data) {
        this.data = data;
        return this;
    }
}

访问结果


我们还能使用自定义注解+AOP进行权限的控制,为了保证博客长度,可以访问自定义注解+AOP实现权限控制

posted @ 2021-06-28 15:20  熬夜总冠军  阅读(3088)  评论(0编辑  收藏  举报