小风筝的学习笔记 - Spring
type: channel
link: https://lxrlog.com/index.php/tag/Spring/
items: 由MybatisPlus配置类未生效引出的问题到SpringBoot复写Bean
items: AOP实现防止前端请求重复提交
items: 自定义Spring转换器解决参数格式转换异常
items: 利用Spring的InitializingBean优雅的实现策略模式

由MybatisPlus配置类未生效引出的问题到SpringBoot复写Bean
type: item
date: 2024-01-12T10:58:00+08:00
description: 1.在某微服务项目中，测试提出一个缺陷：在列表输入框选择条件，不点击查询而是点击翻页，点击后页面刷新，按照条件筛选出数据数量但不显示数据，其实，本质上就是分页查询的页号溢出问题。由于该项目使用的是MybatisPlus，要解决这个问题其实只要写一个MybatisPlus配置类，中设置一个属性即可。 2.但是发现这个配置类设置的这个属性值未生效，在启动类中获取bean发现这个属性依然是false，注释掉这个Bean后发现依然存在这个Bean，这时候就找到在该项目的jar包中已经存在这个Bean导致自己写的Bean未生效。 3.于是思路就是要重写这个Bean，可以实现Spring中的接口：Bean定义注册后置处理器BeanDefinitionRegistryPostProcessor。代码如下：MybatisPlusMyConfigpackage jnpf.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * MyBatisPlus配置 */ @Configuration public class MybatisPlusMyConfig { /** * @Author LiXiangrong * @Description 复写MybatisPlusInterceptor * @Date 2024/01/11 18:04:33 * @Return com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor **/ @Bean("mybatisPlusInterceptor1") public MybatisPlusInterceptor mybatisPlusInterceptor() { // 核心插件 MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 添加分页插件(如果配置多个插件,切记分页最后添加) PaginationInnerInterceptor innerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL); // dbType(数据库类型) innerInterceptor.setOverflow(true); // 默认值：false。溢出总页数后是否进行处理。 interceptor.addInnerInterceptor(innerInterceptor); return interceptor; } }MyBeanDefinitionRegistryPostProcessorpackage jnpf.config; import org.jetbrains.annotations.NotNull; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.stereotype.Component; /** * @Author LiXiangrong * @Description 自定义的Bean定义注册后置处理器 * @Date 2024/01/11 18:06:08 **/ @Component public class MyBeanDefinitionRegistryPostProcessor implements…

AOP实现防止前端请求重复提交
type: item
date: 2024-01-03T16:45:00+08:00
description: 1.防止重复提交注解package com.risesun.common.core.annotation; import java.lang.annotation.*; /** * @Author LiXiangrong * @Description 防止重复提交注解 * @Date 2023/04/03 9:04:15 **/ @Inherited @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RepeatSubmit { /** * 防重复操作限时标记数值(存储redis限时标记数值) */ String value() default "value" ; /** * 防重复操作过期时间(借助redis实现限时控制) */ long expireSeconds() default 10; }2.AOP实现防止请求重复提交package com.risesun.business.aspect; import cn.hutool.crypto.digest.DigestUtil; import cn.hutool.json.JSONUtil; import com.risesun.common.core.annotation.RepeatSubmit; import com.risesun.common.core.constant.TokenConstants; 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.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Objects; import java.util.concurrent.TimeUnit; /** * @Author LiXiangrong * @Description AOP实现防止请求重复提交 * @Date 2023/04/03 8:41:04 **/ @Slf4j @Component @Aspect public class NoRepeatSubmitAspect { @Autowired private RedisTemplate redisTemplate; private final String SIGN = "submit duplication"; private final String PREFIX = "PREVENT_DUPLICATION_PREFIX:"; /** * @Author LiXiangrong * @Description 定义切点 * @Date 2023/04/03 8:53:11 * @Return void **/ @Pointcut("@annotation(com.risesun.common.core.annotation.RepeatSubmit)") public void preventDuplication() {} /** * @Author LiXiangrong * @Description 环绕方法 * @Date 2023/04/03 8:52:50 * @Param pjp * @Return java.lang.Object **…

自定义Spring转换器解决参数格式转换异常
type: item
date: 2024-01-03T16:11:00+08:00
description: 自定义Spring转换器解决参数格式转换异常1.出现的场景当某个实体查询参数类型是Long，Integer，BigDecimal等的时候，前端按照规范如果没有参数应该不传递参数，但是却传递的是字符串“null”的情况下，SpringMVC出现数据格式转换异常的错误。2.利用Spring的转换器拦截请求参数判断null映射成想要的结果import org.apache.commons.lang3.StringUtils; import org.springframework.core.convert.converter.Converter; public class LongConverter implements Converter<String, Long> { @Override public Long convert(String from) { if (StringUtils.isEmpty(from) || "null".equals(from)) { return null; } return Long.valueOf(from); } }3.实现WebMvcConfigurer配置自定义的转换器import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @Author LiXiangrong * @Description 自定义WebConfig * @Date 2023/04/23 10:35:27 **/ @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(new IntegerConverter()); registry.addConverter(new LongConverter()); registry.addConverter(new BigDecimalConverter()); } }

利用Spring的InitializingBean优雅的实现策略模式
type: item
date: 2024-01-03T15:58:00+08:00
description: 当我们实现某个接口时，可能会有很多种不同的实现方式。这些不同的实现方式通过一定的规则可以随意切换使用时，我们就可以考虑使用策略模式来实现。1.首先建一个策略接口去继承InitializingBeanpackage com.example.springbootdemo.strategy; import com.example.springbootdemo.factory.WaitHandleContext; import org.springframework.beans.factory.InitializingBean; /** * @Author LiXiangrong * @Description 策略接口 * @Date 2023/03/13 13:08:20 **/ public interface Strategy extends InitializingBean { /** * @Author LiXiangrong * @Description 要实现的业务方法 * @Date 2023/03/13 13:08:48 * @Return void **/ void method(); @Override default void afterPropertiesSet() { WaitHandleContext.registry(this); } /** * @Author LiXiangrong * @Description getType * @Date 2023/03/13 13:08:43 * @Return java.lang.String **/ String getType(); }2.接下来需要创建一个上下文角色类去注册策略。策略模式的重点是上下文角色类，他封装了对具体策略的调用。上下文角色类中维护着一个抽象角色的引用，高层模块在调用上下文角色类时，通过构造方法或其他方式将具体的策略角色实例赋给该引用，然后调用上下文角色类中的方。通过高层模块在上下文角色类中设置的不同具体策略实例即可执行不同的具体策略。package com.example.springbootdemo.factory; import com.example.springbootdemo.strategy.Strategy; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; public class WaitHandleContext { private WaitHandleContext() { } private static final Map<String, Strategy> strategyMaps = new ConcurrentHashMap<>(); public static Strategy chooseStrategy(String type) { return Optional.ofNullable(strategyMaps.get(type)).orElseThrow(()->new IllegalArgumentException("无对应策略")); } public static void registry(Strategy strategy) { //不注册重复策略 strategyMaps.putIfAbsent(strategy.getType(), strategy); System.out.println("策略"+strategy.getType()+"注册成功！"); } }3.接下来去实现策略接口即可。