如何搭建SpringBoot+MyBatisPlus快速开发脚手架

聊聊mall-tiny项目

可能有些小伙伴还不了解这个脚手架,我们先来聊聊它!

项目简介

mall-tiny是一款基于SpringBoot+MyBatis-Plus的快速开发脚手架,目前在Github上已有1100+Star。它拥有完整的权限管理功能,支持使用MyBatis-Plus代码生成器生成代码,可对接mall项目的Vue前端,开箱即用。

如何搭建SpringBoot+MyBatisPlus快速开发脚手架

项目演示

mall-tiny项目可无缝对接mall-admin-web前端项目,秒变前后端分离脚手架,由于mall-tiny项目仅实现了基础的权限管理功能,所以前端对接后只会展示权限管理相关功能。

如何搭建SpringBoot+MyBatisPlus快速开发脚手架

技术选型

这次升级不仅支持了Spring Boot 2.7.0,其他依赖版本也升级到了最新版本。

技术 版本 说明

SpringBoot2.7.0容器+MVC框架SpringSecurity5.7.1认证和授权框架MyBatis3.5.9ORM框架MyBatis-Plus3.5.1MyBatis增强工具MyBatis-Plus Generator3.5.1数据层代码生成器Swagger-UI3.0.0文档生产工具Redis5.0分布式缓存Docker18.09.0应用容器引擎Druid1.2.9数据库连接池Hutool5.8.0Java工具类库JWT0.9.1JWT登录支持Lombok1.18.24简化对象封装工具

数据库表结构

化繁为简,仅保留了权限管理功能相关的9张表,业务简单更加方便定制开发,觉得mall项目学习太复杂的小伙伴可以先学习下mall-tiny。

如何搭建SpringBoot+MyBatisPlus快速开发脚手架

接口文档

由于升级了Swagger版本,原来的接口文档访问路径已经改变,最新访问路径:http://localhost:8080/swagger-ui/

如何搭建SpringBoot+MyBatisPlus快速开发脚手架

使用流程

升级版本基本不影响之前的使用方式,具体使用流程可以参考最新版README文件:

如何搭建SpringBoot+MyBatisPlus快速开发脚手架

升级过程

接下来我们再来聊聊项目升级Spring Boot 2.7.0版本遇到的问题,这些应该是升级该版本的通用问题,你如果想升级2.7.0版本的话,了解下会很有帮助!

Swagger升级

在升级Spring Boot 2.6.x版本的时候,其实Swagger就有一定的兼容性问题,需要在配置中添加BeanPostProcessor这个Bean,具体可以参考升级 SpringBoot 2.6.x 版本后,Swagger 没法用了! ;

  1. /** * 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  void customizeSpringfoxHandlerMappings(List mappings) {                List copy = mappings.stream()                        .filter(mapping -> mapping.getPatternParser() == null)                        .collect(Collectors.toList());                mappings.clear();                mappings.addAll(copy);            }            @SuppressWarnings("unchecked")            private List getHandlerMappings(Object bean) {                try {                    Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");                    field.setAccessible(true);                    return (List) field.get(bean);                } catch (IllegalArgumentException | IllegalAccessException e) {                    throw new IllegalStateException(e);                }            }        };    }}

登录后复制

之前我们通过@Api注解的description属性来配置接口描述的方法已经被弃用了;

如何搭建SpringBoot+MyBatisPlus快速开发脚手架

我们可以使用@Tag注解来配置接口说明,并使用@Api注解中的tags属性来指定。

如何搭建SpringBoot+MyBatisPlus快速开发脚手架

Spring Security升级

升级Spring Boot 2.7.0版本后,原来通过继承WebSecurityConfigurerAdapter来配置的方法已经被弃用了,仅需配置SecurityFilterChainBean即可,具体参考Spring Security最新用法。

  1. /** * 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.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版本,用法没有大的改变,感觉最大的区别就是代码生成器的用法改了。以前我们使用的方法是创建一个新对象,然后通过设置各种属性来进行配置,具体例子请参考以下代码:

  1. /** * 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类中的代码。

  1. /** * 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版本已经开始不推荐使用循环依赖了,如果你的项目中使用的循环依赖比较多的话,可以使用如下配置开启;

  1. spring:  main:    allow-circular-references: true

登录后复制

不过既然官方都不推荐使用了,我们最好还是避免循环依赖的好,这里分享下我解决循环依赖问题的一点思路。如果一个类有多个依赖项,可以避免配置不必要的Bean,而应该使用单独的类来进行Bean的配置。比如SecurityConfig这个配置类中,我只声明了必要的SecurityFilterChain配置;

  1. /** * 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配置类中,这样就避免了之前的循环依赖;

  1. /** * 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相互依赖了;

  1. /** * 后台管理员管理Service实现类 * Created by macro on 2018/4/26. */@Servicepublic class UmsAdminServiceImpl extends ServiceImpl 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的工具类来实现;

  1. /** * 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 getBean(Class clazz) {        return getApplicationContext().getBean(clazz);    }    // 通过name,以及Clazz返回指定的Bean    public static  T getBean(String name, Class clazz) {        return getApplicationContext().getBean(name, clazz);    }}

登录后复制

然后在UmsAdminServiceImpl中使用该工具类获取Bean来解决循环依赖。

  1. /** * 后台管理员管理Service实现类 * Created by macro on 2018/4/26. */@Servicepublic class UmsAdminServiceImpl extends ServiceImpl 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来设置,具体配置修改如下。

  1. /** * 全局跨域配置 * 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);    }}

登录后复制

以上就是如何搭建SpringBoot+MyBatisPlus快速开发脚手架的详细内容,更多请关注【创想鸟】其它相关文章!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
编程技术

SpringBoot应用的打包和发布怎么实现

2025-3-7 0:43:21

编程技术

SpringBoot读取Yml配置文件的方法有哪些

2025-3-7 0:43:41

0 条回复 A文章作者 M管理员
欢迎您,新朋友,感谢参与互动!
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
私信列表
搜索