SpringBoot1.x-SpringBoot2.1.x变更,切勿踩坑

org.springframework.web.servlet.config.annotation.WebMvcConfigurer 的变更

WebMvcConfigurer是一个接口,用于自定义HttpMessageConverter,自定义json转换工具(fastjson),ViewResolver视图解析器,Interceptor拦截器等

去除其低版本中的适配器org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter,直接采用java8中的default关键字实现。

SpringBoot中有该接口的一个默认实现
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter
在低版本中并没有准确的控制多个WebMvcConfigurer之间的顺序, 自定义的WebConfig可能在SpringBoot默认提供的之前执行,升级SpringBoot2.x之后可能在其之后执行。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
原因: SpringBoot2.x中对其默认实现添加了@Order(0)注解用于排序,
解决办法: 同样使用@Order注解控制自定义的WebMvcConfigurer执行顺序

SpringBoot2.1.3依赖Sping5.1.5版本,而Spring5.0开始支持webFlux反应式编程,是Spring5.0开始一个重大的亮点

SpringBoot2.1 兼容Java8,同时也支持了Java11

SpringBoot2.1.3 部分依赖项变更

SpringBoot中默认情况下已经配置了常用依赖包的版本号,在实际开发过程开发人员只需要指定依赖的groupId和artifactId即可,当父pom中指定版本号时,不建议子模块指定版本。

如果子模块中指明了,version,那么框架升级后,工程使用的依赖任然会是旧的版本号,可能存在兼容性问题。
在这里插入图片描述
其中值得注意的是: SpringBoot2.1.3中的配置的jedis版本是2.9.1,存在bug

精简控制台日志信息

例如:控制台不再打印出Controller中映射的Url信息,并且打印出web应用的根目录

Tomcat started on port(s): 8080 (http) with context path ‘/boot2’

设置web工程根目录配置项发生变更

server.context-path=/boot1
server.servlet.context-path=/boot2

SpringBoot2.x中废弃了一些配置项,可通过其提供的包来帮助检查

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-properties-migrator</artifactId>
    <optional>true</optional>
</dependency>

该依赖只需要开发时使用即可,修改完新的配置项时,直接移除该依赖,不需要打包到项目
在这里插入图片描述

SpringBoot2.x 对时间序列化格式默认不再是timestamps时间戳,而是yyyy-MM-ddTHH:mm:ss:SSS+0000

SpringBoot中默认使用jackson做为json序列化,反序列化工具,

在SpringBoot1.5.x中,向前端返回的对象包含时间属性时,时间被序列化为时间戳

{"username":"小明","password":"titans1.x","birthday":1594448070750}

但是在SpringBoot2.1.x中,时间被序列化为yyyy-MM-ddTHH:mm:ss:SSS+000格式

{"username":"小明","password":"titans2.x","birthday":"2020-07-11T06:14:33.729+0000"}

解决方案:如果仍然需要将时间序列化为时间戳,需要添加如下配置项

spring.jackson.serialization.write-dates-as-timestamps=true

SpringBoot2.x 中的Binder工具类

可以将指定前缀的属性值绑定到一个实体类上,前提是需要将org.springframework.core.env.Environment作为参数

MyPropertyConfig myPropertyConfig = Binder.get(environment)
                .bind("my.customer-tag-config.target", MyPropertyConfig.class)
                .orElse(null);

SpringBoot中配置项优先级问题

  1. Environment是Spring中的一个接口,主要用于获取Spring整个应用启动、运行过程中获取配置信息。
  2. 其实现类通过List保存多个PropertySource,一个PropertySource相当于一个Map,其以key-value的形式保存配置属性信息。
  3. 当通过Environment#getProperty(key)获取一个属性值时,内部实际是遍历List中的PropertySource,通过PropertySource获取key-value而List的有序性则实现类不通配置的优先级。

内嵌容器包结构调整

SpringBoot1.5.17中应用的类型分为两种Web类型的应用,普通类型的应用(运行结束进程退出)
而在SpringBoot2.x中增加了第三种应用类型Reactive,即反应式编程。因此对内嵌的容器包结构进行调整

SpringBoot1.x
org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory

SpringBoot2.x

org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory

org.springframework.boot.web.reactive.server.ReactiveWebServerFactory

SpringBoot应用类型推断与手动指定

SpringBoot在启动时会根据类路径上存在的包来判断改应该属于哪种类型,(Servlet web类型,Reactive web类型,普通类型)

当这些包都存在时,SpringBoot推断出的应用类型可能非项目中真实需要的,则需要手动指定。

SpringBoot1.x

spring.main.web-environment=true/false

SpringBoot2.x

spring.main.web-application-type=none
spring.main.web-application-type=servlet
spring.main.web-application-type=reactive

@ConditionalOnBean判断条件变化

@ConditionalOnBean现在的判断条件由OR变为了AND。

在配置类中定义Bean,如果使用@ConditionalOnBean依赖的也是配置类中Bean,则执行结果不可控,和配置类加载顺序有关,

参数文章: SpringBoot中@ConditionalOnBean实现原理

如果要实现Or的效果,可以通过继承AnyNestedCondition实现。

ApplicationStartedEvent事件执行时机发生变化

注意 org.springframework.boot.context.event.ApplicationStartedEvent 事件的变化
SpringBoot1.5.17

org.springframework.boot.context.event.ApplicationStartedEvent
org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent
org.springframework.boot.context.event.ApplicationPreparedEvent
org.springframework.boot.context.event.ApplicationReadyEvent

SpringBoot2.1.3

org.springframework.boot.context.event.ApplicationStartingEvent
org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent
org.springframework.boot.context.event.ApplicationContextInitializedEvent
org.springframework.boot.context.event.ApplicationPreparedEvent
org.springframework.boot.context.event.ApplicationStartedEvent
org.springframework.boot.context.event.ApplicationReadyEvent

SpringBoot2.x 松散绑定

SpringBoot2.0 松散绑定,不支持key中间带有驼峰命名
在这里插入图片描述
会抛出如下异常

Description:

Configuration property name 'customerTagConfig' is not valid:

Invalid characters: 'T', 'C'
Bean: testController
Reason: Canonical names should be kebab-case ('-' separated), lowercase alpha-numeric characters and must start with a letter

Action:

修改方案:
在这里插入图片描述

SpringBoot2.x默认设置不允许出现相同beanName的BeanDefinition

如果项目中出现了相同beanName的Bean定义,则会抛出DefinitionOverrideException,而在低版本中则不会。
在这里插入图片描述

解决办法:

1、(建议方案)查找项目出现相同beanName的原因,并做相应的修改

2、通过配置允许覆盖相同beanName的beanDefinition: spring.main.allow-bean-definition-overriding=true

SpringMVC请求路径匹配默认行为改变

在2.0之前的版本中,SpringMVC默认会使用.* suffix匹配请求路径,也就是如果Controller请求方法上设置 /person 路径时等同于 /person.* ,当请求url是/person.pdf 或是 /person.json时都是映射到 /person 请求。在SpringBoot2.0中默认关闭了*.suffix匹配规则,如果请求的url是/person.pdf 将不会再匹配到/person。

http返回406案例:在一次开发过程中,前端定义了名称为error.js 的静态文件放在SpringBoot的静态资源中,而SpringBoot中有个默认路径为/error的BasicErrorController用于处理异常请求,当前端请求error.js时http返回码是406,原因在于请求error.js浏览器希望得到的返回头是application/JavaScript,而/error.js正好匹配/error.*,因此被BasicErrorController的/error处理返回的是 application/json,导致406问题。

参考类:

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#getMappingForMethod
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#addMatchingMappings
org.springframework.web.servlet.mvc.condition.PatternsRequestCondition#getMatchingPattern

将Filter,Servlet中的转发类型改为DispatcherType.REQUEST

在原生Servlet编程,使用web.xml向tomcat容器中添加Sevrlet时,默认转发类型为DispatcherType.REQUEST,

但是SpringBoot1.5.17中,如果通过SpringBoot提供方式向web容器添加Sevlet,默认转发类型是
DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.REQUEST

SpringBoot2.1.3中,为了和原生默认配置一致,默认转发类型改为DispatcherType.REQUEST

Auto-configuration排序

@AutoConfigureOrder 默认值由Ordered.LOWEST_PRECEDENCE变为0。 (#10142)

SpringBoot2.1.2默认线程池

SpringBoot2.1.2 中默认有两出会开启线程池
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
创建Bean org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor

用于异步任务:例如@Async注解的方法

@Service
public class UserInfoService implements UserInfoServiceI{
    
    
    /**
     * 默认线程数8,达到30s后线程销毁,再次创建的线程id会递增
     */
    @Async
    @Override
    public void addUser(UserInfo user) {
    
    
        log.info("保存用户信息:{}", user.getUsername());
    }
}

org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration
创建Bean org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler

用于定时任务:例如@Scheduled(initialDelay = 3000, fixedDelay = 2000)

/**
 * 默认线程数 1
 *
 * @see TaskSchedulingAutoConfiguration#taskScheduler(TaskSchedulerBuilder)
 */
@Scheduled(initialDelay = 3000, fixedDelay = 2000)
public void print1() {
    
    
 
    log.info("当前时间:{}", LocalDateTime.now());
}

这两个Bean都实现了org.springframework.core.task.AsyncListenableTaskExecutor和
org.springframework.scheduling.SchedulingTaskExecutor, 所以程序中不能通过这两个类型及其父类类型注入bean

SpringBoot1.x中没有自定义线程池,而是采用的Spring自定义的

Spring会为异步任务创建一个 无限大小的线程池,容易出线程池爆满,参考:org.springframework.core.task.SimpleAsyncTaskExecutor,一般在开发时会自定义线程策略

Spring会为定时任务创建一个只有一条线程的线程池,参考 ScheduledTaskRegistrar#scheduleTasks()

SpringBoot2.x 支持Redis缓存设置,1.5x不支持

spring.cache.redis.cache-null-values=true
spring.cache.redis.use-key-prefix=true
spring.cache.redis.key-prefix=默认为缓存名+:: 例如 myCache::
spring.cache.redis.time-to-live=0

缺点:这是全局设置,无法针对单个缓存设置

不再支持Guava缓存

org.springframework.cache.guava.GuavaCacheManager

替代方案

org.springframework.cache.caffeine.CaffeineCacheManager

默认代理策略

Spring中默认代理策略是:如果是接口实现类怎么通过JDK代理,如果代理普通类则通过Cglib代理。

Spring Boot2.x 现在默认是使用CGLIB代理,同时包含AOP支持。

如果需要基于JDK接口的代理策略,需要把spring.aop.proxy-target-class设置为false。

在这里插入图片描述
在这里插入图片描述

SpringBoot2.0开始, Redis客户端默认使用Lettuce,并且配置项发生变更

SpringBoot2.0 开始,支持jedis和Lettuce两种客户端,默认使用Lettuce客户端
SpringBoot1.5.x中默认的Redis客户端时Jedis,如下是其配置项,但是在SpringBoot2.x中已经被废弃

spring.redis.pool.max-active=8
spring.redis.pool.max-idle=8
spring.redis.pool.max-wait=-1
spring.redis.pool.min-idle=0

Lettuce说明
Lettuce 官方文档:https://docs.spring.io/spring-boot/docs/2.3.1.RELEASE/reference/html/appendix-application-properties.html#cache-properties
在这里插入图片描述
在这里插入图片描述
如上是Lettuce客户端的官方说明,Lettuce是线程安全的,一个连接可以在多线程之间共享,因此一般情况下不需要设置使用连接池,并且连接池对性能影响不大。

SpringBoot2.1.3并没有为Lettuce开放Lettuce线程的配置项,ioThreadPoolSize和computationThreadPoolSize默认情况下都是cpu逻辑处理器数量。

修改Lettuce线程池数量
虽然SpringBoot没有提供修改Lettuce线程池的配置项,但是我们可以在配置类中配置如下的Bean来控制Lettuce线程数量。

@Bean(destroyMethod = "shutdown")
public DefaultClientResources lettuceClientResources() {
    
    
    int cpuSize = Runtime.getRuntime().availableProcessors();
    DefaultClientResources.Builder builder = DefaultClientResources.builder();
    builder.computationThreadPoolSize(cpuSize);
    builder.ioThreadPoolSize(cpuSize);
    return builder.build();
}

Lettuce 使用连接池
少数情况下,我们可能需要使用Lettuce连接池配置,添加如下依赖,并且针对Lettuce连接池做相应的配置

<dependency>
     <groupId>org.apache.commons</groupId>
     <artifactId>commons-pool2</artifactId>
</dependency>

lettuce 连接池配置

spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.max-wait=-1ms
spring.redis.lettuce.pool.min-idle=1
spring.redis.lettuce.shutdown-timeout=1000ms

其他变更参考

SpringBoot2.x变更内容

猜你喜欢

转载自blog.csdn.net/u013202238/article/details/106872441
今日推荐