网站标题加后缀,模拟ip访问网站,爱站网官网,wordpress可视化编辑器不见一、前言
在Spring Boot项目开发过程中#xff0c;我们经常会使用到自定义注解的方式进行业务逻辑开发#xff0c;此时注解我们一般是放在方法或者类上面#xff0c;通过AOP切面拦截的方式进行自定义业务逻辑填充。但是如果自定义注解放在类的字段上#xff0c;此时应该如…一、前言
在Spring Boot项目开发过程中我们经常会使用到自定义注解的方式进行业务逻辑开发此时注解我们一般是放在方法或者类上面通过AOP切面拦截的方式进行自定义业务逻辑填充。但是如果自定义注解放在类的字段上此时应该如何进行解析呢
二、技术思路
自定义注解放在类的字段上其实解析起来也是比较容易的我这里采用的思路是
自定义一个MethodInterceptor拦截器拦截执行的业务方法获取到方法参数数组遍历每个参数获取每个参数中所有字段判断字段上是否有自定义注解筛选出所有标识了自定义注解的字段然后进行自定义业务逻辑填充。
三、技术实践
按照上面的思路我们进行一下技术实践这里我模拟一个案例。
1. 需求
自定义一个注解该注解作用于类的字段上带有该标识注解的字段在使用时如果字段没有设置值则采用注解设置的默认值。
2. 新建spring boot项目导入相关依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId
/dependency
dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId
/dependency
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId
/dependency3. 自定义注解
示例代码如下
import java.lang.annotation.*;Target({ElementType.FIELD}) // 标识此注解适用于字段上
Retention(RetentionPolicy.RUNTIME)
Documented
public interface DefaultValueAnnotation {String value() default ;
}4. 字段注解获取工具类
示例代码
import lombok.Data;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.ReflectionUtils;import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;/*** 字段上注解查找工具类*/
public class FieldAnnotationUtils {/*** 字段注解信息返回VO*/Datapublic static class FieldAnnotationInfo {/*** 类变量*/private Object obj;/*** 携带该注解的字段对象*/private Field field;/*** 注解对象*/private Annotation annotation;}/*** 判断类变量中是否字段上存在指定的注解如果存在则返回字段和注解信息** param obj 类变量* param annotationType 注解类型* return*/public static ListFieldAnnotationInfo parseFieldAnnotationInfo(Object obj, Class annotationType) {return parseFieldAnnotationInfo(obj, annotationType, null);}/*** 判断类变量中是否字段上存在指定的注解如果存在则返回字段和注解信息** param obj 类变量* param annotationType 注解类型* param filterFieldClazz 注解适用的字段类型不适用的字段类型即使字段上面添加改注解也不生效* return*/public static ListFieldAnnotationInfo parseFieldAnnotationInfo(Object obj, Class annotationType, SetClass filterFieldClazz) {if (obj null) {return null;}ListFieldAnnotationInfo resultList new ArrayList();/*** 获取该对象的所有字段进行遍历判断*/ReflectionUtils.doWithFields(obj.getClass(), field - {// 判断该字段上是否存在注解Annotation annotation AnnotatedElementUtils.findMergedAnnotation(field, annotationType);if (annotation ! null) { // 如果存在指定注解boolean flag true;if (filterFieldClazz ! null !filterFieldClazz.isEmpty()) { // 如果指定了适用的字段的类型for (Class c : filterFieldClazz) {if (c.isAssignableFrom(field.getType())) { // 判断该字段类型是否符合使用类型使用isAssignableFrom方法是为了父类也进行判断break;}flag false;}}if (flag) { // 如果该字段类型符合则返回字段注解信息FieldAnnotationInfo fieldAnnotationInfo new FieldAnnotationInfo();fieldAnnotationInfo.setObj(obj);fieldAnnotationInfo.setField(field);fieldAnnotationInfo.setAnnotation(annotation);resultList.add(fieldAnnotationInfo);}}});return resultList;}
}5. 自定义MethodInterceptor
示例代码
public class MyMethodInterceptor implements MethodInterceptor {Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {// 获取执行方法的所有参数Object[] arguments invocation.getArguments();// 参数列表不为空if (arguments ! null arguments.length ! 0) {// 由于我这里注解定义简单我只让String类型的字段生效SetClass clazzSet new HashSet();clazzSet.add(CharSequence.class);for (int i 0; i arguments.length; i) { // 判断所有参数// 从每个参数中获取字段上有DefaultValueAnnotation注解标识并且类型是CharSequence的字段信息ListFieldAnnotationUtils.FieldAnnotationInfo fieldAnnotationInfos FieldAnnotationUtils.parseFieldAnnotationInfo(arguments[i], DefaultValueAnnotation.class, clazzSet);if (!CollectionUtils.isEmpty(fieldAnnotationInfos)) { // 如果存在符合条件的字段信息for (int m 0; m fieldAnnotationInfos.size(); m) { // 判断所有符合条件的字段FieldAnnotationUtils.FieldAnnotationInfo fni fieldAnnotationInfos.get(m);Field field fni.getField(); // 获取字段field.setAccessible(true); // 设置可访问突破private权限Object value field.get(fni.getObj());if (value null) { // 判断当前字段是否已经有值// 如果当前字段没有值利用反射的方式把注解中的值设置进去field.set(fni.getObj(), ((DefaultValueAnnotation) fni.getAnnotation()).value());}field.setAccessible(false);}}}}// 放行方法执行return invocation.proceed();}
}6. 把自定义的MethodInterceptor织入到Spring容器中
示例代码
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;Configuration
public class MyInterceptorConfig {Beanpublic DefaultPointcutAdvisor DefaultPointcutAdvisor() {DefaultPointcutAdvisor defaultPointcutAdvisor new DefaultPointcutAdvisor();defaultPointcutAdvisor.setAdvice(new MyMethodInterceptor()); // 自定义MethodInterceptorAspectJExpressionPointcut aspectJExpressionPointcut new AspectJExpressionPointcut();aspectJExpressionPointcut.setExpression(execution(* com.learn..*(..))); // 指定需要拦截的切面defaultPointcutAdvisor.setPointcut(aspectJExpressionPointcut);return defaultPointcutAdvisor;}}7. 使用自定义注解
示例代码
import lombok.Data;Data
public class TestParamPojo {DefaultValueAnnotation(我是注解设置的值.)private String name;DefaultValueAnnotation(100)private Integer age;
}8. 定义方法入参
示例代码
Component
public class TestService {public void test() {System.out.println(test empty...);}public void test(TestParamPojo pojo) {System.out.println(test pojo: pojo.toString());}public void test(TestParamPojo pojo, String testStr) {System.out.println(test pojo: pojo.toString() testStr: testStr);}}9.测试 测试代码 Autowiredprivate TestService testService;PostConstructpublic void init() {System.out.println();testService.test();TestParamPojo testParamPojo new TestParamPojo();testService.test(testParamPojo);testService.test(testParamPojo, null);testParamPojo.setName(我是自己设置的...);testService.test(testParamPojo, hello, world.);System.out.println();}测试结果 测试结论 可以看到在没有设置值的时候可以从注解中获取默认值进行设置同时不是指定的字段类型即使标识了自定义注解也不会生效。
四、写在最后
本文是对Spring Boo项目中方法参数对象中字段上存在的注解如何进行拦截解析的技术探讨以上只是技术思路的一种实现方式仅供大家参考。