关注我Github的小伙伴应该理解,之前我开源了一款疾速开发脚手架mall-tiny
,该脚手架继承了mall我的项目的技术栈,领有残缺的权限治理性能。最近抽空把该我的项目反对了Spring Boot 2.7.0
,明天再和大家聊聊这个脚手架,同时聊聊降级我的项目到Spring Boot 2.7.0
的一些留神点,心愿对大家有所帮忙!
SpringBoot实战电商我的项目mall(50k+star)地址:https://github.com/macrozheng/mall
聊聊mall-tiny我的项目
可能有些小伙伴还不理解这个脚手架,咱们先来聊聊它!
我的项目简介
mall-tiny是一款基于SpringBoot+MyBatis-Plus的疾速开发脚手架,目前在Github上已有1100+Star
。它领有残缺的权限治理性能,反对应用MyBatis-Plus代码生成器生成代码,可对接mall我的项目的Vue前端,开箱即用。
我的项目地址:https://github.com/macrozheng...
我的项目演示
mall-tiny我的项目可无缝对接mall-admin-web
前端我的项目,秒变前后端拆散脚手架,因为mall-tiny我的项目仅实现了根底的权限治理性能,所以前端对接后只会展现权限治理相干性能。
前端我的项目地址:https://github.com/macrozheng...
技术选型
这次降级不仅反对了Spring Boot 2.7.0,其余依赖版本也降级到了最新版本。
技术 | 版本 | 阐明 |
---|---|---|
SpringBoot | 2.7.0 | 容器+MVC框架 |
SpringSecurity | 5.7.1 | 认证和受权框架 |
MyBatis | 3.5.9 | ORM框架 |
MyBatis-Plus | 3.5.1 | MyBatis加强工具 |
MyBatis-Plus Generator | 3.5.1 | 数据层代码生成器 |
Swagger-UI | 3.0.0 | 文档生产工具 |
Redis | 5.0 | 分布式缓存 |
Docker | 18.09.0 | 利用容器引擎 |
Druid | 1.2.9 | 数据库连接池 |
Hutool | 5.8.0 | Java工具类库 |
JWT | 0.9.1 | JWT登录反对 |
Lombok | 1.18.24 | 简化对象封装工具 |
数据库表构造
化繁为简,仅保留了权限治理性能相干的9张表,业务简略更加不便定制开发,感觉mall我的项目学习太简单的小伙伴能够先学习下mall-tiny。
接口文档
因为降级了Swagger版本,原来的接口文档拜访门路曾经扭转,最新拜访门路:http://localhost:8080/swagger...
应用流程
降级版本根本不影响之前的应用形式,具体应用流程能够参考最新版README
文件:https://github.com/macrozheng...
降级过程
接下来咱们再来聊聊我的项目降级Spring Boot 2.7.0版本遇到的问题,这些应该是降级该版本的通用问题,你如果想降级2.7.0版本的话,理解下会很有帮忙!
Swagger降级
- 在降级Spring Boot 2.6.x版本的时候,其实Swagger就有肯定的兼容性问题,须要在配置中增加
BeanPostProcessor
这个Bean,具体能够参考降级 SpringBoot 2.6.x 版本后,Swagger 没法用了! ;
/** * Swagger API文档相干配置 * Created by macro on 2018/4/26. */@Configuration@EnableSwagger2public class SwaggerConfig extends BaseSwaggerConfig { @Bean public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) { List<T> copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List<RequestMappingInfoHandlerMapping>) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { throw new IllegalStateException(e); } } }; }}
- 之前咱们通过
@Api
注解的description
属性来配置接口形容的办法曾经被弃用了;
- 咱们能够应用
@Tag
注解来配置接口阐明,并应用@Api
注解中的tags
属性来指定。
Spring Security降级
降级Spring Boot 2.7.0版本后,原来通过继承WebSecurityConfigurerAdapter
来配置的办法曾经被弃用了,仅需配置SecurityFilterChain
Bean即可,具体参考Spring Security最新用法。
/** * SpringSecurity 5.4.x以上新用法配置 * 为防止循环依赖,仅用于配置HttpSecurity * Created by macro on 2019/11/5. */@Configurationpublic class SecurityConfig { @Autowired private IgnoreUrlsConfig ignoreUrlsConfig; @Autowired private RestfulAccessDeniedHandler restfulAccessDeniedHandler; @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Autowired private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; @Autowired private DynamicSecurityService dynamicSecurityService; @Autowired private DynamicSecurityFilter dynamicSecurityFilter; @Bean SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity .authorizeRequests(); //不须要爱护的资源门路容许拜访 for (String url : ignoreUrlsConfig.getUrls()) { registry.antMatchers(url).permitAll(); } //容许跨域申请的OPTIONS申请 registry.antMatchers(HttpMethod.OPTIONS) .permitAll(); // 任何申请须要身份认证 registry.and() .authorizeRequests() .anyRequest() .authenticated() // 敞开跨站申请防护及不应用session .and() .csrf() .disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 自定义权限回绝解决类 .and() .exceptionHandling() .accessDeniedHandler(restfulAccessDeniedHandler) .authenticationEntryPoint(restAuthenticationEntryPoint) // 自定义权限拦截器JWT过滤器 .and() .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); //有动静权限配置时增加动静权限校验过滤器 if(dynamicSecurityService!=null){ registry.and().addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class); } return httpSecurity.build(); }}
MyBatis-Plus降级
MyBatis-Plus从之前的版本升级到了3.5.1版本,用法没有大的扭转,感觉最大的区别就是代码生成器的用法改了。 在之前的用法中咱们是通过new对象而后set各种属性来配置的,具体参考如下代码:
/** * MyBatisPlus代码生成器 * Created by macro on 2020/8/20. */public class MyBatisPlusGenerator { /** * 初始化全局配置 */ private static GlobalConfig initGlobalConfig(String projectPath) { GlobalConfig globalConfig = new GlobalConfig(); globalConfig.setOutputDir(projectPath + "/src/main/java"); globalConfig.setAuthor("macro"); globalConfig.setOpen(false); globalConfig.setSwagger2(true); globalConfig.setBaseResultMap(true); globalConfig.setFileOverride(true); globalConfig.setDateType(DateType.ONLY_DATE); globalConfig.setEntityName("%s"); globalConfig.setMapperName("%sMapper"); globalConfig.setXmlName("%sMapper"); globalConfig.setServiceName("%sService"); globalConfig.setServiceImplName("%sServiceImpl"); globalConfig.setControllerName("%sController"); return globalConfig; }}
而新版的MyBatis-Plus代码生成器曾经改成应用建造者模式来配置了,具体能够参考MyBatisPlusGenerator
类中的代码。
/** * MyBatisPlus代码生成器 * Created by macro on 2020/8/20. */public class MyBatisPlusGenerator { /** * 初始化全局配置 */ private static GlobalConfig initGlobalConfig(String projectPath) { return new GlobalConfig.Builder() .outputDir(projectPath + "/src/main/java") .author("macro") .disableOpenDir() .enableSwagger() .fileOverride() .dateType(DateType.ONLY_DATE) .build(); }}
解决循环依赖问题
- 其实Spring Boot从2.6.x版本曾经开始不举荐应用循环依赖了,如果你的我的项目中应用的循环依赖比拟多的话,能够应用如下配置开启;
spring: main: allow-circular-references: true
- 不过既然官网都不举荐应用了,咱们最好还是防止循环依赖的好,这里分享下我解决循环依赖问题的一点思路。
如果一个类里有多个依赖项,这个类非必要的Bean就不要配置了,能够应用独自的类来配置Bean
。比方SecurityConfig
这个配置类中,我只申明了必要的SecurityFilterChain
配置;
/** * SpringSecurity 5.4.x以上新用法配置 * 为防止循环依赖,仅用于配置HttpSecurity * Created by macro on 2019/11/5. */@Configurationpublic class SecurityConfig { @Autowired private IgnoreUrlsConfig ignoreUrlsConfig; @Autowired private RestfulAccessDeniedHandler restfulAccessDeniedHandler; @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Autowired private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; @Autowired private DynamicSecurityService dynamicSecurityService; @Autowired private DynamicSecurityFilter dynamicSecurityFilter; @Bean SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { //省略若干代码... return httpSecurity.build(); }}
- 其余配置都被我挪动到了
CommonSecurityConfig
配置类中,这样就防止了之前的循环依赖;
/** * SpringSecurity通用配置 * 包含通用Bean、Security通用Bean及动静权限通用Bean * Created by macro on 2022/5/20. */@Configurationpublic class CommonSecurityConfig { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public IgnoreUrlsConfig ignoreUrlsConfig() { return new IgnoreUrlsConfig(); } @Bean public JwtTokenUtil jwtTokenUtil() { return new JwtTokenUtil(); } @Bean public RestfulAccessDeniedHandler restfulAccessDeniedHandler() { return new RestfulAccessDeniedHandler(); } @Bean public RestAuthenticationEntryPoint restAuthenticationEntryPoint() { return new RestAuthenticationEntryPoint(); } @Bean public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){ return new JwtAuthenticationTokenFilter(); } @Bean public DynamicAccessDecisionManager dynamicAccessDecisionManager() { return new DynamicAccessDecisionManager(); } @Bean public DynamicSecurityMetadataSource dynamicSecurityMetadataSource() { return new DynamicSecurityMetadataSource(); } @Bean public DynamicSecurityFilter dynamicSecurityFilter(){ return new DynamicSecurityFilter(); }}
- 还有一个典型的循环依赖问题,
UmsAdminServiceImpl
和UmsAdminCacheServiceImpl
相互依赖了;
/** * 后盾管理员治理Service实现类 * Created by macro on 2018/4/26. */@Servicepublic class UmsAdminServiceImpl extends ServiceImpl<UmsAdminMapper,UmsAdmin> implements UmsAdminService { @Autowired private UmsAdminCacheService adminCacheService;}/** * 后盾用户缓存治理Service实现类 * Created by macro on 2020/3/13. */@Servicepublic class UmsAdminCacheServiceImpl implements UmsAdminCacheService { @Autowired private UmsAdminService adminService;}
- 咱们能够创立一个用于获取Spring容器中的Bean的工具类来实现;
/** * Spring工具类 * Created by macro on 2020/3/3. */@Componentpublic class SpringUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; // 获取applicationContext public static ApplicationContext getApplicationContext() { return applicationContext; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (SpringUtil.applicationContext == null) { SpringUtil.applicationContext = applicationContext; } } // 通过name获取Bean public static Object getBean(String name) { return getApplicationContext().getBean(name); } // 通过class获取Bean public static <T> T getBean(Class<T> clazz) { return getApplicationContext().getBean(clazz); } // 通过name,以及Clazz返回指定的Bean public static <T> T getBean(String name, Class<T> clazz) { return getApplicationContext().getBean(name, clazz); }}
- 而后在
UmsAdminServiceImpl
中应用该工具类获取Bean来解决循环依赖。
/** * 后盾管理员治理Service实现类 * Created by macro on 2018/4/26. */@Servicepublic class UmsAdminServiceImpl extends ServiceImpl<UmsAdminMapper,UmsAdmin> implements UmsAdminService { @Override public UmsAdminCacheService getCacheService() { return SpringUtil.getBean(UmsAdminCacheService.class); }}
解决跨域问题
在应用Spring Boot 2.7.0版本时,如果不批改之前的跨域配置,通过前端拜访会呈现跨域问题,后端报错如下。
java.lang.IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead.
具体的意思就是allowedOrigins
曾经不再反对通配符*
的配置了,改为须要应用allowedOriginPatterns
来设置,具体配置批改如下。
/** * 全局跨域配置 * Created by macro on 2019/7/27. */@Configurationpublic class GlobalCorsConfig { /** * 容许跨域调用的过滤器 */ @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); //容许所有域名进行跨域调用 config.addAllowedOriginPattern("*"); //该用法在SpringBoot 2.7.0中已不再反对 //config.addAllowedOrigin("*"); //容许逾越发送cookie config.setAllowCredentials(true); //放行全副原始头信息 config.addAllowedHeader("*"); //容许所有申请办法跨域调用 config.addAllowedMethod("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); }}
总结
明天分享了下我的开源我的项目脚手架mall-tiny
,以及它降级SpringBoot 2.7.0的过程。咱们在写代码的时候,如果有些用法曾经废除,应该尽量去寻找新的用法来应用,这样能力保障咱们的代码足够优雅!
我的项目地址
开源不易,感觉我的项目有帮忙的小伙伴点个Star
反对下吧!
https://github.com/macrozheng...