注解定义
Java注解是使用在类、方法、变量、参数和包等地方的一种特殊注释,可以被编译器打包进class文件,它可以通过反射的方式获取到注解的的内容,利用获取的内容进行一些校验,如参数必填的校验,还可以用在拦截器中,实现全局的权限校验,也可以结合AOP进行日志添加等功能。
我们常见的注解:
注解在类上:@Controller 、@Service、@Component
注解在方法上:@Override、@RequestMapping
注解在参数上:@RequestBody、@RequestParam、 @PathVariable
自定义注解
Java用@interface来定义注解,在注解上添加@Target和@Retention,添加一些参数,最常用的参数应当命名为value,最好设定一个默认值。
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckLogin {
boolean LoginFlag() default true;
String value() default “info”;
}
元注解
有一些注解可以修饰其他注解,这部分注解是Java标准库已经定义好的,不需要手动编写,可以直接使用的注解,就称为元注解。
这些元注解存在于java.lang.Annotation包中,如下图:
元注解以下几种:
@Target
@Retention
@Documented
@Repeatable
@Inherited
其中@Target和@Retention是定义注解时必须的,其他可以选择使用。
@Target
描述了注解修饰的对象范围,可以使用在类、方法、字段、接口等,
类型为ElementType的枚举值:
- TYPE:类、接口或者枚举值
- FIELD:字段
- METHOD:方法
- PARAMETER:参数
- CONSTRUCTOR:构造方法
- ANNOTATION_TYPE:注解类型
如果注解在类上,用@Target(ElementType.TYPE)
如果注解在类和方法上,用@Target({ElementType.TYPE,ElementType.METHOD})
如果注解在方法和字段,用@Target({ElementType.METHOD,ElementType.FIELD})
Target的参数是ElementType[]数组,因此可以根据需要组合使用。
@Retention
表示注解的生命周期,取值为RetentionPolicy的枚举值:
- SOURCE:编译期存在
- CLASS:保存在class文件
- RUNTIME:运行时,可以通过反射读取
我们定义为@Retention(RetentionPolicy.RUNTIME),可通过反射获取。
如果Retention不存在,默认SOURCE;
获取注解:
注解定义后也是一种Class,所有的注解都继承自Annotation,因此读取注解,需要使用反射API,通过反射获取,AnnotationElement接口提供了获取注解的方法,反射相关的类Class、Method、Field都实现了AnnotationElement接口,AnnotationElement提供了几种获取注解的接口如下:
判断某个注解是否存在于Class、Field、Method、或者Constructor:
- Class.isAnnotationPresent(CheckLogin.class)
- Field.isAnnotationPresent(CheckLogin.class)
- Method.isAnnotationPresent(CheckLogin.class)
- Constructor.isAnnotationPresent(CheckLogin.class)
利用反射获取注解的方法:
CheckLogin check = CheckLogin.class.getAnnotation(CheckLogin.class)
读取方法、字段、构造方法的注解和Class类似,但是获取方法参数的注解稍微麻烦一点,因为方法参数本身可以看成一个数组,每个参数又可以定义多个注解,所以方法参数的注解必须用一个二维数组表示。
知识学习了就要进行实践,理论加实践才是技术成长的方式。
应用场景一:自定义注解+拦截器实现权限校验
校验用户是否进行了登陆,即校验token或者cookie,没有登录或者登录超时的需要跳转到登陆页。
第一步:
自定义注解CheckLogin,添加@Target、@Retention以及LoginFlag参数
package com.garin.springboot.demo.config;
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CheckLogin {
boolean LoginFlag() default true;
}
第二步:
创建拦截器,对请求的接口进行拦截,利用反射获取注解,根据注解的信息判断需不需要进行校验登陆
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod)handler;
Method method = handlerMethod.getMethod();
//获取注解
CheckLogin methodLogin = method.getAnnotation(CheckLogin.class);
if (!methodLogin.LoginFlag()) {
return true;
}
//需要校验登陆信息
String token = request.getHeader("token");
if (token == null ) {
//需要登陆
}
return false;
}
}
第三步:
定义一个controller,并使用自定义的注解CheckLogin
@RestController
public class BuyController {
@RequestMapping("/buy")
@CheckLogin(LoginFlag = true)
public String buy(Product product) {
return "我购买了水果和蔬菜";
}
}
第四步:
把定义的拦截器注入到Spring中
@Configuration
public class LoginConfigure implements WebMvcConfigurer {
@Autowired
LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor);
}
}
应用场景二:自定义注解+特定的校验器实现校验功能
一些复杂的参数校验,现有的校验框架有时候无法满足需求,这个时候就需要我们自己手动定义注解来实现校验,会用到ConstraintValidator。
首先定义一个注解StatusVerify:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Constraint(validatedBy = StatusValidator.class) //指定我们定义的校验器
public @interface StatusVerify {
int [] values();
String tip() default "请传入正确的状态值";
Class>[] groups() default {};
Class extends Payload>[] payload() default{};
}
然后定义一个校验器,实现ConstraintValidator
public class StatusValidator implements ConstraintValidator {
List list = new ArrayList<>();
//初始化
@Override
public void initialize(StatusVerify constraintAnnotation) {
int [] values = constraintAnnotation.values();
for (int aa :values) {
list.add(aa);
}
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
System.out.println("list:"+ list.toString());
if (list.contains(value)) {
System.out.println("校验成功!");
return true;
}
return false;
}
}
然后使用注解,在需要校验的字段上添加StatusVerify注解
@Data
public class Product {
@StatusVerify(values ={10,20,30},tip ="请传入正确的状态值")
private String id;
private String name;
private String price;
private String weight;
}
在controller层进行调用:
@RequestMapping("/check")
public void checkStatus(@RequestBody Product product) {
System.out.println("这个状态合法");
}
参考文档:
定义注解 - 廖雪峰的官方网站
Java 自定义注解及使用场景 - 简书
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)