溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何搭建SpringBoot+MyBatisPlus快速開發(fā)腳手架

發(fā)布時間:2022-06-21 09:16:26 來源:億速云 閱讀:263 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“如何搭建SpringBoot+MyBatisPlus快速開發(fā)腳手架”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

    聊聊mall-tiny項目

    可能有些小伙伴還不了解這個腳手架,我們先來聊聊它!

    項目簡介

    mall-tiny是一款基于SpringBoot+MyBatis-Plus的快速開發(fā)腳手架,目前在Github上已有1100+Star。它擁有完整的權(quán)限管理功能,支持使用MyBatis-Plus代碼生成器生成代碼,可對接mall項目的Vue前端,開箱即用。

    如何搭建SpringBoot+MyBatisPlus快速開發(fā)腳手架

    項目演示

    mall-tiny項目可無縫對接mall-admin-web前端項目,秒變前后端分離腳手架,由于mall-tiny項目僅實現(xiàn)了基礎(chǔ)的權(quán)限管理功能,所以前端對接后只會展示權(quán)限管理相關(guān)功能。

    如何搭建SpringBoot+MyBatisPlus快速開發(fā)腳手架

    技術(shù)選型

    這次升級不僅支持了Spring Boot 2.7.0,其他依賴版本也升級到了最新版本。

    技術(shù)版本說明
    SpringBoot2.7.0容器+MVC框架
    SpringSecurity5.7.1認證和授權(quán)框架
    MyBatis3.5.9ORM框架
    MyBatis-Plus3.5.1MyBatis增強工具
    MyBatis-Plus Generator3.5.1數(shù)據(jù)層代碼生成器
    Swagger-UI3.0.0文檔生產(chǎn)工具
    Redis5.0分布式緩存
    Docker18.09.0應(yīng)用容器引擎
    Druid1.2.9數(shù)據(jù)庫連接池
    Hutool5.8.0Java工具類庫
    JWT0.9.1JWT登錄支持
    Lombok1.18.24簡化對象封裝工具

    數(shù)據(jù)庫表結(jié)構(gòu)

    化繁為簡,僅保留了權(quán)限管理功能相關(guān)的9張表,業(yè)務(wù)簡單更加方便定制開發(fā),覺得mall項目學(xué)習(xí)太復(fù)雜的小伙伴可以先學(xué)習(xí)下mall-tiny。

    如何搭建SpringBoot+MyBatisPlus快速開發(fā)腳手架

    接口文檔

    由于升級了Swagger版本,原來的接口文檔訪問路徑已經(jīng)改變,最新訪問路徑:http://localhost:8080/swagger-ui/

    如何搭建SpringBoot+MyBatisPlus快速開發(fā)腳手架

    使用流程

    升級版本基本不影響之前的使用方式,具體使用流程可以參考最新版README文件:

    如何搭建SpringBoot+MyBatisPlus快速開發(fā)腳手架

    升級過程

    接下來我們再來聊聊項目升級Spring Boot 2.7.0版本遇到的問題,這些應(yīng)該是升級該版本的通用問題,你如果想升級2.7.0版本的話,了解下會很有幫助!

    Swagger升級

    • 在升級Spring Boot 2.6.x版本的時候,其實Swagger就有一定的兼容性問題,需要在配置中添加BeanPostProcessor這個Bean,具體可以參考升級 SpringBoot 2.6.x 版本后,Swagger 沒法用了! ;

    /**
     * Swagger API文檔相關(guān)配置
     * 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);
                    }
                }
            };
        }
    }
    • 之前我們通過@Api注解的description屬性來配置接口描述的方法已經(jīng)被棄用了;

    如何搭建SpringBoot+MyBatisPlus快速開發(fā)腳手架

    • 我們可以使用@Tag注解來配置接口說明,并使用@Api注解中的tags屬性來指定。

    如何搭建SpringBoot+MyBatisPlus快速開發(fā)腳手架

    Spring Security升級

    升級Spring Boot 2.7.0版本后,原來通過繼承WebSecurityConfigurerAdapter來配置的方法已經(jīng)被棄用了,僅需配置SecurityFilterChainBean即可,具體參考Spring Security最新用法。

    /**
     * SpringSecurity 5.4.x以上新用法配置
     * 為避免循環(huán)依賴,僅用于配置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()
                    // 關(guān)閉跨站請求防護及不使用session
                    .and()
                    .csrf()
                    .disable()
                    .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    // 自定義權(quán)限拒絕處理類
                    .and()
                    .exceptionHandling()
                    .accessDeniedHandler(restfulAccessDeniedHandler)
                    .authenticationEntryPoint(restAuthenticationEntryPoint)
                    // 自定義權(quán)限攔截器JWT過濾器
                    .and()
                    .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
            //有動態(tài)權(quán)限配置時添加動態(tài)權(quán)限校驗過濾器
            if(dynamicSecurityService!=null){
                registry.and().addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
            }
            return httpSecurity.build();
        }
    }

    MyBatis-Plus升級

    MyBatis-Plus從之前的版本升級到了3.5.1版本,用法沒有大的改變,感覺最大的區(qū)別就是代碼生成器的用法改了。 在之前的用法中我們是通過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代碼生成器已經(jīng)改成使用建造者模式來配置了,具體可以參考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();
        }
    }

    解決循環(huán)依賴問題

    • 其實Spring Boot從2.6.x版本已經(jīng)開始不推薦使用循環(huán)依賴了,如果你的項目中使用的循環(huán)依賴比較多的話,可以使用如下配置開啟;

    spring:
      main:
        allow-circular-references: true
    • 不過既然官方都不推薦使用了,我們最好還是避免循環(huán)依賴的好,這里分享下我解決循環(huán)依賴問題的一點思路。如果一個類里有多個依賴項,這個類非必要的Bean就不要配置了,可以使用單獨的類來配置Bean。比如SecurityConfig這個配置類中,我只聲明了必要的SecurityFilterChain配置;

    /**
     * SpringSecurity 5.4.x以上新用法配置
     * 為避免循環(huán)依賴,僅用于配置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();
        }
    }
    • 其他配置都被我移動到了CommonSecurityConfig配置類中,這樣就避免了之前的循環(huán)依賴;

    /**
     * SpringSecurity通用配置
     * 包括通用Bean、Security通用Bean及動態(tài)權(quán)限通用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();
        }
    }
    • 還有一個典型的循環(huán)依賴問題,UmsAdminServiceImpl和UmsAdminCacheServiceImpl相互依賴了;

    /**
     * 后臺管理員管理Service實現(xiàn)類
     * Created by macro on 2018/4/26.
     */
    @Service
    public class UmsAdminServiceImpl extends ServiceImpl<UmsAdminMapper,UmsAdmin> implements UmsAdminService {
        @Autowired
        private UmsAdminCacheService adminCacheService;
    }
    /**
     * 后臺用戶緩存管理Service實現(xiàn)類
     * Created by macro on 2020/3/13.
     */
    @Service
    public class UmsAdminCacheServiceImpl implements UmsAdminCacheService {
        @Autowired
        private UmsAdminService adminService;
    }
    • 我們可以創(chuàng)建一個用于獲取Spring容器中的Bean的工具類來實現(xiàn);

    /**
     * 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);
        }
    }
    • 然后在UmsAdminServiceImpl中使用該工具類獲取Bean來解決循環(huán)依賴。

    /**
     * 后臺管理員管理Service實現(xiàn)類
     * 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);
        }
    }

    解決跨域問題

    在使用Spring Boot 2.7.0版本時,如果不修改之前的跨域配置,通過前端訪問會出現(xiàn)跨域問題,后端報錯如下。

    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已經(jīng)不再支持通配符*的配置了,改為需要使用allowedOriginPatterns來設(shè)置,具體配置修改如下。

    /**
     * 全局跨域配置
     * Created by macro on 2019/7/27.
     */
    @Configuration
    public class GlobalCorsConfig {
        /**
         * 允許跨域調(diào)用的過濾器
         */
        @Bean
        public CorsFilter corsFilter() {
            CorsConfiguration config = new CorsConfiguration();
            //允許所有域名進行跨域調(diào)用
            config.addAllowedOriginPattern("*");
            //該用法在SpringBoot 2.7.0中已不再支持
            //config.addAllowedOrigin("*");
            //允許跨越發(fā)送cookie
            config.setAllowCredentials(true);
            //放行全部原始頭信息
            config.addAllowedHeader("*");
            //允許所有請求方法跨域調(diào)用
            config.addAllowedMethod("*");
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**", config);
            return new CorsFilter(source);
        }
    }

    “如何搭建SpringBoot+MyBatisPlus快速開發(fā)腳手架”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

    向AI問一下細節(jié)

    免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI