Spring自定义注解全集,从入门到......我们的需要。根据注解的使用场合,文章将分为字段注解、方法注解、类注解来介绍自定义注解。字段注解字段注解一般用于验证字段是否符合要求。hibernate-validate依赖提供了很多验证注解,比如@NotNull、@Range等,但是这些注解并不能满足所有的业务场景。比如我们想让传入的参数在指定的String集合中,那么现有的注解是不能满足要求的,需要自己实现。自定义注解定义一个@Check注解,通过@interface声明一个注解@Target({ElementType.FIELD})//只允许在类的字段上使用@Retention(RetentionPolicy.RUNTIME)//该注解是程序运行过程中保留,此时可以通过反射获取到类上定义的所有注解@Constraint(validatedBy=ParamConstraintValidated.class)public@interfaceCheck{/***合法参数值**/String[]paramValues();/***提示信息**/Stringmessage()default"参数不是指定值";Class>[]groups()default{};Class[]payload()default{};}@Target定义注解使用位置来表示注解可以声明在那些元素之前。ElementType.TYPE:表示这个注解只能声明在一个类之前。ElementType.FIELD:表示这个注解只能声明在类的某个字段之前。ElementType.METHOD:表示这个注解只能声明在类的方法之前。ElementType.PARAMETER:表示这个注解只能声明在方法参数之前。ElementType.CONSTRUCTOR:表示这个注解只能声明在类的构造函数之前。ElementType.LOCAL_VARIABLE:表示这个注解只能声明在局部变量之前。ElementType.ANNOTATION_TYPE:表示这个注解只能声明在一个注解类型之前。ElementType.PACKAGE:表示注解只能声明在包名之前。@Constraint使用validatedBy指定与注释关联的验证器。@Retention用于描述注解类的生命周期。RetentionPolicy.SOURCE:注解只保留在源文件中。RetentionPolicy.CLASS:注解保存在class文件中,加载到JVM虚拟机时丢弃。一个类的所有注解。Validator类验证器类需要实现ConstraintValidator泛型接口publicclassParamConstraintValidatedimplementsConstraintValidator{/***合法的参数值,从注解中获取**/privateListparamValues;@Overridepublicvoidinitialize(CheckconstraintAnnotation){//初始化时获取注解上的值paramValues=Arrays.asList(constraintAnnotation.paramValues());}publicbooleanisValid(Objecto,ConstraintValidatorContextconstraintValidatorContext){if(paramValues.contains(o)){returntrue;}//不在指定的参数列表中returnfalse;}}第一个泛型参数Check:注解,第二个泛型参数Object:校验字段类型。需要实现initialize和isValid方法。isValid方法是验证逻辑。initialize方法用于初始化工作。定义一个实体类@DatapublicclassUser{/***name**/privateStringname;/***gendermanorwomen**/@Check(paramValues={"man","woman"})privateStringsex;}对sex字段添加验证,它的值必须是woman或mantest@RestController("/api/test")publicclassTestController{@PostMappingpublicObjecttest(@Validated@RequestBodyUseruser){return"helloworld";}}注意需要给User加上@Validated注解目的。这里也可以使用@Valid注解方法和类注解。我在开发过程中遇到过这样的需求,比如只有授权用户才能访问该类中的方法或者特定的方法,在查找数据时,不要先从数据库中查找,先从guava缓存中查找,再从redis中查找,最后来自mysql(多级缓存)。这时候我们可以自定义注解来实现这个需求。第一种场景是定义一个注解进行权限校验,第二种场景是在spring-data-redis包下定义类似@Cacheable的注解。权限注解自定义注解@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public@interfacePermissionCheck{/***resourcekey**/StringresourceKey();}这个注解的作用域是class或者方法上拦截器类publicclassPermissionCheckInterceptorextendsHandlerInterceptorAdapter{/***处理器处理之前调用*/@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{HandlerMethodhandlerMethod=(HandlerMethod)handler;PermissionCheckpermission=findPermissionCheck(handlerMethod);//如果没有添加权限Annotations直接跳过权限访问if(permission==null){returntrue;}//获取注解中的值StringresourceKey=permission.resourceKey();//TODO权限验证一般需要获取用户信息,通过查询进行权限databaseVerify//TODO这里只是简单演示,如果resourceKey为testKey,则验证通过,否则不通过if("testKey".equals(resourceKey)){returntrue;}returnfalse;}/***根据handlerMethod返回注解信息**@paramhandlerMethod方法对象*@returnPermissionCheck注解*/privatePermissionCheckfindPermissionCheck(HandlerMethodhandlerMethod){//在方法上找到注解PermissionCheckpermission=handlerMethod.getMethodAnnotation(PermissionCheck.class);if(permission==null){//在类上找注解permission可以访问,没有访问则不允许访问。本质其实就是一个拦截器。我们首先需要获取注解,然后获取注解上的字段进行校验。验证返回true,否则返回false。测试@GetMapping("/api/test")@PermissionCheck(resourceKey="test")publicObjecttestPermissionCheck(){return"helloworld";}该方法需要进行权限校验,所以添加PermissionCheck注解。缓存注解自定义注解@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public@interfaceCustomCache{/***缓存的键值**/Stringkey();}注解可以用在方法中或者类,但缓存注释通常用在方法上。Aspect@Aspect@ComponentpublicclassCustomCacheAspect{/***方法执行前处理注解**@parampjd*@paramcustomCache注解*@return返回值**/@Around("@annotation(com.cqupt.annotation.CustomCache)&&@annotation(customCache)")publicObjectdealProcess(ProceedingJoinPointpjd,CustomCachecustomCache){Objectresult=null;if(customCache.key()==null){//TODOthrowerror}//TODO业务场景会比这个更复杂,会涉及参数的解析,比如因为key可能是#{id},数据查询//TODO这里是一个简单的演示,如果key是testKey,returnhelloworldif("testKey".equals(customCache.key())){return"helloword";}//执行目标方法try{result=pjd.proceed();}catch(Throwablethrowable){throwable.printStackTrace();}returnresult;}}因为缓存注解在方法执行前需要有返回值,它不通过拦截器处理这个注解,而是在执行方法之前使用方面来处理这个注解。如果注解没有返回值,会返回方法test中的值@GetMapping("/api/cache")@CustomCache(key="test")publicObjecttestCustomCache(){return"don'thitcache";}