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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 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


相关文章
|
9天前
|
数据可视化 数据挖掘 BI
团队管理者必读:高效看板类协同软件的功能解析
在现代职场中,团队协作的效率直接影响项目成败。看板类协同软件通过可视化界面,帮助团队清晰规划任务、追踪进度,提高协作效率。本文介绍看板类软件的优势,并推荐五款优质工具:板栗看板、Trello、Monday.com、ClickUp 和 Asana,助力团队实现高效管理。
31 2
|
1天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
8天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
52 14
|
5天前
|
小程序 安全 搜索推荐
陪玩小程序的搭建解析与功能需求
陪玩小程序是为玩家提供专业陪玩服务的应用,嵌入社交或游戏平台,具备智能匹配、实时聊天、预约服务等功能,支持便捷高效的游戏体验。源码交付时需提供详细文档、技术支持及定制开发服务,确保客户能顺利维护和升级。选择陪玩小程序时应关注功能需求、用户体验、安全性和成本效益,以确保最佳使用效果。
33 0
|
22天前
|
存储 安全 数据安全/隐私保护
深入解析iOS 14隐私保护功能:用户数据安全的新里程碑
随着数字时代的到来,个人隐私保护成为全球关注的焦点。苹果公司在最新的iOS 14系统中引入了一系列创新的隐私保护功能,旨在为用户提供更透明的数据使用信息和更强的控制权。本文将深入探讨iOS 14中的几项关键隐私功能,包括App跟踪透明性、简化的隐私设置以及增强的系统安全性,分析它们如何共同作用以提升用户的隐私保护水平。
73 3
|
26天前
|
前端开发 Java Spring
Spring MVC核心:深入理解@RequestMapping注解
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的核心,它将HTTP请求映射到控制器的处理方法上。本文将深入探讨`@RequestMapping`注解的各个方面,包括其注解的使用方法、如何与Spring MVC的其他组件协同工作,以及在实际开发中的应用案例。
40 4
|
26天前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
91 2
|
26天前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
44 2
|
27天前
|
前端开发 Java Maven
深入解析:如何用 Spring Boot 实现分页和排序
深入解析:如何用 Spring Boot 实现分页和排序
52 2
|
29天前
|
消息中间件 Java 数据库
解密Spring Boot:深入理解条件装配与条件注解
Spring Boot中的条件装配与条件注解提供了强大的工具,使得应用程序可以根据不同的条件动态装配Bean,从而实现灵活的配置和管理。通过合理使用这些条件注解,开发者可以根据实际需求动态调整应用的行为,提升代码的可维护性和可扩展性。希望本文能够帮助你深入理解Spring Boot中的条件装配与条件注解,在实际开发中更好地应用这些功能。
36 2

推荐镜像

更多