Spring 自定义注解
1、@Taget注解详解
@Target
说明了Annotation
所修饰的对象范围:Annotation
可被用于 packages
、types
(类、接口、枚举、Annotation
类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch
参数)。在Annotation
类型的声明中使用了target
可更加明晰其修饰的目标。
具体属性如下:
- ElementType.TYPE:说明该注解只能被声明在一个类前;
- ElementType.FIELD:说明该注解只能被声明在一个类的字段前;
- ElementType.METHOD:说明该注解只能被声明在一个类的方法前;
- ElementType.PARAMETER:说明该注解只能被声明在一个方法参数前;
- ElementType.CONSTRUCTOR:说明该注解只能声明在一个类的构造方法前;
- ElementType.LOCAL_VARIABLE:说明该注解只能声明在一个局部变量前;
- ElementType.ANNOTATION_TYPE:说明该注解只能声明在一个注解类型前;
- ElementType.PACKAGE:说明该注解只能声明在一个包名前。
2、@Documented的作用
@Documented
注解表明这个注解应该被 javadoc
工具记录. 默认情况下,javadoc
是不包括注解的. 但如果声明注解时指定了 @Documented
,则它会被 javadoc
之类的工具处理, 所以注解类型信息也会被包括在生成的文档中,是一个标记注解,没有成员。
3、@Retention作用
@Retention
作用是定义被它所注解的注解保留多久,一共有三种策略,定义在RetentionPolicy
枚举中。
属性详解
- source:注解只保留在源文件,当
Java
文件编译成class
文件的时候,注解被遗弃;被编译器忽略; - class:注解被保留到
class
文件,但Jvm
加载class
文件时候被遗弃,这是默认的生命周期; - runtime:注解不仅被保存到
class
文件中,jvm加载class
文件之后,仍然存在。
4、@Constraint的作用
@Constraint
注解用于自定义校验注解中, 必须实现ConstraintValidator
接口;接口里面有两个泛型, 第一个是自定义的注解类, 第二个是需要验证的数据类型, 可以用Object
, 表示所有类型都支持, 根据不同类型走不同逻辑方法。- 接口中必须实现的两个方法:
- 第一个是初始化验证器, 初始化完成后的结果让
isValid
调用, - 第二个是验证逻辑, 不同的逻辑方法就是在这里实现. 返回
true
, 则验证通过,false
则不通过, 然后会抛出message
里面自定义的异常信息。
- 第一个是初始化验证器, 初始化完成后的结果让
5、自定义校验注解
5.1、自定义校验注解
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Constraint(validatedBy = CheckField.ParamConstraintValidated.class)
public @interface CheckField {
String name() default "";
// 必须有这三个参数
String message() default "参数不为指定值";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface List{
CheckField [] value();
}
class ParamConstraintValidated implements ConstraintValidator<CheckField, String> {
private String paramConstraint;
@Override
public void initialize(CheckField checkField) {
paramConstraint = checkField.name();
}
@Override
public boolean isValid(String o, ConstraintValidatorContext constraintValidatorContext) {
if (paramConstraint == o) {
return true;
}
return false;
}
}
}
5.2、注解使用类
import com.xdx.annotation.CheckField;
import lombok.*;
import org.hibernate.validator.constraints.Length;
@Data
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class User {
@CheckField(name = "dasdasddad")
@Length(max = 2)
private String uid;
private String name;
private int sex;
@Override
public String toString() {
return "User{" +
"uid='" + uid + '\'' +
", name='" + name + '\'' +
", sex=" + sex +
'}';
}
}
5.3、校验注解的触发
5.3.1、controller中触发
import com.xdx.entity.User;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
@RequestMapping("/aspect")
@Validated
public class AspectController {
@PostMapping("/hello")
public String hello() {
return "Hello Word";
}
@PostMapping("/getUser")
public String getUser(@RequestBody @Valid User user, BindingResult bindingResult) {
if (chargeAnnotation(user, bindingResult)) {
return user.toString();
}
return "你是错的";
}
@PostMapping("/delete")
public String deleteUser(
@NotBlank
@Length(max = 32, message = "asdasdasdasd")
@RequestParam(value = "id") String id) {
User user = new User();
user.setUid("fadzfsfgf");
return "删除失败";
}
public boolean chargeAnnotation(User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
bindingResult.getAllErrors().forEach(v -> {
System.out.println(v.getDefaultMessage());
});
return false;
} else {
return true;
}
}
}
5.3.2、在方法中触发
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.util.Set;
@Component
public class AnnotationCheck <T>{
/**
* 方法1:通过静态的方法生成validator器
*/
// private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
/**
* 当Spring实例化此服务时,它将自动将一个Validator实例注入构造函数。
* 此种写法更优秀
*/
private Validator validator;
AnnotationCheck(Validator validator) {
this.validator = validator;
}
public void checkAnnotation(T t) {
//执行验证
Set<ConstraintViolation<T>> constraintViolations = validator.validate(t);
//打印校验信息
if (!CollectionUtils.isEmpty(constraintViolations)) {
for (ConstraintViolation<T> constraintViolation : constraintViolations) {
System.out.println(constraintViolation.getPropertyPath().toString() + ": " + constraintViolation.getMessage());
}
}
}
}
测试
@PostMapping("/delete")
@ApiOperation(value = "测试注解是否生效")
public String deleteUser(
@NotBlank
@Length(max = 32, message = "asdasdasdasd")
@RequestParam(value = "id") String id) {
User user = new User();
user.setUid("fadzfsfgf");
annotationCheck.checkAnnotation(user);
return "删除失败";
}
5.4、自定义注解-groups的使用
5.4.1、新建两个声明接口
/**
* 可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型新增时的参数校验规则
*/
public interface CreateAction {
}
/**
* 可以在一个Model上面添加多套参数验证规则,此接口定义添加Person模型修改时的参数校验规则
*/
public interface UpdateAction {
}
5.4.2、修改实体类
import com.xdx.annotation.CheckField;
import com.xdx.annotation.CreateAction;
import com.xdx.annotation.UpdateAction;
import lombok.*;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Size;
import java.util.Arrays;
@Data
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class User {
@CheckField(name = "dasdasddad")
@Length(max = 10)
private String uid;
@CheckField.List({
@CheckField(groups = CreateAction.class, message = "test_____CreateAction"),
@CheckField(groups = UpdateAction.class, message = "test_____UpdateAction")
})
private String name;
private int sex;
@Size(min = 2, max = 8)
private char[] ch;
@Override
public String toString() {
return "User{" +
"uid='" + uid + '\'' +
", name='" + name + '\'' +
", sex=" + sex +
", ch=" + Arrays.toString(ch) +
'}';
}
}
5.4.3、Controller测试
@PostMapping("/getUserGroup")
@ApiOperation(value = "测试注解中的Group是否生效")
public String getUserGroup(@RequestBody @Validated({CreateAction.class}) User user, BindingResult bindingResult) {
if (chargeAnnotation(user, bindingResult)) {
return user.toString();
}
return "你是错的";
}
public boolean chargeAnnotation(User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
bindingResult.getAllErrors().forEach(v -> {
System.out.println(v.getDefaultMessage());
});
return false;
} else {
return true;
}
}
5.4.4、方法测试
5.4.4.1新建一个测试方法
import com.xdx.entity.User;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
@Validated
@Component
public class CheckMethodGroup {
@Validated(CreateAction.class)
public boolean chargeMethodAnnotation(
@Valid User user) {
return true;
}
}
5.4.4.2、Controller测试
@RequestMapping(value = "/getUserGroupMethod")
@ApiOperation(value = "测试自定义校验注解-groups分组-方法")
public String getUserGroupMethod(@RequestBody User user){
try {
methodGroup.chargeMethodAnnotation(user);
} catch (Exception e) {
return "1231231";
}
return "你是错的";
}
6、自定义切面注解
6.1、定义注解类
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CheckMethod {
String value() default "";
}
6.2、定义注解类的实现类
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
@Component
@Aspect
@Configuration
public class CheckMethodAspect {
private static Logger logger = LoggerFactory.getLogger(CheckMethodAspect.class);
@Pointcut("@annotation(com.xdx.annotation.CheckMethod)")
private void pointCut() {
// System.out.println("切面开始了");
}
@Before("pointCut() && @annotation(CheckMethod)")
public void advice(JoinPoint joinPoint, CheckMethod CheckMethod) {
logger.info("注解作用的方法名: " + joinPoint.getSignature().getName());
logger.info("所在类的简单类名: " +
joinPoint.getSignature().getDeclaringType().getSimpleName());
logger.info("所在类的完整类名: " +
joinPoint.getSignature().getDeclaringType());
}
}
3、测试
@CheckMethod("123123123123")
@RequestMapping("/getCheckMethod")
public String getCheckMethod(@RequestBody User user) {
return user.toString();
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?