SpringBoot中自定义注解
目录
SpringBoot中自定义注解
关于注解的解释
元注解
@Documented
@Target
@Retention
@Inherited
@Native
自定义注解
自定义注解与SpringBoot全局异常处理完成参数校验
约束验证器
自定义全局异常处理器
自定义注解完成数据脱敏
定义脱敏策略枚举
自定义注解
实行脱敏逻辑
自定义注解的与Aop切面
使用主定义注解
定义切面获取注解信息
SpringBoot中自定义注解
- 本文主要记录在SpringBoot中对注解的操作,如
自定义注解
、自定义注解配置SpringBoot全局异常处理完成参数校验
、自定义注解完成数据脱敏
、Aop与自定义注解的配合
关于注解
的解释
- 注解的本质是继承了Annotation的特殊接口,其具体实现类是Java运行生成的动态代理类。当通过反射获取注解时,返回的是Java运行时生成的动态代理对象。通过代理对象调用自定义注解的方法,最会调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中取出应的值。而memberValues的来源则是Java常量池。在java中,所有的元注解都定义java.lang.annotation包下。其中
Annotation
是注解的基本接口,所有的注解都继承此接口,如同Java中所有的类都继承Object
一样
元注解
- 在java.lang.annotation包下提供了六种元注解,其作用于帮你定义注解时使用。
@Documented
- 主要用于指示编译器将被注解元素的注释信息包含在生成的API文档中
@Target
-
该元注解有一个ElementType数组类型的字段,主要用于标识自定义注解使用的位置
ElementType.TYPE:说明该注解只能被声明在一个类前。
ElementType.FIELD:说明该注解只能被声明在一个类的字段前。
ElementType.METHOD:说明该注解只能被声明在一个类的方法前。
ElementType.PARAMETER:说明该注解只能被声明在一个方法参数前。
ElementType.CONSTRUCTOR:说明该注解只能声明在一个类的构造方法前。
ElementType.LOCAL_VARIABLE:说明该注解只能声明在一个局部变量前。
ElementType.ANNOTATION_TYPE:说明该注解只能声明在一个注解类型前。
ElementType.PACKAGE:说明该注解只能声明在一个包名前
@Retention
-
该元注解有一个RetentionPolicy类型的字段,主要用于标识自定义注解的生命周期
RetentionPolicy.SOURCE: 注解只保留在源文件中
RetentionPolicy.CLASS : 注解保留在class文件中,在加载到JVM虚拟机时丢弃
RetentionPolicy.RUNTIME: 注解保留在程序运行期间,此时可以通过反射获得定义在某个类上的所有注解。
@Inherited
-
用来指示自定义注解是否可以被继承。当自定义注解被
@Inherited
注解时,将自定义注解使用在某个类上,另一个类继承某个类的时候,此注解也会被继承。-
可以能有些拗口,笔者举个例子。在SpringBoot中,@SpringBootApplication也是用了
@Inherited
注解,把以下代码复制到SpringBoot项目中运行,也可以正常启动你的项目。@SpringBootApplication public class RunApp {public static void main(String[] args) {SpringApplication.run(RunApp.class, args);} }
-
@Native
- 指被@Native标注的字段是使用本地语言(如C、C++)实现的方法,该注解告诉Java虚拟机不要执行本地方法的标准Java调用,而是直接调用本地语言实现的方法。再具体一点就比较麻烦了,有好奇心自行搜索
自定义注解
- 在下面的自定义注解中,需要注意的有两点
- 注解的每个属性都有默认值,也可以不给默认值,但是在使用时就必须给没有默认值的字段属性赋值!
- @Constraint注解,此注解主要用于自定义注解关联约束验证器,至于怎么理解约束验证器,如定义了一个注解,约定了使用范围和每个属性的用处,那么怎么校验自定义注解被使用时是否符合你的要求呢
/*** <P>自定义注解完成参数校验</P>** @author unknown* @see com.example.blog.base.common.annotation.MyText* @since 2023/08/25 19:50*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@Constraint(validatedBy = {MyText.class}) // 约束关联校验器 -> 注解定义了,那么在哪里校验这个注解的内容呢?
public @interface MyBlogText {String message() default "不符合格式规范或值域范围";int length() default 0;// 正则校验String regex() default "";// 分组Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};}
自定义注解与SpringBoot全局异常处理完成参数校验
约束验证器
- 在一下代码中,实现
ConstraintValidator
接口,重写isValid
方法,在方法中对MyBlogText
注解进行参数验证
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;/*** <P>自定义注解关联验证器</P>** @author unknown* @since 2023/08/25 19:59*/
public class MyText implements ConstraintValidator<MyBlogText, String> {// 注解校验逻辑@Overridepublic boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {// 判断名字不能为null 并且长度不能唱过20return value != null && value.length() <= 20 && !value.equals("张三");}
}
自定义全局异常处理器
- @RestControllerAdvice :
- 此注解是对所有使用了@RestController注解的Controller层异常的处理,也就是所可以直接在Service层抛出异常,在Controller层也直接抛出异常,在GlobalExceptionHandler进行逐个类型的异常进行处理。如果Controller层用的是Controller注解,对应的可以使用@ControllerAdvice对异常同样的处理。
/*** <P>全局异常处理器</P>** @author unknown* @since 2023/08/05 01:39*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {@Autowiredprivate HttpServletRequest httpServletRequest;/*** <P>Post请求的对象参数校验异常</P>* @param methodArgumentNotValidException* @return*/@ResponseStatus(HttpStatus.BAD_REQUEST)@ExceptionHandler({MethodArgumentNotValidException.class})public Result<String> methodArgumentNotValidHandler(MethodArgumentNotValidException methodArgumentNotValidException) {List<ObjectError> allErrors = methodArgumentNotValidException.getBindingResult().getAllErrors();String requestURI = httpServletRequest.getRequestURI();String validExceptionMsg = getValidExceptionMsg(allErrors);log.error("请求地址'{}',post方式请求参数异常'{}'", requestURI, validExceptionMsg);// 还有一种写法 然后对这个hashMap进行JSON格式化
// List<FieldError> allErrors = methodArgumentNotValidException.getFieldErrors();
// LinkedHashMap<Object, Object> fieldErrors = new LinkedHashMap<>();
// for (FieldError allError : allErrors) {
// fieldErrors.put(allError.getField(), allError.getDefaultMessage());
// }return ResultBuilder.errorResult(ResultCode.INTERNAL_SERVER_ERROR, validExceptionMsg);}
}
自定义注解完成数据脱敏
定义脱敏策略枚举
import java.util.function.Function;/*** @author unknown* @since 2023/08/19 15:04*/
public enum SensitiveStrategy {/*** Username sensitive strategy. $1 替换为正则的第一组 $2 替换为正则的第二组*//*** 地址*/ADDRESS(new Function<String, String>() {@Overridepublic String apply(String s) {return s.replaceAll("(\\S{3})\\S{2}(\\S*)\\S{2}", "$1****$2****");}}),/*** 电子邮件*/EMAIL(new Function<String, String>() {@Overridepublic String apply(String s) {return s.replaceAll("(?<=^.{3}).*(?=@)", "*****");}}),/*** 身份证号码*/ID_CARD(new Function<String, String>() {@Overridepublic String apply(String s) {return s.replaceAll("(\\d{3})\\d{13}(\\w{2})", "$1****$2");}}),/*** 手机号*/PHONE(new Function<String, String>() {@Overridepublic String apply(String s) {return s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");}}),/*** 用户名称*/USERNAME(new Function<String, String>() {@Overridepublic String apply(String s) {return s.replaceAll("(\\S)\\S(\\S*)", "$1*$2");}});private final Function<String, String> deSerializer;/*** 定义构造函数,传入一个函数*/SensitiveStrategy(Function<String, String> deSerializer) {this.deSerializer = deSerializer;}/*** getter方法*/public Function<String, String> deSerializer() {return deSerializer;}}
自定义注解
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** <P>自定义注解完成数据脱敏</P>* @see com.example.blog.base.common.desensitization.SensitiveJsonSerializer* @see com.example.blog.base.common.desensitization.SensitiveStrategy* @author unknown* @since 2023/08/19 15:07*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside //将多个注解组合到一起
@JsonSerialize(using = SensitiveJsonSerializer.class) //声明使用自定义的序列化器
public @interface Sensitive {SensitiveStrategy strategy();
}
实行脱敏逻辑
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;import java.io.IOException;
import java.util.Objects;/*** @author unknown* @since 2023/08/19 15:13*/
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {private SensitiveStrategy strategy;//serialize方法执行脱敏序列化逻辑@Overridepublic void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {// 返一个Function
// Function<String, String> stringStringFunction = strategy.deSerializer();// Function.apply(value) 执行枚举里面定义的脱敏方法gen.writeString(strategy.deSerializer().apply(value));}//reateContextual方法用来获取实体类上的@Sensitive注解并根据条件初始化对应的JsonSerializer对象;@Overridepublic JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {Sensitive annotation = property.getAnnotation(Sensitive.class);if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) {this.strategy = annotation.strategy();return this;}return prov.findValueSerializer(property.getType(), property);}
}
自定义注解的与Aop切面
使用主定义注解
- @MyBlogText
/*** <P>新增用户</P>* @see com.example.blog.base.common.annotation.MyBlogText* @param userInsertRequestVO 新增用户请求体* @return Result<T>*/@MyBlogText(message = "李四")@PostMapping(value = "insertUser")public Result<UserInsertResponseVO> insertUser(@Validated @RequestBody UserInsertRequestVO userInsertRequestVO) throws JsonProcessingException {log.info("UserController insertUser Request param: {}", userInsertRequestVO);UserInsertRequestBO userInsertRequestBO = UserConvert.INSTANCE.toInsertUserRequestBO(userInsertRequestVO);UserInsertResponseVO userInsertResponseVO = userService.insertUser(userInsertRequestBO);log.info("UserController insertUser Response param: {}", objectMapper.writeValueAsString(userInsertResponseVO));return ResultBuilder.successResult(userInsertResponseVO);}
定义切面获取注解信息
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;/*** <P>Aop切面注解进行校验</P>** @author unknown* @since 2023/08/27 01:29*/
@Slf4j
@Aspect
@Component
public class MyBlogTextAop {@Around("@annotation(mySysLog)")public Object around(ProceedingJoinPoint point, MyBlogText mySysLog) throws Throwable {// 也可以直接用时 String message = mySysLog.message(); ,但是不能获取切面信息MethodSignature signature = (MethodSignature) point.getSignature();MyBlogText annotation = signature.getMethod().getAnnotation(MyBlogText.class);String message = annotation.message();System.out.println("获取注解上传入的参数: "+message);String name = point.getTarget().getClass().getName();System.out.println("被切面的类相对路径: "+name);String name1 = signature.getName();System.out.println("被切面的类名: "+name1);return point.proceed();}
}
相关文章:
SpringBoot中自定义注解
目录 SpringBoot中自定义注解 关于注解的解释 元注解 Documented Target Retention Inherited Native 自定义注解 自定义注解与SpringBoot全局异常处理完成参数校验 约束验证器 自定义全局异常处理器 自定义注解完成数据脱敏 定义脱敏策略枚举 自定义注解 实行脱…...
《TCP/IP网络编程》阅读笔记--地址族和数据序列
目录 1--IP地址和端口号 2--地址信息的表示 3--网络字节序与地址变换 4--网络地址的初始化与分配 5--Windows部分代码案例 1--IP地址和端口号 IP 地址分为两类: ① IPv4 表示 4 字节地址族; ② IPv6 表示 16 字节地址族; IPv4 标准的 4 …...
【C++】可变参数模板
2023年9月9日,周六下午 这个还是挺难学的,我学了好几天... 在这里我会举大量的示例程序,这样可以有一个更好的理解, 不定期更新。 目录 推荐文章: 示例程序一:拼接字符串 示例程序二:求整…...
WPF Flyout风格动画消息弹出消息提示框
WPF Flyout风格动画消息弹出消息提示框 效果如图: XAML: <Window x:Class"你的名称控件.FlyoutNotication"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xam…...
Spring Boot 集成 Redis
Spring-data-redis 在 Spring 中整合 Redis jedis : 采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用 jedis pool 连接池 lettuce : 采用netty,实例可以再多个线程中进行共享,不存在…...
Java线程之间通信方式
目录 1 线程之间的通信方式主要有以下几种2 共享变量3 锁机制4 条件变量5 信号量6 管道 1 线程之间的通信方式主要有以下几种 在实际开发时,一个进程中往往有很多个线程,大多数线程之间往往不是绝对独立的,比如说我们需要将A和B 两个线程的执…...
【LeetCode-中等题】367. 有效的完全平方数
文章目录 题目方法一:二分查找 题目 方法一:二分查找 找 1 - num 之间的 mid, 开方是整数 就找得到 mid, 不是整数自然找不到mid class Solution { // 二分查找 ;找 1 - num 之间的mid 开方是整数 就找得到 不是…...
英语单词(二)
1.int:整形 2.char:字符型 3.scanner:接受输入,扫描器 4.integer:整数,整形 5.type:类型 6.string:字符串类型 7.double:双精度浮点型...
Django 用相对路径方式引用自定义模块 或 文件
Django的文件夹结构 projectName/websiteName/appName manage.py 所在路径为:D:/projectA/website1/manage.py views.py 所在路径为:D:/projectA/website1/app1/views.py D:/projectA/website1/app1/module1.py 如果要引用自定义模块,引用…...
企业架构LNMP学习笔记22
防盗链原理和实现。 域名A的资源文件,经常被域名B直接调用访问。 而用户经常访问域名B,看到的资源(图片等)以为是域名B的,实际则是域名A的。 但是域名A没有获得任何收益,却要给域名B来源的访问消耗服务器…...
uniapp和小程序设置tabBar和显示与隐藏tabBar
(1)设置tabBar: uni.setTabberItem({ }); wx.setTabberItem({ }); 属性值: indexnumber是tabBar 的哪一项,从左边算起,索引从0开始textstring否tab 上按钮文字iconPathstring否图片路径selectedIc…...
物联网、无线通讯
LAN:局域网 Local Area Network WAN:广域网 Wide Area Network WLAN:无线局域网 Wireless LAN LPWAN:低功耗广域网 Low Power Wide Area Network技术特点无线通信技术应用场景高功耗、高速率的远距离传输3G、4G蜂窝这类传输技术适…...
Pod和容器设计模式
为什么需要Pod 一些应用的实现是需要多个进程配合完成的,由于容器实际上是一个“单进程”模型,如果在容器里启动多个进程会存在进程管理的难题。在Kubernetes里面,实际上会被定义为一个拥有四个容器的Pod。 Pod相当于进程组 Kubernetes 是…...
docker系列(3) - 常用软件安装
文章目录 3. docker安装常用软件3.1 安装nginx3.2 安装redis3.3 安装mysql3.4 部署springboot程序3.4.1 编写dockerfile3.4.2 构建镜像3.4.3 启动镜像 3. docker安装常用软件 3.1 安装nginx docker pull nginx#挂载启动 docker run -it -d \ --namenginx \ --networkpub_netw…...
Apache Hive之数据查询
文章目录 版权声明数据查询环境准备基本查询准备数据select基础查询分组、聚合JOINRLIKE正则匹配UNION联合Sampling采用Virtual Columns虚拟列 版权声明 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明,所有版权属于黑马程序员或相关权利…...
OpenCV---视频操作
用摄像头捕获视频 import cv2 as cv import numpy cap cv.VideoCapture(0) while(cap.isOpened()):ret, frame cap.read() # read() 它返回两个值,第一个是布尔值,表示是否成功读取到一帧,第二个是帧本身。cv.imshow(Video, frame)if c…...
《TCP/IP网络编程》阅读笔记--进程间通信
目录 1--进程间通信 2--pipe()函数 3--代码实例 3-1--pipe1.c 3-2--pipe2.c 3-3--pipe3.c 3-4--保存信息的回声服务器端 1--进程间通信 为了实现进程间通信,使得两个不同的进程间可以交换数据,操作系统必须提供两个进程可以同时访问的内存空间&am…...
mysql中show status参数介绍
Uptime_since_flush_status, 2159061:自上次刷新状态以来的服务器运行时间(以秒为单位)。Uptime, 2159061:服务器的总运行时间(以秒为单位)。Threads_running, 2:当前正在运行的客户端线程数。T…...
Tomcat服务的部署及配置优化
文章目录 1. Tomcat的相关介绍1.1 Tomcat简介1.2 Tomcat的核心组件1.2.1 Web容器1.2.2 Servlet容器1.2.3 JSP容器 1.3 Tomcat的功能组件1.3.1 connector连接器1.3.2 container容器1.3.2.1 子容器及其相关功能 1.4 主要作用1.5 Tmocat处理请求的过程 2. Tomcata服务部署2.1 安装…...
入门力扣自学笔记279 C++ (题目编号:1123)
1123. 最深叶节点的最近公共祖先 题目: 给你一个有根节点 root 的二叉树,返回它 最深的叶节点的最近公共祖先 。 回想一下: 叶节点 是二叉树中没有子节点的节点树的根节点的 深度 为 0,如果某一节点的深度为 d,那它…...
【AIGC专题】Stable Diffusion 从入门到企业级实战0402
一、概述 本章是《Stable Diffusion 从入门到企业级实战》系列的第四部分能力进阶篇《Stable Diffusion ControlNet v1.1 图像精准控制》第02节, 利用Stable Diffusion ControlNet Openpose模型精准控制图像生成。上一节,我们介绍了《Stable Diffusion C…...
【Spring事务】Spring事务的传播机制(通俗易懂)
目录 什么是spring事务 Spring事务的传播机制 什么是spring事务 封装在数据库事务之上的一种事务处理机制。其管理方法有两种,分别是编程式事务以及声明式事务。一般我们使用Transactional进行声明式事务。 Spring事务的传播机制 Spring的事务传播机制种类 传播行…...
使用 Python 的高效相机流
一、说明 让我们谈谈在Python中使用网络摄像头。我有一个简单的任务,从相机读取帧,并在每一帧上运行神经网络。对于一个特定的网络摄像头,我在设置目标 fps 时遇到了问题(正如我现在所理解的——因为相机可以用 mjpeg 格式运行 30…...
pycharm使用
在使用pycharm时,有时一个回车或者一个tab键,缩进的长度不符合预期可以调整设置tab键缩进的长度: 平时工作中,不同的人在编辑代码缩进的时候,有的人喜欢按四个或者六个空格,有的人喜欢按tab键,而…...
C++项目实战——基于多设计模式下的同步异步日志系统-②-相关技术补充(不定参函数)
文章目录 专栏导读不定参函数C风格不定参函数不定参宏函数 专栏导读 🌸作者简介:花想云 ,在读本科生一枚,C/C领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C、Linux 学…...
iOS开发Swift-10-位置授权, cocoapods,API,天气获取,城市获取-和风天气App首页代码
1.获取用户当前所在的位置 在infi中点击加号,选择权限:当用户使用app的时候获取位置权限. 填写使用位置权限的目的. 2.获取用户的经纬度. ViewController: import UIKit import CoreLocationclass ViewController: UIViewController, CLLocationManagerDelegate { //遵循CLL…...
CNN(七):ResNeXt-50算法的思考
🍨 本文为🔗365天深度学习训练营中的学习记录博客🍖 原作者:K同学啊|接辅导、项目定制 在进行ResNeXt-50实战练习时,我也跟其他学员一样有这个疑惑,如下图所示: 反复查看代码,仍然有…...
【人月神话】深入了解软件工程和项目管理
文章目录 👨⚖️《人月神话》的主要观点👨🏫《人月神话》的主要内容👨💻作者介绍 🌸🌸🌸🌷🌷🌷💐💐💐&a…...
52、基于函数式方式开发 Spring WebFlux 应用
★ Spring WebFlux的两种开发方式 1. 采用类似于Spring MVC的注解的方式来开发。此时开发时感觉Spring MVC差异不大,但底层依然是反应式API。2. 使用函数式编程来开发★ 使用函数式方式开发Web Flux 使用函数式开发WebFlux时需要开发两个组件: ▲ Han…...
MySQL的用户管理
1、MySQL的用户管理 (1)创建用户 create user zhang3 identified by 123123;表示创建名称为zhang3的用户,密码设为123123。 (2)了解user表 1)查看用户 select host,user,authentication_string,select…...
专门做学校政府的网站/国内网络营销公司排名
20145120 《Java程序设计》第1周学习总结 教材学习内容总结 刚刚开始学习java,感觉还十分陌生,在第一周的学习中,我知道了java的历史,JVM、JRE和JDK是什么等各种知识。JVM是java虚拟机,windows、linux等平台有对应的JV…...
哪几个网站做acm题目/营销比较成功的品牌
mysql配置文件详解 0x00 mysql配置文件路径 /etc/my.cnf 0x01 详解 #以下选项会被MySQL客户端应用读取。注意只有MySQL附带的客户端应用程序保证可以读取这段内容。如果你想你自己的MySQL应用程序获取这些值。需要在MySQL客户端库初始化的时候指定这些选项。 [client] port 3…...
赤峰网站建设/营销软件网站
◆ 方案背景<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />IWS-ICB解决方案是北京一维天地科技有限公司(微软金牌认证合作伙伴)在微软SharePoint及Office系列产品基础上实现的企业集成协作工作平台解决方…...
桐乡网站设计公司/石家庄关键词优化软件
小易有n块砖块,每一块砖块有一个高度。小易希望利用这些砖块堆砌两座相同高度的塔。为了让问题简单,砖块堆砌就是简单的高度相加,某一块砖只能使用在一座塔中一次。小易现在让能够堆砌出来的两座塔的高度尽量高,小易能否完成呢。 …...
公司网站建设哪家好/百度ai人工智能平台
浏览器的缓存机制提供了可以将用户数据存储在客户端上的方式,可以利用cookie,session等跟服务端进行数据交互。 一、cookie和session cookie和session都是用来跟踪浏览器用户身份的会话方式。 区别: 1、保持状态:cookie保存在浏览器端&#x…...
做电影网站资源哪里来的/最近热搜新闻事件
PS:下载地址在最以下 1.登录 2.副本选择3.地图4. 选择敌人5. 战斗6. 战斗结算7. 地图拓展8. 武将拓展9. 下载地址:点击打开链接转载于:https://www.cnblogs.com/zfyouxi/p/4241032.html...