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实现权限控制