3
头图
Friends who follow me on Github should know that I have open sourced a rapid development scaffold mall-tiny , which inherits the technology stack of the mall project and has complete permission management functions. I recently took the time to support this project Spring Boot 2.7.0 . Today, I will talk to you about this scaffolding and some points for upgrading the project to Spring 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 annotation description attribute has been deprecated;

  • We can use the @Tag annotation to configure the interface description and specify it using the tags 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 example SecurityConfig in this configuration class, I only declare the necessary SecurityFilterChain 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 and UmsAdminCacheServiceImpl 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!

https://github.com/macrozheng/mall-tiny


macrozheng
1.1k 声望1.3k 粉丝