一、创建SpringBoot脚手架项目

1.1、使用Spring Initializr 创建项目

1.2、引入依赖

  1. <dependency>
  2. <groupId>org.projectlombok</groupId>
  3. <artifactId>lombok</artifactId>
  4. <optional>true</optional>
  5. </dependency>
  6. <!-- web依赖 -->
  7. <dependency>
  8. <groupId>org.springframework.boot</groupId>
  9. <artifactId>spring-boot-starter-web</artifactId>
  10. </dependency>
  11. <!-- Spring test-->
  12. <dependency>
  13. <groupId>org.springframework.boot</groupId>
  14. <artifactId>spring-boot-starter-test</artifactId>
  15. <scope>test</scope>
  16. </dependency>
  17. <!--hutool 工具集-->
  18. <dependency>
  19. <groupId>cn.hutool</groupId>
  20. <artifactId>hutool-all</artifactId>
  21. <version>${hutool.version}</version>
  22. </dependency>

1.3、配置 Banner 打印

  • 创建Banner 图文件
  • 加载banner.txt文件
    • 放到resources根目录下,自动加载或使用 spring.banner.location 指定位置

1.4、集成Druid + Mybatis-Plus

  • 依赖 ```xml com.alibaba druid-spring-boot-starter
mysql mysql-connector-java org.springframework.boot spring-boot-starter-jdbc

com.baomidou mybatis-plus-boot-starter

  1. - 配置
  2. ```java
  3. spring:
  4. datasource:
  5. type: com.alibaba.druid.pool.DruidDataSource
  6. druid:
  7. driver-class-name: com.mysql.cj.jdbc.Driver
  8. username: 'root'
  9. password: '123456'
  10. url: 'jdbc:mysql://127.0.0.1:3306/blog?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8'
  11. stat-view-servlet:
  12. enabled: true
  13. login-password: 123456
  14. login-username: admin
  15. url-pattern: /druid/*
  16. mybatis-plus:
  17. mapper-locations: classpath*:/mapper/**/*.xml #mapper.xml文件路径
  18. configuration:
  19. map-underscore-to-camel-case: true #驼峰
  • 配置MyBatisPlus自动填充和分页插件

    1. @MapperScan(basePackages = "cn.hdj.fastboot.modules.**.mapper") //扫描mapper接口
    2. @Configuration
    3. public class MyBatisPlusConfig {
    4. /**
    5. * 分页插件
    6. *
    7. * @return
    8. */
    9. @Bean
    10. public PaginationInnerInterceptor paginationInterceptor() {
    11. PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
    12. paginationInterceptor.setOptimizeJoin(true);
    13. return paginationInterceptor;
    14. }
    15. /**
    16. * 元数据自动填充
    17. *
    18. * @return
    19. */
    20. @Bean
    21. public MetaObjectHandler metaObjectHandler() {
    22. return new MetaObjectHandler() {
    23. //插入时,填充createTime 和updateTime字段
    24. @Override
    25. public void insertFill(MetaObject metaObject) {
    26. this.setFieldValByName("createTime", new Date(), metaObject);
    27. this.setFieldValByName("updateTime", new Date(), metaObject);
    28. }
    29. @Override
    30. public void updateFill(MetaObject metaObject) {
    31. this.setFieldValByName("updateTime", new Date(), metaObject);
    32. }
    33. };
    34. }
    35. }

1.5、配置日志

  • 依赖

    • 一般不需要手动在添加到 maven 依赖中
    • 这些依赖已经包含在 spring-boot-starter-web 中
      1. <!-- 添加slf4j日志api -->
      2. <dependency>
      3. <groupId>org.slf4j</groupId>
      4. <artifactId>slf4j-api</artifactId>
      5. </dependency>
      6. <!-- 添加logback-classic依赖 -->
      7. <dependency>
      8. <groupId>ch.qos.logback</groupId>
      9. <artifactId>logback-classic</artifactId>
      10. </dependency>
      11. <!-- 添加logback-core依赖 -->
      12. <dependency>
      13. <groupId>ch.qos.logback</groupId>
      14. <artifactId>logback-core</artifactId>
      15. </dependency>
  • logback-spring.xml 文件配置 ```xml <?xml version=”1.0” encoding=”UTF-8”?>

    fast-boot

  1. <!--0. 日志格式和颜色渲染 -->
  2. <!-- 彩色日志依赖的渲染类 -->
  3. <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
  4. <conversionRule conversionWord="wex"
  5. converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
  6. <conversionRule conversionWord="wEx"
  7. converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
  8. <!-- 彩色日志格式 -->
  9. <property name="CONSOLE_LOG_PATTERN"
  10. value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS} %contextName ) [%thread] %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
  11. <!--1. 输出到控制台-->
  12. <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
  13. <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
  14. <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
  15. <level>debug</level>
  16. </filter>
  17. <encoder>
  18. <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
  19. <!-- 设置字符集 -->
  20. <charset>UTF-8</charset>
  21. </encoder>
  22. </appender>
  23. <!-- %d{HH: mm:ss.SSS}——日志输出时间 -->
  24. <!-- %thread——输出日志的进程名字,这在Web应用以及异步任务处理中很有用 -->
  25. <!-- %-5level——日志级别,并且使用5个字符靠左对齐 -->
  26. <!-- %logger{36}——日志输出者的名字 -->
  27. <!-- %msg——日志消息 -->
  28. <!-- %n——平台的换行符 -->
  29. <!--2. 输出到文档-->
  30. <!-- 2.1 level为 DEBUG 日志,时间滚动输出 -->
  31. <appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  32. <!-- 正在记录的日志文档的路径及文档名 -->
  33. <file>${log.path}/debug.log</file>
  34. <!--日志文档输出格式-->
  35. <encoder>
  36. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
  37. <charset>UTF-8</charset> <!-- 设置字符集 -->
  38. </encoder>
  39. <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
  40. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  41. <!-- 日志归档 -->
  42. <fileNamePattern>${log.path}/debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
  43. <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
  44. <maxFileSize>100MB</maxFileSize>
  45. </timeBasedFileNamingAndTriggeringPolicy>
  46. <!--日志文档保留天数-->
  47. <maxHistory>7</maxHistory>
  48. </rollingPolicy>
  49. <!-- 此日志文档只记录debug级别的 -->
  50. <filter class="ch.qos.logback.classic.filter.LevelFilter">
  51. <level>debug</level>
  52. <onMatch>ACCEPT</onMatch>
  53. <onMismatch>DENY</onMismatch>
  54. </filter>
  55. </appender>
  1. <!-- 4. 最终的策略 -->
  2. <!-- 4.1 开发环境:打印控制台-->
  3. <springProfile name="dev"> <!-- 设置指定包下的级别,这样的话 就可以在控制台输出sql语句了 -->
  4. <logger name="org.springframework.web" level="INFO"/>
  5. <logger name="org.mybatis" level="debug"/>
  6. <logger name="cn.hdj" level="debug"/>
  7. </springProfile>
  8. <root level="info">
  9. <appender-ref ref="CONSOLE"/>
  10. <appender-ref ref="DEBUG_FILE"/>
  11. <appender-ref ref="INFO_FILE"/>
  12. <appender-ref ref="WARN_FILE"/>
  13. <appender-ref ref="ERROR_FILE"/>
  14. </root>
  15. <!-- 4.2 生产环境:输出到文档-->
  16. <springProfile name="prod">
  17. <root level="info">
  18. <appender-ref ref="CONSOLE"/>
  19. <appender-ref ref="DEBUG_FILE"/>
  20. <appender-ref ref="INFO_FILE"/>
  21. <appender-ref ref="ERROR_FILE"/>
  22. <appender-ref ref="WARN_FILE"/>
  23. </root>
  24. </springProfile>

  1. <a name="K4ofz"></a>
  2. ### 1.6、全局异常处理
  3. ```java
  4. @Slf4j
  5. @RestControllerAdvice
  6. public class GlobalExceptionHandler {
  7. /**
  8. * 全局异常类中定义的异常都可以被拦截,只是触发条件不一样,如IO异常这种必须抛出异常到
  9. * controller中才可以被拦截,或者在类中用try..catch自己处理
  10. * 绝大部分不需要向上抛出异常即可被拦截,返回前端json数据,如数组下标越界,404 500 400等错误
  11. * 如果自己想要写,按着以下格式增加异常即可
  12. */
  13. /**
  14. * 启动应用后,被 @ExceptionHandler、@InitBinder、@ModelAttribute 注解的方法,
  15. * 都会作用在 被 @RequestMapping 注解的方法上。
  16. *
  17. * @param binder
  18. */
  19. @InitBinder
  20. public void initWebBinder(WebDataBinder binder) {
  21. }
  22. /**
  23. * 处理自定义异常
  24. *
  25. * @param ex 异常信息
  26. * @return 返回前端异常信息
  27. */
  28. @ExceptionHandler(BaseException.class)
  29. @ResponseBody
  30. public ResultVO exception(BaseException ex) {
  31. log.error("错误详情:" + ex.getMessage(), ex);
  32. return ResultVO.errorJson(ex.getMessage(), ex.getCode());
  33. }
  34. //省略....
  35. /**
  36. * 系统其它异常
  37. *
  38. * @param e
  39. * @return
  40. */
  41. @ResponseBody
  42. @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  43. @ExceptionHandler(Exception.class)
  44. public ResultVO handleException(Exception e) {
  45. log.error("错误详情:" + e.getMessage(), e);
  46. return ResultVO.errorJson(e.getMessage());
  47. }
  48. }

1.7、配置请求响应转换器

  1. @Configuration
  2. public class WebMvcConfig implements WebMvcConfigurer {
  3. /**
  4. * 日期格式化
  5. *
  6. * @param registry
  7. */
  8. @Override
  9. public void addFormatters(FormatterRegistry registry) {
  10. registry.addFormatter(new Formatter<LocalDateTime>() {
  11. @Override
  12. public LocalDateTime parse(String text, Locale locale) {
  13. return DateUtil.parse(text).toTimestamp().toLocalDateTime();
  14. }
  15. @Override
  16. public String print(LocalDateTime object, Locale locale) {
  17. return LocalDateTimeUtil.format(object, DatePattern.NORM_DATETIME_FORMATTER);
  18. }
  19. });
  20. }
  21. /**
  22. * json 消息转换器
  23. *
  24. * @param converters
  25. */
  26. @Override
  27. public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
  28. MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
  29. List<MediaType> mediaTypes = new ArrayList<>(converter.getSupportedMediaTypes());
  30. converter.setSupportedMediaTypes(mediaTypes);
  31. mediaTypes.addAll(Arrays.asList(MediaType.TEXT_PLAIN, MediaType.TEXT_HTML, MediaType.TEXT_XML));
  32. ObjectMapper mapper = new ObjectMapper();
  33. mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  34. //前端Long类型精度丢失
  35. SimpleModule simpleModule = new SimpleModule();
  36. simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
  37. mapper.registerModule(simpleModule);
  38. //java8 LocalDateTime
  39. JavaTimeModule javaTimeModule = new JavaTimeModule();
  40. javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
  41. javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
  42. javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
  43. javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
  44. mapper.registerModule(javaTimeModule);
  45. converter.setObjectMapper(mapper);
  46. converters.add(0, converter);
  47. }
  48. }

1.8、集成 Swagger

  • 依赖

    1. <dependency>
    2. <groupId>com.github.xiaoymin</groupId>
    3. <artifactId>knife4j-spring-boot-starter</artifactId>
    4. <version>3.0.3</version>
    5. </dependency>
  • 配置 ```java @Configuration @EnableSwagger2 public class SwaggerConfig {

    @Bean(value = “defaultApi2”) public Docket defaultApi2() {

    1. Contact contact=new Contact("huangjiajian","https://github.com/h-dj","1432517356@qq.com");
    2. Docket docket=new Docket(DocumentationType.SWAGGER_2)
    3. .apiInfo(new ApiInfoBuilder()
    4. //.title("swagger-bootstrap-ui-demo RESTful APIs")
    5. .description("# swagger-bootstrap-ui-demo RESTful APIs")
    6. .contact(contact)
    7. .version("1.0")
    8. .build())
    9. //分组名称
    10. .groupName("0.0.1版本")
    11. .select()
    12. //这里指定Controller扫描包路径
    13. .apis(RequestHandlerSelectors.basePackage("cn.hdj.fastboot.modules"))
    14. .paths(PathSelectors.any())
    15. .build();
    16. return docket;

    }

}

  1. - swagger2切换swagger3 对应的注解
  2. ```java
  3. @Api → @Tag
  4. @ApiIgnore → @Parameter(hidden = true) or @Operation(hidden = true) or @Hidden
  5. @ApiImplicitParam → @Parameter
  6. @ApiImplicitParams → @Parameters
  7. @ApiModel → @Schema
  8. @ApiModelProperty(hidden = true) → @Schema(accessMode = READ_ONLY)
  9. @ApiModelProperty → @Schema
  10. @ApiOperation(value = "foo", notes = "bar") → @Operation(summary = "foo", description = "bar")
  11. @ApiParam → @Parameter
  12. @ApiResponse(code = 404, message = "foo") → @ApiResponse(responseCode = "404", description = "foo")
  • 处理错误问题

在springboot 2.6.6会提示documentationPluginsBootstrapper NullPointerException

  1. spring:
  2. mvc:
  3. pathmatch:
  4. matching-strategy: ant-path-matcher
  1. @Bean
  2. public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier
  3. , ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier
  4. , EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties
  5. , WebEndpointProperties webEndpointProperties
  6. , Environment environment) {
  7. List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
  8. Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();
  9. allEndpoints.addAll(webEndpoints);
  10. allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());
  11. allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());
  12. String basePath = webEndpointProperties.getBasePath();
  13. EndpointMapping endpointMapping = new EndpointMapping(basePath);
  14. boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);
  15. return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints
  16. , endpointMediaTypes, corsProperties.toCorsConfiguration()
  17. , new EndpointLinksResolver(allEndpoints, basePath)
  18. , shouldRegisterLinksMapping, null);
  19. }
  20. private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {
  21. return webEndpointProperties.getDiscovery().isEnabled()
  22. && (StringUtils.hasText(basePath)
  23. || ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));
  24. }

1.9、整合 Redis

  • 添加依赖

    1. <dependency>
    2. <groupId>org.redisson</groupId>
    3. <artifactId>redisson-spring-boot-starter</artifactId>
    4. <version>${redisson.version}</version>
    5. </dependency>
  • 配置

    1. spring:
    2. redis:
    3. redisson:
    4. file: classpath:redisson.yml
    1. singleServerConfig:
    2. idleConnectionTimeout: 10000
    3. connectTimeout: 10000
    4. timeout: 3000
    5. retryAttempts: 3
    6. retryInterval: 1500
    7. password: 'redis@01234560'
    8. subscriptionsPerConnection: 5
    9. clientName: null
    10. address: "redis://127.0.0.1:6379"
    11. subscriptionConnectionMinimumIdleSize: 1
    12. subscriptionConnectionPoolSize: 50
    13. connectionMinimumIdleSize: 24
    14. connectionPoolSize: 64
    15. database: 0
    16. dnsMonitoringInterval: 5000
    17. threads: 16
    18. nettyThreads: 32
    19. codec: !<org.redisson.codec.JsonJacksonCodec> {}
    20. transportMode: "NIO"
  • 缓存配置 ```java @Slf4j @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport {

  1. private RedisSerializer serializer(){
  2. Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(ICacheEntity.class);
  3. ObjectMapper objectMapper = new ObjectMapper();
  4. // 将类型序列化到属性json字符串中
  5. objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
  6. // 对于找不到匹配属性的时候忽略报错
  7. objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  8. // 不包含任何属性的bean也不报错
  9. objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
  10. serializer.setObjectMapper(objectMapper);
  11. return serializer;
  12. }
  13. /**
  14. * 如使用注解的话需要配置cacheManager
  15. *
  16. * @return
  17. */
  18. @Bean
  19. public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
  20. //覆盖默认的序列化
  21. RedisSerializer serializer = serializer();
  22. //默认配置
  23. RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration
  24. .defaultCacheConfig()
  25. //设置默认超过期时间是7天
  26. .entryTtl(Duration.ofDays(7))
  27. .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer));
  28. return RedisCacheManager.builder(redisConnectionFactory)
  29. .cacheDefaults(defaultCacheConfig)
  30. .build();
  31. }
  32. @Bean
  33. public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  34. //覆盖默认的序列化
  35. RedisSerializer serializer = serializer();
  36. StringRedisSerializer stringRedisSerializer = StringRedisSerializer.UTF_8;
  37. RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
  38. redisTemplate.setConnectionFactory(redisConnectionFactory);
  39. redisTemplate.setKeySerializer(stringRedisSerializer);
  40. redisTemplate.setHashKeySerializer(stringRedisSerializer);
  41. redisTemplate.setHashValueSerializer(serializer);
  42. redisTemplate.setValueSerializer(serializer);
  43. redisTemplate.setDefaultSerializer(serializer);
  44. redisTemplate.afterPropertiesSet();
  45. return redisTemplate;
  46. }
  47. @Override
  48. public CacheErrorHandler errorHandler() {
  49. // 异常处理,当Redis发生异常时,打印日志,但是程序正常走
  50. log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
  51. CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
  52. @Override
  53. public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
  54. log.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
  55. }
  56. @Override
  57. public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
  58. log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);
  59. }
  60. @Override
  61. public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
  62. log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
  63. }
  64. @Override
  65. public void handleCacheClearError(RuntimeException e, Cache cache) {
  66. log.error("Redis occur handleCacheClearError:", e);
  67. }
  68. };
  69. return cacheErrorHandler;
  70. }

}

  1. <a name="NArZX"></a>
  2. ## 二、配置 Maven archetype 生成脚手架
  3. <a name="x5Pj5"></a>
  4. ### 2.1、添加插件
  5. ```java
  6. <!-- 脚手架插件 -->
  7. <plugin>
  8. <groupId>org.apache.maven.plugins</groupId>
  9. <artifactId>maven-archetype-plugin</artifactId>
  10. <version>3.2.1</version>
  11. </plugin>

2.2、配置

  1. # 排除打包到脚手架中的文件
  2. excludePatterns=archetype.properties,*.iml,.idea/,logs/,build.sh
  3. # maven脚手架会丢弃.gitignore文件,需要重命名为__gitignore__,脚手架会把__XX__的文件按照配置进行替换
  4. #gitignore=.gitignore

2.3、生成

  1. mvn archetype:create-from-project -Darchetype.properties=archetype.properties

2.4、安装到本地Maven 仓库

  1. # ./target/generated-sources/archetype 目录下执行
  2. mvn install

2.5、生成项目

  1. mvn archetype:generate \
  2. -DarchetypeGroupId=cn.hdj \
  3. -DarchetypeArtifactId=fast-boot-archetype \
  4. -DarchetypeVersion=0.0.1-SNAPSHOT

三、发布到Maven 仓库

四、使用规范

参考