Friends who follow me on Github should know that I have open sourced a rapid development scaffoldmall-tiny
, which inherits the technology stack of the mall project and has complete permission management functions. I recently took the time to support this projectSpring Boot 2.7.0
. Today, I will talk to you about this scaffolding and some points for upgrading the project toSpring Boot 2.7.0
. I hope it will be helpful to everyone!
SpringBoot actual e-commerce project mall (50k+star) address: https://github.com/macrozheng/mall
Talk about the mall-tiny project
Maybe some friends don't know this scaffolding yet, let's talk about it first!
Project Description
mall-tiny is a rapid development scaffold based on SpringBoot+MyBatis-Plus, currently available on Github 1100+Star
. It has a complete permission management function, supports the use of the MyBatis-Plus code generator to generate code, can be connected to the Vue front end of the mall project, and can be used out of the box.
Project address: https://github.com/macrozheng/mall-tiny
Project demo
The mall-tiny project can be seamlessly mall-admin-web
the front-end project, and the front-end and back-end separate scaffolding can be changed in seconds. Since the mall-tiny project only implements basic rights management functions, only the rights management related functions will be displayed after the front-end docking. .
Front-end project address: https://github.com/macrozheng/mall-admin-web
Technical selection
This upgrade not only supports Spring Boot 2.7.0, but other dependent versions have also been upgraded to the latest version.
technology | Version | illustrate |
---|---|---|
SpringBoot | 2.7.0 | Container + MVC framework |
SpringSecurity | 5.7.1 | Authentication and Authorization Framework |
MyBatis | 3.5.9 | ORM framework |
MyBatis-Plus | 3.5.1 | MyBatis Enhancement Tool |
MyBatis-Plus Generator | 3.5.1 | Data Layer Code Generator |
Swagger-UI | 3.0.0 | document production tool |
Redis | 5.0 | Distributed cache |
Docker | 18.09.0 | Application Container Engine |
Druid | 1.2.9 | database connection pool |
Hutool | 5.8.0 | Java tool class library |
JWT | 0.9.1 | JWT login support |
Lombok | 1.18.24 | Simplified Object Encapsulation Tool |
database table structure
To simplify the complex, only 9 tables related to the authority management function are retained. The business is simple and more convenient for customized development. Those who feel that the mall project is too complicated to learn can learn mall-tiny first.
interface documentation
Due to the upgraded Swagger version, the original interface document access path has been changed, the latest access path: http://localhost:8080/swagger-ui/
manual
The upgraded version basically does not affect the previous usage. For the specific usage process, please refer to the latest version README
file: https://github.com/macrozheng/mall-tiny
upgrade process
Next, let's talk about the problems encountered in upgrading the Spring Boot 2.7.0 version of the project. These should be the general problems of upgrading this version. If you want to upgrade the 2.7.0 version, it will be very helpful to understand!
Swagger upgrade
- When upgrading Spring Boot 2.6.x version, in fact, Swagger has certain compatibility problems, and needs to add
BeanPostProcessor
this bean to the configuration. For details, please refer to Swagger after upgrading Spring Boot 2.6.x version. used! ;
/**
* Swagger API文档相关配置
* Created by macro on 2018/4/26.
*/
@Configuration
@EnableSwagger2
public 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);
}
}
};
}
}
- The method we used to configure the interface description through the
@Api
annotationdescription
attribute has been deprecated;
- We can use the
@Tag
annotation to configure the interface description and specify it using thetags
attribute in the ---@Api
annotation.
Spring Security upgrade
After upgrading Spring Boot 2.7.0 version, the original method of configuring by inheriting WebSecurityConfigurerAdapter
has been deprecated. You only need to configure SecurityFilterChain
Bean. For details, please refer to the latest usage of Spring Security .
/**
* SpringSecurity 5.4.x以上新用法配置
* 为避免循环依赖,仅用于配置HttpSecurity
* Created by macro on 2019/11/5.
*/
@Configuration
public 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 upgrade
MyBatis-Plus has been upgraded from the previous version to version 3.5.1, and the usage has not changed much. I feel that the biggest difference is that the usage of the code generator has been changed. In the previous usage, we configured the new object and then set various properties. For details, refer to the following code:
/**
* 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;
}
}
The new version of the MyBatis-Plus code generator has been changed to use the builder mode to configure. For details, please refer to the code in the MyBatisPlusGenerator
class.
/**
* 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();
}
}
Solve circular dependencies
- In fact, Spring Boot has deprecated the use of circular dependencies since version 2.6.x. If there are many circular dependencies used in your project, you can use the following configuration to enable it;
spring:
main:
allow-circular-references: true
- However, since the official use is not recommended, it is better to avoid circular dependencies. Here I share some ideas for solving the circular dependency problem.
如果一个类里有多个依赖项,这个类非必要的Bean就不要配置了,可以使用单独的类来配置Bean
. For exampleSecurityConfig
in this configuration class, I only declare the necessarySecurityFilterChain
configuration;
/**
* SpringSecurity 5.4.x以上新用法配置
* 为避免循环依赖,仅用于配置HttpSecurity
* Created by macro on 2019/11/5.
*/
@Configuration
public 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();
}
}
- All other configurations have been moved to
CommonSecurityConfig
configuration class, thus avoiding the previous circular dependency;
/**
* SpringSecurity通用配置
* 包括通用Bean、Security通用Bean及动态权限通用Bean
* Created by macro on 2022/5/20.
*/
@Configuration
public 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();
}
}
- There is also a typical circular dependency problem,
UmsAdminServiceImpl
andUmsAdminCacheServiceImpl
depend on each other;
/**
* 后台管理员管理Service实现类
* Created by macro on 2018/4/26.
*/
@Service
public class UmsAdminServiceImpl extends ServiceImpl<UmsAdminMapper,UmsAdmin> implements UmsAdminService {
@Autowired
private UmsAdminCacheService adminCacheService;
}
/**
* 后台用户缓存管理Service实现类
* Created by macro on 2020/3/13.
*/
@Service
public class UmsAdminCacheServiceImpl implements UmsAdminCacheService {
@Autowired
private UmsAdminService adminService;
}
- We can create a tool class for getting beans in the Spring container to achieve this;
/**
* Spring工具类
* Created by macro on 2020/3/3.
*/
@Component
public 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);
}
}
- Then use this tool class in
UmsAdminServiceImpl
to get beans to resolve circular dependencies.
/**
* 后台管理员管理Service实现类
* Created by macro on 2018/4/26.
*/
@Service
public class UmsAdminServiceImpl extends ServiceImpl<UmsAdminMapper,UmsAdmin> implements UmsAdminService {
@Override
public UmsAdminCacheService getCacheService() {
return SpringUtil.getBean(UmsAdminCacheService.class);
}
}
Solve cross-domain problems
When using Spring Boot version 2.7.0, if the previous cross-domain configuration is not modified, cross-domain problems will occur through front-end access, and the back-end error is reported as follows.
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.
The specific meaning is that allowedOrigins
no longer supports the configuration of wildcard *
, instead, it needs to use allowedOriginPatterns
to set, the specific configuration is modified as follows.
/**
* 全局跨域配置
* Created by macro on 2019/7/27.
*/
@Configuration
public 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);
}
}
Summarize
Today I shared my open source project scaffolding mall-tiny
and its process of upgrading SpringBoot 2.7.0. When we write code, if some usages have been abandoned, we should try our best to find new usages to use, so as to ensure that our code is elegant enough!
project address
Open source is not easy, friends who think the project is helpful, please click Star
to support it!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。