一、创建SpringBoot脚手架项目
1.1、使用Spring Initializr 创建项目
1.2、引入依赖
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- web依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring test--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--hutool 工具集--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>${hutool.version}</version></dependency>
1.3、配置 Banner 打印
- 创建Banner 图文件
- 加载banner.txt文件
- 放到resources根目录下,自动加载或使用 spring.banner.location 指定位置
1.4、集成Druid + Mybatis-Plus
- 依赖
```xml
com.alibaba druid-spring-boot-starter
- 配置```javaspring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedruid:driver-class-name: com.mysql.cj.jdbc.Driverusername: 'root'password: '123456'url: 'jdbc:mysql://127.0.0.1:3306/blog?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8'stat-view-servlet:enabled: truelogin-password: 123456login-username: adminurl-pattern: /druid/*mybatis-plus:mapper-locations: classpath*:/mapper/**/*.xml #mapper.xml文件路径configuration:map-underscore-to-camel-case: true #驼峰
配置MyBatisPlus自动填充和分页插件
@MapperScan(basePackages = "cn.hdj.fastboot.modules.**.mapper") //扫描mapper接口@Configurationpublic class MyBatisPlusConfig {/*** 分页插件** @return*/@Beanpublic PaginationInnerInterceptor paginationInterceptor() {PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();paginationInterceptor.setOptimizeJoin(true);return paginationInterceptor;}/*** 元数据自动填充** @return*/@Beanpublic MetaObjectHandler metaObjectHandler() {return new MetaObjectHandler() {//插入时,填充createTime 和updateTime字段@Overridepublic void insertFill(MetaObject metaObject) {this.setFieldValByName("createTime", new Date(), metaObject);this.setFieldValByName("updateTime", new Date(), metaObject);}@Overridepublic void updateFill(MetaObject metaObject) {this.setFieldValByName("updateTime", new Date(), metaObject);}};}}
1.5、配置日志
依赖
- 一般不需要手动在添加到 maven 依赖中
- 这些依赖已经包含在 spring-boot-starter-web 中
<!-- 添加slf4j日志api --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId></dependency><!-- 添加logback-classic依赖 --><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId></dependency><!-- 添加logback-core依赖 --><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId></dependency>
logback-spring.xml 文件配置 ```xml <?xml version=”1.0” encoding=”UTF-8”?>
fast-boot
<!--0. 日志格式和颜色渲染 --><!-- 彩色日志依赖的渲染类 --><conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/><conversionRule conversionWord="wex"converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/><conversionRule conversionWord="wEx"converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/><!-- 彩色日志格式 --><property name="CONSOLE_LOG_PATTERN"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}}"/><!--1. 输出到控制台--><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息--><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>debug</level></filter><encoder><Pattern>${CONSOLE_LOG_PATTERN}</Pattern><!-- 设置字符集 --><charset>UTF-8</charset></encoder></appender><!-- %d{HH: mm:ss.SSS}——日志输出时间 --><!-- %thread——输出日志的进程名字,这在Web应用以及异步任务处理中很有用 --><!-- %-5level——日志级别,并且使用5个字符靠左对齐 --><!-- %logger{36}——日志输出者的名字 --><!-- %msg——日志消息 --><!-- %n——平台的换行符 --><!--2. 输出到文档--><!-- 2.1 level为 DEBUG 日志,时间滚动输出 --><appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><!-- 正在记录的日志文档的路径及文档名 --><file>${log.path}/debug.log</file><!--日志文档输出格式--><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern><charset>UTF-8</charset> <!-- 设置字符集 --></encoder><!-- 日志记录器的滚动策略,按日期,按大小记录 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 日志归档 --><fileNamePattern>${log.path}/debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>100MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy><!--日志文档保留天数--><maxHistory>7</maxHistory></rollingPolicy><!-- 此日志文档只记录debug级别的 --><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>debug</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender>
<!-- 4. 最终的策略 --><!-- 4.1 开发环境:打印控制台--><springProfile name="dev"> <!-- 设置指定包下的级别,这样的话 就可以在控制台输出sql语句了 --><logger name="org.springframework.web" level="INFO"/><logger name="org.mybatis" level="debug"/><logger name="cn.hdj" level="debug"/></springProfile><root level="info"><appender-ref ref="CONSOLE"/><appender-ref ref="DEBUG_FILE"/><appender-ref ref="INFO_FILE"/><appender-ref ref="WARN_FILE"/><appender-ref ref="ERROR_FILE"/></root><!-- 4.2 生产环境:输出到文档--><springProfile name="prod"><root level="info"><appender-ref ref="CONSOLE"/><appender-ref ref="DEBUG_FILE"/><appender-ref ref="INFO_FILE"/><appender-ref ref="ERROR_FILE"/><appender-ref ref="WARN_FILE"/></root></springProfile>
<a name="K4ofz"></a>### 1.6、全局异常处理```java@Slf4j@RestControllerAdvicepublic class GlobalExceptionHandler {/*** 全局异常类中定义的异常都可以被拦截,只是触发条件不一样,如IO异常这种必须抛出异常到* controller中才可以被拦截,或者在类中用try..catch自己处理* 绝大部分不需要向上抛出异常即可被拦截,返回前端json数据,如数组下标越界,404 500 400等错误* 如果自己想要写,按着以下格式增加异常即可*//*** 启动应用后,被 @ExceptionHandler、@InitBinder、@ModelAttribute 注解的方法,* 都会作用在 被 @RequestMapping 注解的方法上。** @param binder*/@InitBinderpublic void initWebBinder(WebDataBinder binder) {}/*** 处理自定义异常** @param ex 异常信息* @return 返回前端异常信息*/@ExceptionHandler(BaseException.class)@ResponseBodypublic ResultVO exception(BaseException ex) {log.error("错误详情:" + ex.getMessage(), ex);return ResultVO.errorJson(ex.getMessage(), ex.getCode());}//省略..../*** 系统其它异常** @param e* @return*/@ResponseBody@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)@ExceptionHandler(Exception.class)public ResultVO handleException(Exception e) {log.error("错误详情:" + e.getMessage(), e);return ResultVO.errorJson(e.getMessage());}}
1.7、配置请求响应转换器
@Configurationpublic class WebMvcConfig implements WebMvcConfigurer {/*** 日期格式化** @param registry*/@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addFormatter(new Formatter<LocalDateTime>() {@Overridepublic LocalDateTime parse(String text, Locale locale) {return DateUtil.parse(text).toTimestamp().toLocalDateTime();}@Overridepublic String print(LocalDateTime object, Locale locale) {return LocalDateTimeUtil.format(object, DatePattern.NORM_DATETIME_FORMATTER);}});}/*** json 消息转换器** @param converters*/@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();List<MediaType> mediaTypes = new ArrayList<>(converter.getSupportedMediaTypes());converter.setSupportedMediaTypes(mediaTypes);mediaTypes.addAll(Arrays.asList(MediaType.TEXT_PLAIN, MediaType.TEXT_HTML, MediaType.TEXT_XML));ObjectMapper mapper = new ObjectMapper();mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);//前端Long类型精度丢失SimpleModule simpleModule = new SimpleModule();simpleModule.addSerializer(Long.class, ToStringSerializer.instance);mapper.registerModule(simpleModule);//java8 LocalDateTimeJavaTimeModule javaTimeModule = new JavaTimeModule();javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));mapper.registerModule(javaTimeModule);converter.setObjectMapper(mapper);converters.add(0, converter);}}
1.8、集成 Swagger
依赖
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.3</version></dependency>
配置 ```java @Configuration @EnableSwagger2 public class SwaggerConfig {
@Bean(value = “defaultApi2”) public Docket defaultApi2() {
Contact contact=new Contact("huangjiajian","https://github.com/h-dj","1432517356@qq.com");Docket docket=new Docket(DocumentationType.SWAGGER_2).apiInfo(new ApiInfoBuilder()//.title("swagger-bootstrap-ui-demo RESTful APIs").description("# swagger-bootstrap-ui-demo RESTful APIs").contact(contact).version("1.0").build())//分组名称.groupName("0.0.1版本").select()//这里指定Controller扫描包路径.apis(RequestHandlerSelectors.basePackage("cn.hdj.fastboot.modules")).paths(PathSelectors.any()).build();return docket;
}
}
- swagger2切换swagger3 对应的注解```java@Api → @Tag@ApiIgnore → @Parameter(hidden = true) or @Operation(hidden = true) or @Hidden@ApiImplicitParam → @Parameter@ApiImplicitParams → @Parameters@ApiModel → @Schema@ApiModelProperty(hidden = true) → @Schema(accessMode = READ_ONLY)@ApiModelProperty → @Schema@ApiOperation(value = "foo", notes = "bar") → @Operation(summary = "foo", description = "bar")@ApiParam → @Parameter@ApiResponse(code = 404, message = "foo") → @ApiResponse(responseCode = "404", description = "foo")
- 处理错误问题
在springboot 2.6.6会提示documentationPluginsBootstrapper NullPointerException
spring:mvc:pathmatch:matching-strategy: ant-path-matcher
@Beanpublic WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebEndpointsSupplier webEndpointsSupplier, ServletEndpointsSupplier servletEndpointsSupplier, ControllerEndpointsSupplier controllerEndpointsSupplier, EndpointMediaTypes endpointMediaTypes, CorsEndpointProperties corsProperties, WebEndpointProperties webEndpointProperties, Environment environment) {List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();allEndpoints.addAll(webEndpoints);allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());String basePath = webEndpointProperties.getBasePath();EndpointMapping endpointMapping = new EndpointMapping(basePath);boolean shouldRegisterLinksMapping = this.shouldRegisterLinksMapping(webEndpointProperties, environment, basePath);return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, endpointMediaTypes, corsProperties.toCorsConfiguration(), new EndpointLinksResolver(allEndpoints, basePath), shouldRegisterLinksMapping, null);}private boolean shouldRegisterLinksMapping(WebEndpointProperties webEndpointProperties, Environment environment, String basePath) {return webEndpointProperties.getDiscovery().isEnabled()&& (StringUtils.hasText(basePath)|| ManagementPortType.get(environment).equals(ManagementPortType.DIFFERENT));}
1.9、整合 Redis
添加依赖
<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>${redisson.version}</version></dependency>
配置
spring:redis:redisson:file: classpath:redisson.yml
singleServerConfig:idleConnectionTimeout: 10000connectTimeout: 10000timeout: 3000retryAttempts: 3retryInterval: 1500password: 'redis@01234560'subscriptionsPerConnection: 5clientName: nulladdress: "redis://127.0.0.1:6379"subscriptionConnectionMinimumIdleSize: 1subscriptionConnectionPoolSize: 50connectionMinimumIdleSize: 24connectionPoolSize: 64database: 0dnsMonitoringInterval: 5000threads: 16nettyThreads: 32codec: !<org.redisson.codec.JsonJacksonCodec> {}transportMode: "NIO"
缓存配置 ```java @Slf4j @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport {
private RedisSerializer serializer(){Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(ICacheEntity.class);ObjectMapper objectMapper = new ObjectMapper();// 将类型序列化到属性json字符串中objectMapper.activateDefaultTyping(objectMapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);// 对于找不到匹配属性的时候忽略报错objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);// 不包含任何属性的bean也不报错objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);serializer.setObjectMapper(objectMapper);return serializer;}/*** 如使用注解的话需要配置cacheManager** @return*/@Beanpublic CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {//覆盖默认的序列化RedisSerializer serializer = serializer();//默认配置RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()//设置默认超过期时间是7天.entryTtl(Duration.ofDays(7)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer));return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(defaultCacheConfig).build();}@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {//覆盖默认的序列化RedisSerializer serializer = serializer();StringRedisSerializer stringRedisSerializer = StringRedisSerializer.UTF_8;RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(redisConnectionFactory);redisTemplate.setKeySerializer(stringRedisSerializer);redisTemplate.setHashKeySerializer(stringRedisSerializer);redisTemplate.setHashValueSerializer(serializer);redisTemplate.setValueSerializer(serializer);redisTemplate.setDefaultSerializer(serializer);redisTemplate.afterPropertiesSet();return redisTemplate;}@Overridepublic CacheErrorHandler errorHandler() {// 异常处理,当Redis发生异常时,打印日志,但是程序正常走log.info("初始化 -> [{}]", "Redis CacheErrorHandler");CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {@Overridepublic void handleCacheGetError(RuntimeException e, Cache cache, Object key) {log.error("Redis occur handleCacheGetError:key -> [{}]", key, e);}@Overridepublic void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);}@Overridepublic void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);}@Overridepublic void handleCacheClearError(RuntimeException e, Cache cache) {log.error("Redis occur handleCacheClearError:", e);}};return cacheErrorHandler;}
}
<a name="NArZX"></a>## 二、配置 Maven archetype 生成脚手架<a name="x5Pj5"></a>### 2.1、添加插件```java<!-- 脚手架插件 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-archetype-plugin</artifactId><version>3.2.1</version></plugin>
2.2、配置
# 排除打包到脚手架中的文件excludePatterns=archetype.properties,*.iml,.idea/,logs/,build.sh# maven脚手架会丢弃.gitignore文件,需要重命名为__gitignore__,脚手架会把__XX__的文件按照配置进行替换#gitignore=.gitignore
2.3、生成
mvn archetype:create-from-project -Darchetype.properties=archetype.properties
2.4、安装到本地Maven 仓库
# ./target/generated-sources/archetype 目录下执行mvn install
2.5、生成项目
mvn archetype:generate \-DarchetypeGroupId=cn.hdj \-DarchetypeArtifactId=fast-boot-archetype \-DarchetypeVersion=0.0.1-SNAPSHOT
