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("张三"); //获取类模板 ClassuserClass = 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{ 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 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 withSuccess(Boolean success) { this.success = success; return this; } public TransDTO withMessage(String message) { this.message = message; return this; } public TransDTO withData(T data) { this.data = data; return this; } }
访问结果
我们还能使用自定义注解+AOP进行权限的控制,为了保证博客长度,可以访问自定义注解+AOP实现权限控制