# loggerAspect **Repository Path**: EmperorHeart/logger-aspect ## Basic Information - **Project Name**: loggerAspect - **Description**: 自定义注解,实现在方法上、类上、启动类上进行包扫描,以此来实现日志增强 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-06-18 - **Last Updated**: 2026-01-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 简介 打造一套开发常用的注解中心仓库,提供自定义的可扩展性方案。深挖著名框架(lombok、hibernate、spring、...)没有直接干预的灰色地带。 在那里扎根发展、茁壮成长。为开源社区贡献绵薄之力。 【让天下没有难干的开发】 # 1.日志切面 a. 使用`@Log`注解,可以标记在类上(对类中的所有方法)、方法上,表示对标记的方法进行切面处理,打印入参、出参日志。 默认由`com.freeheart.aop.log.DefaultLogInterceptor`类提供切面日志逻辑,如果用户需要自定义日志切面逻辑, 可以通过实现:`LogInterceptor`接口 提供实现类,并注入Spring容器。 b. 如果需要标记的类过多,可使用:`@LogScan`注解在启动类上,指定需要扫描的包路径,将会对所有指定包下的所有类的所有方法进行日志增强。 如果某个方法被`@Log`和`@LogScan`同时选中,只会生效一次。 c. 如果存在某个方法不需要日志增强,可使用`@LogIgnore`来实现忽略功能,针对:`@Log`和`@LogScan`均有效。 # 2.防重复提交 a. 通过标记`@AntiRepeatSubmit`注解,来对标记方法进行防重复提交加锁保护。需要提供锁的key,以及如果拿不到锁的策略。 * key: 支持Spel表达式,使用 `#参数名` 可以表示标记方法的参数值。使用`@beanname` 可以从Spring 容器中获取Bean,并调用方法。 * RefuseStrategy:拿不到锁时的返回策略(返回Null,或者抛异常) b. 默认实现加锁、释放锁的实现类是:`com.freeheart.aop.antireapte.SimpleAntiRepeatLock`,实质是采用ReentrantLock,只能单体项目用, 如果是分布式项目,实现分布式锁,需要实现:`AntiRepeatLock`接口,并注入Spring容器中即可。 # 3.基础校验 a. 对于Controller中的入参参数校验工作,一般配合:JSR303、JSR380注解(javax、jakarta包下的)就可以实现功能, 但还是存在一些注解无法处理的情况,例如:参数之间具有协同关系,需要一起校验。 可以对入参实现:`BaseValid`接口,重写:valid 方法,此时:在Controller层接受外部请求时,先进行:JSR303的属性校验、然后在切面执行 BaseValid实现类的valid方法进行校验,无须手动显示进行校验。当校验失败后,抛出`BaseValidFailException`异常,记得在全局异常处理器 哪里捕获异常进行处理。 b. 可以在yml配置`base-valid.packages`来指定需要需要校验的包。 # 4.敏捷脱敏 只需要在配置文件中进行配置声明,即可实现脱敏,支持Jackson、Fastjson、Gson的序列化。 如果数量较少,也提供注解方案。 # 5.Filter调序 在Spring中使用Servlet的Filter时,如果使用@WebFilter + @ServletComponentScan方式,实现Ordered接口或标记@Orded注解, **无法实现顺序指定**。 如果在Filter上面标记@Component,实现Ordered接口或标记@Orded注解,**可以实现顺序指定**,但会遗失拦截路径(@WebFilter上的urlPatterns属性)。 如果想在第一种方案下,让顺序生效,只需在启动类上:标记`@EnableFilterOrder`注解即可生效。 # 6.最近时间校验 a. JSR303中对于时间的校验注解:@Future、@FutureOrPresent、@Past、@PastOrPresent。 现在有一个关于最近两天的时间校验,上述注解不能满足要求,为此开发最近时间校验注解。 注解可以放在属性上、也可以放在集合中元素上标记。如下方法使用: 支持对LocalDate、LocalDateTime、Date、String类型的属性进行校验。最近两天,指的是:今天和昨天。 如果用户想实现对其他类型的属性校验支持,可以通过SPI机制实现:首先,提供`ComputeGap`接口提供实现类;然后,在 `resource/META-INF/services/com.freeheart.validation.constraintvalidators.recent.ComputeGap` 中写入实现类的全限定类名,即可支持用户定义的类型解析(为啥不注入Spring容器?因为这个校验不一定是依赖Spring容器,可独立校验)。 注意:自定义的实现类,需要实现Ordered接口、或者标记@Order注解来声明顺序,默认LocalDate、LocalDateTime、Date、String的这4个 类型解析的处理器都没有声明顺序,默认是最小值。只要用户提供顺序在4个之前,那么先过用户的类型解析计算器。 ```java import jakarta.validation.Valid; @Data public class RecentDto { @Recent(2) private LocalDate localDate; @Recent(2) private LocalDateTime localDateTime; @Recent(2) private Date date; @Recent(value = 2,formater = "yyyy-MM-dd HH:mm:ss") private String dateString; // 对元素属性校验也是可以的 @Valid private Set<@Recent(2) String> dateStringSet; } ``` 下面对注解属性进行逐一介绍: * value:最近多少时间单位(天、时、分、秒、毫秒、微妙、纳秒)。 * timeUnit:时间单位:天、时、分、秒、毫秒、微妙、纳秒,默认是天 * truncate:计算时间差时,是否对时间单位进行截断,默认是截断。 * 例如:以 `2025-04-29 22:52:30.465`为例。 * 如果timeUnit为DAYS,截断以后为:2025-04-29 (00:00:00.000)。 * 如果timeUnit为HOURS,截断以后为:2025-04-29 22:00:00.000 * 如果timeUnit为MINUTES,截断以后为:2025-04-29 22:52:00.000 * 如果timeUnit为SECONDS,截断以后为:2025-04-29 22:52:30.000 * containFuture:是否包含未来,当为false,最近两天表示,今天和昨天。如果为true, 表示昨天、今天、明天。即,包括未来的时间区间。 * formater:对于String类型日期、时间,需要指定格式来进行解析,默认是"yyyy-MM-dd", 支持含时分秒的日期格式。 * message:校验失败以后的描述信息。默认:不在最近{value}{timeUnit}内。 其中:{value}表示value,{timeUnit}表示timeUnit的中文翻译。 * group:分组 * payload:携带元信息(不常用) # 7.集合属性校验 需求:校验一个属性是否在某一个集合中,可以使用JSR303中的@Pattern注解来完成, 假设:订单中的支付类型只能是:ALI、WEIXIN、UNION中的一个,可以使用下列方式处理。 ```java @Data public class Order { @Pattern(regexp = "ALI|WEIXIN|UNION") private String tradeType; } ``` 但,如果支付类型很多(事实上也是如此)呢?如果他的集合数量是固定的,使用`|`拼接元素还是可以接收的。 再但,如果它是动态变动的呢?这个类型来自数据库,它是变动的,使用@Pattern注解就有点无能为力了,因此,设计出一个支持 支持动态集合校验的注解。 ```java import com.freeheart.validation.constraints.In; @Data public class Order { @In(TempInCollection.class) private String tradeType; } ``` ```java @Component @EnableScheduling public class TempInCollection implements InCollection, InitializingBean { public static final Set TRADE_TYPE_SET = new HashSet<>(); @Override public Collection getCollection() { return TRADE_TYPE_SET; } // 定时更新这个集合 @Scheduled(cron = "0 */5 * * * *") public void refresh(){ this.updateSet(); } // 初始化就刷新一次 @Override public void afterPropertiesSet() throws Exception { this.updateSet(); } public void updateSet(){ // 查库 } } ``` 先看,@In的关键属性,一个InCollection子类的class。这个子类用来提供校验集合的地址值。 在看子类TempInCollection。重写getCollection方法,提供了一个空的Set集合的地址值。里面没有值。 在Bean初始化的时候,执行afterPropertiesSet方法时,从数据库更新数据到这个集合中。 然后通过定时任务定时(也可以监听数据库数据变动,例如:canal)更新这个集合。这样,就可以保证:集合里面的数据是动态的。 必须是Spring Bean吗?不必须!你可以不用注入Spring容器,只是一个普通的类。但,只有一点要求: 重写getCollection方法的返回值一定要被static修饰,你可以通过其他方法来更新这个集合。 @In属性介绍: * value: InCollection子类的class,提供校验集合地址值 * message:校验失败后的描述,采用{e}表示 校验失败的值