经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » MyBatis » 查看文章
从零搭建SpringBoot+MyBatisPlus快速开发脚手架
来源:jb51  时间:2022/6/20 19:56:26  对本文有异议

前言

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

前端项目地址:https://github.com/macrozheng/mall-admin-web

技术选型

这次升级不仅支持了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。

接口文档

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

使用流程

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

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

升级过程

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

Swagger升级

  1. /**
  2. * Swagger API文档相关配置
  3. * Created by macro on 2018/4/26.
  4. */
  5. @Configuration
  6. @EnableSwagger2
  7. public class SwaggerConfig extends BaseSwaggerConfig {
  8. @Bean
  9. public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
  10. return new BeanPostProcessor() {
  11. @Override
  12. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  13. if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
  14. customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
  15. }
  16. return bean;
  17. }
  18. private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
  19. List<T> copy = mappings.stream()
  20. .filter(mapping -> mapping.getPatternParser() == null)
  21. .collect(Collectors.toList());
  22. mappings.clear();
  23. mappings.addAll(copy);
  24. }
  25. @SuppressWarnings("unchecked")
  26. private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
  27. try {
  28. Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
  29. field.setAccessible(true);
  30. return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
  31. } catch (IllegalArgumentException | IllegalAccessException e) {
  32. throw new IllegalStateException(e);
  33. }
  34. }
  35. };
  36. }
  37. }
  • 之前我们通过@Api注解的description属性来配置接口描述的方法已经被弃用了;

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

Spring Security升级

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

  1. /**
  2. * SpringSecurity 5.4.x以上新用法配置
  3. * 为避免循环依赖,仅用于配置HttpSecurity
  4. * Created by macro on 2019/11/5.
  5. */
  6. @Configuration
  7. public class SecurityConfig {
  8. @Autowired
  9. private IgnoreUrlsConfig ignoreUrlsConfig;
  10. @Autowired
  11. private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
  12. @Autowired
  13. private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
  14. @Autowired
  15. private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
  16. @Autowired
  17. private DynamicSecurityService dynamicSecurityService;
  18. @Autowired
  19. private DynamicSecurityFilter dynamicSecurityFilter;
  20. @Bean
  21. SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
  22. ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
  23. .authorizeRequests();
  24. //不需要保护的资源路径允许访问
  25. for (String url : ignoreUrlsConfig.getUrls()) {
  26. registry.antMatchers(url).permitAll();
  27. }
  28. //允许跨域请求的OPTIONS请求
  29. registry.antMatchers(HttpMethod.OPTIONS)
  30. .permitAll();
  31. // 任何请求需要身份认证
  32. registry.and()
  33. .authorizeRequests()
  34. .anyRequest()
  35. .authenticated()
  36. // 关闭跨站请求防护及不使用session
  37. .and()
  38. .csrf()
  39. .disable()
  40. .sessionManagement()
  41. .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  42. // 自定义权限拒绝处理类
  43. .and()
  44. .exceptionHandling()
  45. .accessDeniedHandler(restfulAccessDeniedHandler)
  46. .authenticationEntryPoint(restAuthenticationEntryPoint)
  47. // 自定义权限拦截器JWT过滤器
  48. .and()
  49. .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
  50. //有动态权限配置时添加动态权限校验过滤器
  51. if(dynamicSecurityService!=null){
  52. registry.and().addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
  53. }
  54. return httpSecurity.build();
  55. }
  56. }

MyBatis-Plus升级

MyBatis-Plus从之前的版本升级到了3.5.1版本,用法没有大的改变,感觉最大的区别就是代码生成器的用法改了。 在之前的用法中我们是通过new对象然后set各种属性来配置的,具体参考如下代码:

  1. /**
  2. * MyBatisPlus代码生成器
  3. * Created by macro on 2020/8/20.
  4. */
  5. public class MyBatisPlusGenerator {
  6. /**
  7. * 初始化全局配置
  8. */
  9. private static GlobalConfig initGlobalConfig(String projectPath) {
  10. GlobalConfig globalConfig = new GlobalConfig();
  11. globalConfig.setOutputDir(projectPath + "/src/main/java");
  12. globalConfig.setAuthor("macro");
  13. globalConfig.setOpen(false);
  14. globalConfig.setSwagger2(true);
  15. globalConfig.setBaseResultMap(true);
  16. globalConfig.setFileOverride(true);
  17. globalConfig.setDateType(DateType.ONLY_DATE);
  18. globalConfig.setEntityName("%s");
  19. globalConfig.setMapperName("%sMapper");
  20. globalConfig.setXmlName("%sMapper");
  21. globalConfig.setServiceName("%sService");
  22. globalConfig.setServiceImplName("%sServiceImpl");
  23. globalConfig.setControllerName("%sController");
  24. return globalConfig;
  25. }
  26. }

而新版的MyBatis-Plus代码生成器已经改成使用建造者模式来配置了,具体可以参考MyBatisPlusGenerator类中的代码。

  1. /**
  2. * MyBatisPlus代码生成器
  3. * Created by macro on 2020/8/20.
  4. */
  5. public class MyBatisPlusGenerator {
  6. /**
  7. * 初始化全局配置
  8. */
  9. private static GlobalConfig initGlobalConfig(String projectPath) {
  10. return new GlobalConfig.Builder()
  11. .outputDir(projectPath + "/src/main/java")
  12. .author("macro")
  13. .disableOpenDir()
  14. .enableSwagger()
  15. .fileOverride()
  16. .dateType(DateType.ONLY_DATE)
  17. .build();
  18. }
  19. }

解决循环依赖问题

  • 其实Spring Boot从2.6.x版本已经开始不推荐使用循环依赖了,如果你的项目中使用的循环依赖比较多的话,可以使用如下配置开启;
  1. spring:
  2. main:
  3. allow-circular-references: true
  • 不过既然官方都不推荐使用了,我们最好还是避免循环依赖的好,这里分享下我解决循环依赖问题的一点思路。如果一个类里有多个依赖项,这个类非必要的Bean就不要配置了,可以使用单独的类来配置Bean。比如SecurityConfig这个配置类中,我只声明了必要的SecurityFilterChain配置;
  1. /**
  2. * SpringSecurity 5.4.x以上新用法配置
  3. * 为避免循环依赖,仅用于配置HttpSecurity
  4. * Created by macro on 2019/11/5.
  5. */
  6. @Configuration
  7. public class SecurityConfig {
  8. @Autowired
  9. private IgnoreUrlsConfig ignoreUrlsConfig;
  10. @Autowired
  11. private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
  12. @Autowired
  13. private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
  14. @Autowired
  15. private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
  16. @Autowired
  17. private DynamicSecurityService dynamicSecurityService;
  18. @Autowired
  19. private DynamicSecurityFilter dynamicSecurityFilter;
  20. @Bean
  21. SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
  22. //省略若干代码...
  23. return httpSecurity.build();
  24. }
  25. }
  • 其他配置都被我移动到了CommonSecurityConfig配置类中,这样就避免了之前的循环依赖;
  1. /**
  2. * SpringSecurity通用配置
  3. * 包括通用Bean、Security通用Bean及动态权限通用Bean
  4. * Created by macro on 2022/5/20.
  5. */
  6. @Configuration
  7. public class CommonSecurityConfig {
  8. @Bean
  9. public PasswordEncoder passwordEncoder() {
  10. return new BCryptPasswordEncoder();
  11. }
  12. @Bean
  13. public IgnoreUrlsConfig ignoreUrlsConfig() {
  14. return new IgnoreUrlsConfig();
  15. }
  16. @Bean
  17. public JwtTokenUtil jwtTokenUtil() {
  18. return new JwtTokenUtil();
  19. }
  20. @Bean
  21. public RestfulAccessDeniedHandler restfulAccessDeniedHandler() {
  22. return new RestfulAccessDeniedHandler();
  23. }
  24. @Bean
  25. public RestAuthenticationEntryPoint restAuthenticationEntryPoint() {
  26. return new RestAuthenticationEntryPoint();
  27. }
  28. @Bean
  29. public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){
  30. return new JwtAuthenticationTokenFilter();
  31. }
  32. @Bean
  33. public DynamicAccessDecisionManager dynamicAccessDecisionManager() {
  34. return new DynamicAccessDecisionManager();
  35. }
  36. @Bean
  37. public DynamicSecurityMetadataSource dynamicSecurityMetadataSource() {
  38. return new DynamicSecurityMetadataSource();
  39. }
  40. @Bean
  41. public DynamicSecurityFilter dynamicSecurityFilter(){
  42. return new DynamicSecurityFilter();
  43. }
  44. }
  • 还有一个典型的循环依赖问题,UmsAdminServiceImpl和UmsAdminCacheServiceImpl相互依赖了;
  1. /**
  2. * 后台管理员管理Service实现类
  3. * Created by macro on 2018/4/26.
  4. */
  5. @Service
  6. public class UmsAdminServiceImpl extends ServiceImpl<UmsAdminMapper,UmsAdmin> implements UmsAdminService {
  7. @Autowired
  8. private UmsAdminCacheService adminCacheService;
  9. }
  10. /**
  11. * 后台用户缓存管理Service实现类
  12. * Created by macro on 2020/3/13.
  13. */
  14. @Service
  15. public class UmsAdminCacheServiceImpl implements UmsAdminCacheService {
  16. @Autowired
  17. private UmsAdminService adminService;
  18. }
  • 我们可以创建一个用于获取Spring容器中的Bean的工具类来实现;
  1. /**
  2. * Spring工具类
  3. * Created by macro on 2020/3/3.
  4. */
  5. @Component
  6. public class SpringUtil implements ApplicationContextAware {
  7. private static ApplicationContext applicationContext;
  8. // 获取applicationContext
  9. public static ApplicationContext getApplicationContext() {
  10. return applicationContext;
  11. }
  12. @Override
  13. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  14. if (SpringUtil.applicationContext == null) {
  15. SpringUtil.applicationContext = applicationContext;
  16. }
  17. }
  18. // 通过name获取Bean
  19. public static Object getBean(String name) {
  20. return getApplicationContext().getBean(name);
  21. }
  22. // 通过class获取Bean
  23. public static <T> T getBean(Class<T> clazz) {
  24. return getApplicationContext().getBean(clazz);
  25. }
  26. // 通过name,以及Clazz返回指定的Bean
  27. public static <T> T getBean(String name, Class<T> clazz) {
  28. return getApplicationContext().getBean(name, clazz);
  29. }
  30. }
  • 然后在UmsAdminServiceImpl中使用该工具类获取Bean来解决循环依赖。
  1. /**
  2. * 后台管理员管理Service实现类
  3. * Created by macro on 2018/4/26.
  4. */
  5. @Service
  6. public class UmsAdminServiceImpl extends ServiceImpl<UmsAdminMapper,UmsAdmin> implements UmsAdminService {
  7. @Override
  8. public UmsAdminCacheService getCacheService() {
  9. return SpringUtil.getBean(UmsAdminCacheService.class);
  10. }
  11. }

解决跨域问题

在使用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. /**
  2. * 全局跨域配置
  3. * Created by macro on 2019/7/27.
  4. */
  5. @Configuration
  6. public class GlobalCorsConfig {
  7. /**
  8. * 允许跨域调用的过滤器
  9. */
  10. @Bean
  11. public CorsFilter corsFilter() {
  12. CorsConfiguration config = new CorsConfiguration();
  13. //允许所有域名进行跨域调用
  14. config.addAllowedOriginPattern("*");
  15. //该用法在SpringBoot 2.7.0中已不再支持
  16. //config.addAllowedOrigin("*");
  17. //允许跨越发送cookie
  18. config.setAllowCredentials(true);
  19. //放行全部原始头信息
  20. config.addAllowedHeader("*");
  21. //允许所有请求方法跨域调用
  22. config.addAllowedMethod("*");
  23. UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
  24. source.registerCorsConfiguration("/**", config);
  25. return new CorsFilter(source);
  26. }
  27. }

总结

今天分享了下我的开源项目脚手架mall-tiny,以及它升级SpringBoot 2.7.0的过程。我们在写代码的时候,如果有些用法已经废弃,应该尽量去寻找新的用法来使用,这样才能保证我们的代码足够优雅!

项目地址 https://github.com/macrozheng/mall-tiny

以上就是从零搭建SpringBoot+MyBatisPlus快速开发脚手架的详细内容,更多关于SpringBoot+MyBatisPlus从零搭建的资料请关注w3xue其它相关文章!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号