springboot高级功能(三)自定义注解实现方式全解析

简介: springboot高级功能(三)自定义注解实现方式全解析


源注解

@Retention

1.  //注解只会存在源代码中,将会被编译器丢弃
2.     SOURCE,
3. //注解将会保留到class文件阶段,但是在加载如vm的时候会被抛弃
4. CLASS,
5. //注解不单会被保留到class文件阶段,而且也会被vm加载进虚拟机的时候保留
6.     RUNTIME

@Target

1.     用于描述类、接口(包括注解类型) 或enum声明 Class, interface (including annotation type), or enum declaration 
2.     TYPE,
3. 
4.     用于描述域 Field declaration (includes enum constants)
5.     FIELD,
6. 
7.     用于描述方法 Method declaration
8. METHOD,
9. 
10.     用于描述参数 Formal parameter declaration 
11. PARAMETER,
12. 
13.     用于描述构造器 Constructor declaration
14.     CONSTRUCTOR,
15. 
16.     用于描述局部变量 Local variable declaration
17.     LOCAL_VARIABLE,
18. 
19.     Annotation type declaration
20.     ANNOTATION_TYPE,
21. 
22.     用于描述包 Package declaration
23.     PACKAGE,
24. 
25.     用来标注类型参数 Type parameter declaration
26.     TYPE_PARAMETER,
27. 
28. *能标注任何类型名称 Use of a type
29.     TYPE_USE

依赖于@Conditional

原理是是否满足@Conditional中绑定类的条件,如果满足,就将使用注解的类注入进factory,不满足就不注入,只能在类中使用或者配合@bean使用

代码

首先定义一个注解类

定义的变量为参数

@Conditional(CustomOnPropertyCondition.class)意思为CustomOnPropertyCondition类中返回为true才会使用注解

1. package com.airboot.bootdemo.config;
2. import org.springframework.context.annotation.Conditional;
3. import java.lang.annotation.*;
4. 
5. @Retention(RetentionPolicy.RUNTIME)
6. @Target({ElementType.TYPE, ElementType.METHOD})
7. @Documented
8. @Conditional(CustomPropertyCondition.class)
9. public @interface CustomProperty {
10. 
11. //参数
12.     String name() default "";
13. /**
14.          * havingValue数组,支持or匹配
15.          */
16. 
17. //参数
18.     String[] havingValue() default {};
19. 
20. }

CustomOnPropertyCondition类

实现condition接口 以下是实现接口后的代码 然后在其中填补

1. public class CustomPropertyCondition implements Condition {
2. @Override
3. public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
4. return false;
5.     }
6. }
1. 
2. import org.springframework.context.annotation.Condition;
3. import org.springframework.context.annotation.ConditionContext;
4. import org.springframework.core.type.AnnotatedTypeMetadata;
5. import org.springframework.web.bind.annotation.RequestMapping;
6. 
7. import java.util.Map;
8. 
9. 
10. public class CustomPropertyCondition implements Condition {
11. @Override
12. public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
13. //获取注解上的参数和配置值
14. Map<String, Object> annotationAttributes = annotatedTypeMetadata.getAnnotationAttributes(RequestMapping.class.getName());
15. //获取具体的参数值
16. String propertyName = (String) annotationAttributes.get("name");
17. String[] values = (String[]) annotationAttributes.get("havingValue");
18. if (0 == values.length) {
19. return false;
20.         }
21. //从application.properties中获取配置
22. String propertyValue = conditionContext.getEnvironment().getProperty(propertyName);
23. // 有一个匹配上就ok
24. for (String havingValue : values) {
25. if (propertyValue.equalsIgnoreCase(havingValue)) {
26. return true;
27.             }
28.         }
29. return false;
30.     }
31. }

Map annotationAttributes = annotatedTypeMetadata.getAnnotationAttributes(RequestMapping.class.getName()); 获取类中注解上的参数

String propertyValue = conditionContext.getEnvironment().getProperty(propertyName);获取application.properties上的配置

使用

1. @RequestMapping(value ="/Demo")
2. @Controller
3. @ResponseBody
4. @Api("demo测试类")
5. @CustomProperty(name = "condition",havingValue = {"2"})
6. public class DemoController {
7. 
8. 
9. }

注解在spring boot启动时加载。

使用HandlerMethodArgumentResolver

注意 1 只能接受get请求

定义注解

1. package com.airboot.bootdemo.config;
2. 
3. import java.lang.annotation.*;
4. 
5. 
6. @Target({ElementType.PARAMETER, ElementType.METHOD})
7. @Retention(RetentionPolicy.RUNTIME)
8. @Documented
9. public @interface UserCheck {
10. 
11. //当前用户在request中的名字
12.     String value() default "userid";
13. }

定义类实现HandlerMethodArgumentResolver

其中resolveArgument中返回的是controllor的参数,也就是这个方法里面可以组装入参

1. package com.airboot.bootdemo.config;
2. 
3. import org.springframework.core.MethodParameter;
4. import org.springframework.web.bind.support.WebDataBinderFactory;
5. import org.springframework.web.context.request.NativeWebRequest;
6. import org.springframework.web.method.support.HandlerMethodArgumentResolver;
7. import org.springframework.web.method.support.ModelAndViewContainer;
8. 
9. public class UserCheckMethodArgumentResolver implements HandlerMethodArgumentResolver {
10. @Override
11. public boolean supportsParameter(MethodParameter methodParameter) {
12. return false;
13.     }
14. 
15. @Override
16. public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
17. return null;
18.     }
19. }
1. import com.airboot.bootdemo.entity.DemoVO;
2. import org.springframework.core.MethodParameter;
3. import org.springframework.web.bind.support.WebDataBinderFactory;
4. import org.springframework.web.context.request.NativeWebRequest;
5. import org.springframework.web.method.support.HandlerMethodArgumentResolver;
6. import org.springframework.web.method.support.ModelAndViewContainer;
7. 
8. public class UserCheckMethodArgumentResolver implements HandlerMethodArgumentResolver {
9. @Override
10. public boolean supportsParameter(MethodParameter parameter) {
11. if (parameter.getParameterType().isAssignableFrom(DemoVO.class) && parameter.hasParameterAnnotation(UserCheck.class)) {
12. return true;
13.         }
14. return false;
15.     }
16. 
17. @Override
18. public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
19. UserCheck currentUserAnnotation = parameter.getParameterAnnotation(UserCheck.class);
20. //获取head中的
21. String userId = webRequest.getHeader("userId");
22. //组装入参 return后的参数 类型需要和controllor中的相同 
23. DemoVO demoVO = new DemoVO();
24.         demoVO.setId(Long.valueOf(1));
25. return demoVO;
26.     }
27. }

controllor

1. @RequestMapping(value = "/selectDemoByVo")
2. public List<DemoVO> selectDemoByVo(@RequestBody @UserCheck DemoVO demoVO) {
3. return demoService.selectDemoVO(demoVO);
4.     }

基于aop

1. 
2. 1.JoinPoint  
3. * java.lang.Object[] getArgs():获取连接点方法运行时的入参列表; 
4. * Signature getSignature() :获取连接点的方法签名对象; 
5. * java.lang.Object getTarget() :获取连接点所在的目标对象; 
6. * java.lang.Object getThis() :获取代理对象本身; 
7. 
8. 2.ProceedingJoinPoint  
9. * ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法: 
10. * java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法; 
11. * java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。

注解类

1. import java.lang.annotation.ElementType;
2. import java.lang.annotation.Retention;
3. import java.lang.annotation.RetentionPolicy;
4. import java.lang.annotation.Target;
5. 
6. @Target({ ElementType.METHOD })
7. @Retention(RetentionPolicy.RUNTIME)
8. public @interface TestAnnotation {
9. 
10. int p0() default 0;
11.     String p1() default "";
12.     Class<?> clazz();
13. 
14. }

切面配置

其中切点要切到注解类的路径

1. import org.aspectj.lang.ProceedingJoinPoint;
2. import org.aspectj.lang.annotation.*;
3. import org.springframework.stereotype.Component;
4. import lombok.extern.slf4j.Slf4j;
5. 
6. @Slf4j
7. @Aspect
8. @Component
9. public class TestAspect {
10. 
11. // 切入点签名
12. @Pointcut("@annotation(com.airboot.bootdemo.config.TestAnnotation)")
13. private void cut() {
14.     }
15. 
16. // 前置通知
17. @Before("cut()")
18. public void BeforeCall() {
19.         log.info("====前置通知start");
20. 
21.         log.info("====前置通知end");
22.     }
23. 
24. // 环绕通知
25. @Around(value = "cut()")
26. public Object AroundCall(ProceedingJoinPoint joinPoint) throws Throwable {
27.         log.info("====环绕通知start");
28. 
29. // 注解所切的方法所在类的全类名
30. String typeName = joinPoint.getTarget().getClass().getName();
31.         log.info("目标对象:[{}]", typeName);
32. 
33. // 注解所切的方法名
34. String methodName = joinPoint.getSignature().getName();
35.         log.info("所切方法名:[{}]", methodName);
36. 
37. StringBuilder sb = new StringBuilder();
38. // 获取参数
39.         Object[] arguments = joinPoint.getArgs();
40. for (Object argument : arguments) {
41.             sb.append(argument.toString());
42.         }
43.         log.info("所切方法入参:[{}]", sb.toString());
44. 
45. // 统计方法执行时间
46. long start = System.currentTimeMillis();
47. 
48. //执行目标方法,并获得对应方法的返回值
49. Object result = joinPoint.proceed();
50.         log.info("返回结果:[{}]", result);
51. 
52. long end = System.currentTimeMillis();
53.         log.info("====执行方法共用时:[{}]", (end - start));
54. 
55.         log.info("====环绕通知之结束");
56. return result;
57.     }
58. 
59. // 后置通知
60. @After("cut()")
61. public void AfterCall() {
62.         log.info("====后置通知start");
63. 
64.         log.info("====后置通知end");
65.     }
66. 
67. // 最终通知
68. @AfterReturning("cut()")
69. public void AfterReturningCall() {
70.         log.info("====最终通知start");
71. 
72.         log.info("====最终通知end");
73.     }
74. 
75. // 异常通知
76. @AfterThrowing(value = "cut()", throwing = "ex")
77. public void afterThrowing(Throwable ex) {
78. throw new RuntimeException(ex);
79.     }
80. 
81. }

controllor

1. @RequestMapping(value = "/selectDemoByVo")
2. @TestAnnotation(p0 = 123, p1 = "qaws",clazz = DemoVO.class)
3. public List<DemoVO> selectDemoByVo(@RequestBody DemoVO demoVO) {
4. return demoService.selectDemoVO(demoVO);
5.     }

拦截器

自定义注解类

1. import java.lang.annotation.*;
2. 
3. @Target(ElementType.METHOD)
4. @Retention(RetentionPolicy.RUNTIME)
5. @Documented
6. public @interface InterceptorAnnotation {
7. 
8. 
9.     String[] value() default {};
10. 
11.     String[] authorities() default {};
12. 
13.     String[] roles() default {};
14. }

拦截器

1. import org.springframework.web.method.HandlerMethod;
2. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
3. 
4. import javax.servlet.http.HttpServletRequest;
5. import javax.servlet.http.HttpServletResponse;
6. import java.lang.reflect.Method;
7. 
8. public class TestAnnotationInterceptor extends HandlerInterceptorAdapter {
9. // 在调用方法之前执行拦截
10. @Override
11.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
12. // 将handler强转为HandlerMethod, 前面已经证实这个handler就是HandlerMethod
13. HandlerMethod handlerMethod = (HandlerMethod) handler;
14. // 从方法处理器中获取出要调用的方法
15. Method method = handlerMethod.getMethod();
16. // 获取出方法上的自定义注解
17. InterceptorAnnotation access = method.getAnnotation(InterceptorAnnotation.class);
18. if (access == null) {
19. // 如果注解为null,没有注解 不拦截
20. return true;
21.         }
22. //获取注解值
23. if (access.authorities().length > 0) {
24. // 如果权限配置不为空, 则取出配置值
25. String[] authorities = access.authorities();
26.         }
27. // 拦截之后应该返回公共结果, 这里没做处理
28. return true;
29.     }
30. }

主要原理就是拦截所有的请求 然后判断方法上是否有自定的注解 如果有注解 执行注解的操作

注册拦截器

1. package com.airboot.bootdemo.config;
2. 
3. 
4. import org.springframework.context.annotation.Configuration;
5. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
6. import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
7. 
8. @Configuration
9. public class InterceptorConfig extends WebMvcConfigurerAdapter {
10. @Override
11.     public void addInterceptors(InterceptorRegistry registry) {
12.         registry.addInterceptor(new TestAnnotationInterceptor()).addPathPatterns("/**");
13.     }
14. }

调用

1. @RequestMapping(value = "/selectDemoByVo")
2. @InterceptorAnnotation(authorities = {"admin"})
3. public List<DemoVO> selectDemoByVo(@RequestBody DemoVO demoVO) {
4. return demoService.selectDemoVO(demoVO);
5.     }

ConstraintValidator注解实现验证

验证入参格式使用

注解类

其中message是返回值,groups()和payload() 是必须有的

@Constraint(validatedBy = TestConstraintValidator.class) 是处理注解逻辑的类

1. import javax.validation.Constraint;
2. import javax.validation.Payload;
3. import java.lang.annotation.*;
4. 
5. @Target({ElementType.METHOD, ElementType.FIELD})
6. @Retention(RetentionPolicy.RUNTIME)
7. @Documented
8. @Constraint(validatedBy = TestConstraintValidator.class)
9. public @interface TestConstraintAnnotation {
10. 
11.     String message() default "入参大小不合适";
12. 
13. long min();
14. 
15. long max();
16. 
17. boolean required() default true;
18. 
19.     Class<?>[] groups() default {};
20. 
21.     Class<? extends Payload>[] payload() default {};
22. 
23. }

逻辑类

需要实现ConstraintValidator,第一个参数是注解类,第二个参数是入参类型

只有第一次调用时才会调用initialize ,如果满足isValid逻辑,那么就正常执行,不满足会有message提示

1. public class TestConstraintValidator implements ConstraintValidator<TestConstraintAnnotation, Object> {
2. private long max = 1;
3. private long min = 1;
4. 
5. @Override
6. public void initialize(TestConstraintAnnotation constraintAnnotation) {
7.         max = constraintAnnotation.max();
8.         min = constraintAnnotation.min();
9.     }
10. 
11. @Override
12. public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
13. if(o == null){
14. return true;
15.         }
16. if(Long.valueOf(o.toString())>=min && Long.valueOf(o.toString())<=max){
17. return true;
18.         }
19. return false;
20.     }
21. }

使用

vo中在需要验证的参数上加上自定义的注解,在方法接收参数前加入@Valid 说明本方法需要验证

1. @RequestMapping(value = "/selectDemoByVo")
2. public List<DemoVO> selectDemoByVo(@Valid @RequestBody DemoVO demoVO) {
3. return demoService.selectDemoVO(demoVO);
4.     }
5. 
6. 
7. @TestConstraintAnnotation(min = 1, max = 10)
8. private Long id;

参考:https://www.licoy.cn/3238.html

       https://www.jianshu.com/p/8cbfff715581

       http://www.360doc.com/content/17/1122/16/16915_706175138.shtml

       https://blog.csdn.net/m0_37819279/article/details/80455165

       https://www.jianshu.com/p/d7842927340f

       https://www.jianshu.com/p/e04eeae86cf9


相关文章
|
4月前
|
存储 Java 文件存储
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— logback.xml 配置文件解析
本文解析了 `logback.xml` 配置文件的详细内容,包括日志输出格式、存储路径、控制台输出及日志级别等关键配置。通过定义 `LOG_PATTERN` 和 `FILE_PATH`,设置日志格式与存储路径;利用 `&lt;appender&gt;` 节点配置控制台和文件输出,支持日志滚动策略(如文件大小限制和保存时长);最后通过 `&lt;logger&gt;` 和 `&lt;root&gt;` 定义日志级别与输出方式。此配置适用于精细化管理日志输出,满足不同场景需求。
383 1
|
3月前
|
前端开发 安全 Java
Spring Boot 便利店销售系统项目分包设计解析
本文深入解析了基于Spring Boot的便利店销售系统分包设计,通过清晰的分层架构(表现层、业务逻辑层、数据访问层等)和模块化设计,提升了代码的可维护性、复用性和扩展性。具体分包结构包括`controller`、`service`、`repository`、`entity`、`dto`、`config`和`util`等模块,职责分明,便于团队协作与功能迭代。该设计为复杂企业级应用开发提供了实践参考。
144 0
|
3月前
|
XML 前端开发 Java
SpringBoot实现文件上传下载功能
本文介绍了如何使用SpringBoot实现文件上传与下载功能,涵盖配置和代码实现。包括Maven依赖配置(如`spring-boot-starter-web`和`spring-boot-starter-thymeleaf`)、前端HTML页面设计、WebConfig路径映射配置、YAML文件路径设置,以及核心的文件上传(通过`MultipartFile`处理)和下载(利用`ResponseEntity`返回文件流)功能的Java代码实现。文章由Colorful_WP撰写,内容详实,适合开发者学习参考。
215 0
|
3月前
|
存储 前端开发 JavaScript
调用DeepSeek API增强版纯前端实现方案,支持文件上传和内容解析功能
本方案基于DeepSeek API增强版,提供纯前端实现的文件上传与内容解析功能。通过HTML和JavaScript,用户可选择文件并调用API完成上传及解析操作。方案支持多种文件格式(如PDF、TXT、DOCX),具备简化架构、提高响应速度和增强安全性等优势。示例代码展示了文件上传、内容解析及结果展示的完整流程,适合快速构建高效Web应用。开发者可根据需求扩展功能,满足多样化场景要求。
1180 64
|
2月前
|
安全 Java API
Spring Boot 功能模块全解析:构建现代Java应用的技术图谱
Spring Boot不是一个单一的工具,而是一个由众多功能模块组成的生态系统。这些模块可以根据应用需求灵活组合,构建从简单的REST API到复杂的微服务系统,再到现代的AI驱动应用。
320 8
|
4月前
|
人工智能 API 语音技术
HarmonyOS Next~鸿蒙AI功能开发:Core Speech Kit与Core Vision Kit的技术解析与实践
本文深入解析鸿蒙操作系统(HarmonyOS)中的Core Speech Kit与Core Vision Kit,探讨其在AI功能开发中的核心能力与实践方法。Core Speech Kit聚焦语音交互,提供语音识别、合成等功能,支持多场景应用;Core Vision Kit专注视觉处理,涵盖人脸检测、OCR等技术。文章还分析了两者的协同应用及生态发展趋势,展望未来AI技术与鸿蒙系统结合带来的智能交互新阶段。
246 31
|
4月前
|
供应链 监控 搜索推荐
反向海淘代购独立站:功能解析与搭建指南
“反向海淘”指海外消费者购买中国商品的现象,体现了中国制造的创新与强大。国产商品凭借高性价比和丰富功能,在全球市场备受欢迎。跨境电商平台的兴起为“反向海淘”提供了桥梁,而独立站因其自主权和品牌溢价能力逐渐成为趋势。一个成功的反向海淘代购独立站需具备多语言支持、多币种支付、物流跟踪、商品展示、购物车管理等功能,并通过SEO优化、社交媒体营销等手段提升运营效果。这不仅助力中国企业开拓海外市场,还推动了品牌全球化进程。
144 19
|
3月前
|
SQL 前端开发 Java
深入理解 Spring Boot 项目中的分页与排序功能
本文深入讲解了在Spring Boot项目中实现分页与排序功能的完整流程。通过实际案例,从Service层接口设计到Mapper层SQL动态生成,再到Controller层参数传递及前端页面交互,逐一剖析每个环节的核心逻辑与实现细节。重点包括分页计算、排序参数校验、动态SQL处理以及前后端联动,确保数据展示高效且安全。适合希望掌握分页排序实现原理的开发者参考学习。
154 4
|
3月前
|
缓存 安全 Java
深入解析HTTP请求方法:Spring Boot实战与最佳实践
这篇博客结合了HTTP规范、Spring Boot实现和实际工程经验,通过代码示例、对比表格和架构图等方式,系统性地讲解了不同HTTP方法的应用场景和最佳实践。
269 5
|
4月前
|
SQL 运维 监控
高效定位 Go 应用问题:Go 可观测性功能深度解析
为进一步赋能用户在复杂场景下快速定位与解决问题,我们结合近期发布的一系列全新功能,精心梳理了一套从接入到问题发现、再到问题排查与精准定位的最佳实践指南。

推荐镜像

更多
  • DNS