Spring 4 升级踩雷指南

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> Spring 4 升级踩雷指南

点击上方“后端技术精选”,选择“置顶公众号”

技术文章第一时间送达!

作者:静默虚空 https://github.com/dunwu/spring-tutorial

https://github.com/dunwu/spring-tutorial

前言

最近,一直在为公司老项目做核心库升级工作。本来只是想升级一下 JDK8 ,却因为兼容性问题而不得不升级一些其他的库,而其他库本身依赖的一些库可能也要同步升级。这是一系列连锁问题,你很难一一识别,往往只有在编译时、运行时才能发现问题。

总之,这是个费劲的活啊。

本文小结一下升级 Spring4 的连锁问题。

为什么升级 spring4

升级 Spring4 的原因是:Spring 4 以前的版本不兼容 JDK8。当你的项目同时使用 Spring3 和 JDK8,如果代码中有使用 JDK8 字节码或 Lambada 表达式,那么会出问题。

也许你会问,为什么不使用最新的 Spring 5 呢?因为作为企业软件,一般更倾向使用稳定的版本(bug 少),而不是最新的版本,尤其是一些核心库。

更多细节可以参考:

https://spring.io/blog/2013/05/21/spring-framework-4-0-m1-3-2-3-available/

spring 4 重要新特性

Spring 4 相比 Spring 3,引入许多新特性,这里列举几条较为重要的:

  • 支持 `JDK8` (这个是最主要的)。
  • `Groovy Bean Definition DSL` 风格配置。
  • 支持 WebSocket、SockJS、STOMP 消息
  • 移除 Deprecated 包和方法
  • 一些功能加强,如:核心容器、Web、Test 等等,不一一列举。
  • 更多 Spring 4 新特性可以参考:

    https://docs.spring.io/spring/docs/4.3.14.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/#spring-whats-new

    http://jinnianshilongnian.iteye.com/blog/1995111

    升级 spring 4 步骤

    了解了前面内容,我们知道了升级 Spring 4 带来的好处。现在开始真刀真枪的升级了。

    不要以为升级一下 Spring 4,仅仅是改一下版本号,那么简单,细节处多着呢。

    下面,结合我在公司项目升级 Spring4 时遇到的一系列坑,希望能帮助各位少走弯路。


    下文内容基于假设你的项目是用 maven 管理这一前提。如果不满足这一前提,那么这篇文章对你没什么太大帮助。

    下文内容基于假设你的项目是用 maven 管理这一前提。如果不满足这一前提,那么这篇文章对你没什么太大帮助。

    修改 spring 版本

    第一步,当然是修改 pom.xml 中的 spring 版本。

    3.x.x.RELEASE   4.x.x.RELEASE

    实例:升级 spring-core

    其它 spring 库的升级也如此:

    
    properties
      spring.version4.3.13.RELEASE/spring.version
    /properties
    dependency
      groupIdorg.springframework/groupId
      artifactIdspring-core/artifactId
      version${spring.version}/version
    /dependency
    

    修改 spring xml 文件的 xsd

    用过 spring 的都知道,spring 通常依赖于大量的 xml 配置。

    spring 的 xml 解析器在解析 xml 时,需要读取 xml schema,schema 定义了 xml 的命名空间。它的好处在于可以避免命名冲突,有点像 Java 中的 package。

    实例:一个 spring xml 的 schema

    
    ?xml version="1.0" encoding="UTF-8"?
    beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"
    

    说明

  • `xmlns="http://www.springframework.org/schema/beans"` 声明 xml 文件默认的命名空间,表示未使用其他命名空间的所有标签的默认命名空间。
  • `xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"` 声明XML Schema 实例,声明后就可以使用 schemaLocation 属性了。
  • `xmlns:mvc="http://www.springframework.org/schema/mvc"` 声明前缀为 mvc 的命名空间,后面的 URL 用于标示命名空间的地址不会被解析器用于查找信息。其惟一的作用是赋予命名空间一个惟一的名称。当命名空间被定义在元素的开始标签中时,所有带有相同前缀的子元素都会与同一个命名空间相关联。 其它的类似 `xmlns:context` 、`xmlns:jdbc` 等等同样如此。

  • 
      xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
    ..."
    

    这个从命名可以看出个大概,指定 schema 位置这个属性必须结合命名空间使用。这个属性有两个值,第一个值表示需要使用的命名空间。第二个值表示供命名空间使用的 xml schema 的位置。

    上面示例中的 xsd 版本是  3.1.xsd ,表示 spring 的 xml 解析器会将其视为 3.1 版本的 xml 文件来处理。

    现在,我们使用了 Spring 4, 3.1.xsd 版本显然就不正确了,我们可以根据自己引入的 Spring 4 的子版本号将其改为  4.x.xsd 。

    但是,还有一种更好的做法:把这个指定 xsd 版本的关键字干掉,类似这样: http://www.springframework.org/schema/tx/spring-tx.xsd 。

    这么做的原因如下:

  • Spring 默认在启动时要加载 xsd 文件来验证 xml 文件。
  • 如果没有提供 `schemaLocation`,那么 spring 的 xml 解析器会从 namespace 的 uri 里加载 xsd 文件。
  • `schemaLocation` 提供了一个 xml namespace 到对应的 xsd 文件的一个映射。
  • 如果不指定 spring xsd 的版本号,spring 取的就是当前本地 jar 里的 xsd 文件,减少了各种风险(比如 xsd 与实际 spring jar 版本不一致)。
  • 更多详细内容可以参考这篇文章:

    http://blog.csdn.net/hengyunabc/article/details/22295749)

    修改 spring xml 文件

    spring 4 对 xml 做了一些改动。这里说一个最常用的改动:

    ref local

    spring 不再支持  ref 元素的  local 属性,如果你的项目中使用了,需要改为  bean

    spring 4 以前:

    
    bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
      property name="dataSource"
        ref local="dataSource" /
      /property
    /bean
    

    spring 4 以后:

    
    bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
      property name="dataSource"
        ref bean="dataSource" /
      /property
    /bean
    

    如果不改启动会报错:

    
    Caused by: org.xml.sax.SAXParseException: cvc-complex-type.3.2.2: Attribute 'local' is not allowed to appear in element 'ref'.
    

    当然,可能还有一些其他配置改动,这个只能说兵来将挡水来土掩,遇到了再去查官方文档吧。

    加入 spring support

    spring 3 中很多的扩展内容不需要引入support 。但是 spring 4 中分离的更彻底了,如果不分离,会有很多 ClassNotFound 。

    
    dependency
        groupIdorg.springframework/groupId
        artifactIdspring-context-support/artifactId
        version4.2.3.RELEASE/version
    /dependency
    

    更换 spring-mvc jackson

    spring mvc 中如果返回结果为 json 需要依赖 jackson 的jar包,但是他升级到了2, 以前是  codehaus.jackson,现在换成了  fasterxml.jackson

    
    dependency      
        groupIdcom.fasterxml.jackson.core/groupId
        artifactIdjackson-core/artifactId
        version2.7.0/version
    /dependency
    dependency                
        groupIdcom.fasterxml.jackson.core/groupId
        artifactIdjackson-databind/artifactId
        version2.7.0/version
    /dependency
    

    同时修改spring mvc的配置文件:

    
    bean
        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"
        property name="messageConverters"
            list
                 ref bean="stringHttpMessageConverter" /  
                bean
                    class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"
                /bean
            /list
        /property
    /bean
    
    bean id="stringHttpMessageConverter"
        class="org.springframework.http.converter.StringHttpMessageConverter"
        property name="supportedMediaTypes"
            list
                valuetext/plain;charset=UTF-8/value
            /list
        /property
    /bean
    

    解决 ibatis 兼容问题

    问题

    如果你的项目中使用了 ibatis (mybatis 的前身)这个 orm 框架,当 spring3 升级 spring4 后,会出现兼容性问题,编译都不能通过。

    这是因为 Spring4 官方已经不再支持 ibatis。

    解决方案

    添加兼容性 jar 包

    
    dependency
        groupIdorg.mybatis/groupId
        artifactIdmybatis-2-spring/artifactId
        version1.0.1/version
    /dependency
    

    更多内容可参考:

    https://stackoverflow.com/questions/32353286/no-support-for-ibatis-in-spring4-2-0

    升级 Dubbo

    我们的项目中使用了 soa 框架 Dubbo 。由于 Dubbo 是老版本的,具体来说是(2013年的 2.4.10),而老版本中使用的 spirng 版本为2.x,有兼容性问题。

    Dubbo 项目从今年开始恢复维护了,首先把一些落后的库升级到较新版本,比如 jdk8,spring4 等,并修复了一些 bug。所以,我们可以通过升级一下 Dubbo 版本来解决问题。

    
    dependency
      groupIdcom.alibaba/groupId
      artifactIddubbo/artifactId
      version2.5.8/version
      exclusions
        exclusion
          groupIdorg.springframework/groupId
          artifactIdspring-context/artifactId
        /exclusion
        exclusion
          groupIdorg.springframework/groupId
          artifactIdspring-web/artifactId
        /exclusion
        exclusion
          groupIdorg.javassist/groupId
          artifactIdjavassist/artifactId
        /exclusion
      /exclusions
    /dependency
    

    升级 Jedis

    升级 Dubbo 为当前最新的 2.5.8 版本后,运行时报错:

    JedisPoolConfig 配置错误****

    
    Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.GenericObjectPoolConfig
    

    由于项目中使用了 redis,版本为 2.0.0 ,这个问题是由于 jedis 需要升级:

    
    dependency
      groupIdredis.clients/groupId
      artifactIdjedis/artifactId
      version2.9.0/version
    /dependency
    

    jedis 2.4.1 以上版本的  JedisPoolConfig 已经没有了 maxActive 和  maxWait 属性。

    修改方法如下:

    maxActive  maxTotal

    maxWait  maxWaitMillis

    
    bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"
      property name="maxTotal" value="200" /
      property name="maxIdle" value="10" /
      property name="maxWaitMillis" value="1000" /
      property name="testOnBorrow" value="true" /
    /bean
    

    JedisPool 配置错误

    
    InvalidURIException: Cannot open Redis connection due invalid URI
    

    原来的配置如下:

    
    bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="destroy" depends-on="jedisPoolConfig"
      constructor-arg ref="jedisPoolConfig" /
      constructor-arg type="java.lang.String" value="${redis.host}" /
      constructor-arg type="int" value="${redis.port}" /
    /bean
    

    查看源码可以发现,初始化 JedisPool 时未指定结构方法参数的类型,导致 host 字符串值被视为 URI 类型,当然类型不匹配。

    解决方法是修改上面的host 配置,为: constructor-arg type="java.lang.String" value="${redis.host}" /

    至此,spring 4 升级结束。后面如果遇到其他升级问题再补充。

    资料

    https://spring.io/blog/2013/05/21/spring-framework-4-0-m1-3-2-3-available/

    https://docs.spring.io/spring/docs/4.3.14.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/#spring-whats-new

    http://www.sojson.com/blog/145.html

    http://jinnianshilongnian.iteye.com/blog/1995111

    http://blog.csdn.net/hengyunabc/article/details/22295749)

    https://stackoverflow.com/questions/32353286/no-support-for-ibatis-in-spring4-2-0

    https://docs.spring.io/spring/docs/4.3.14.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/#spring-whats-new

    http://jinnianshilongnian.iteye.com/blog/1995111

    https://stackoverflow.com/questions/32353286/no-support-for-ibatis-in-spring4-2-0

    推荐阅读(点击即可跳转阅读)

    1. 

    **2. **

    **3. **

    **4. **

    **5. **

    Spring 4 升级踩雷指南

    原文始发于微信公众号(后端技术精选):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> Spring 4 升级踩雷指南


     上一篇
    SpringBoot+Mybatis+Swagger2环境搭建 SpringBoot+Mybatis+Swagger2环境搭建
    点击上方“Java知音”,选择“置顶公众号” 技术文章第一时间送达! 作者:calebman https://www.jianshu.com/p/95946d6b0c7d https://www.jianshu.com/p/
    2021-04-05
    下一篇 
    Spring事务传播属性有那么难吗?看这一篇就够了 Spring事务传播属性有那么难吗?看这一篇就够了
    点击上方“后端技术精选”,选择“置顶公众号” 技术文章第一时间送达! 作者:不学无数的程序员 juejin.im/post/5da6eee2f265da5bb977d65c juejin.im/post/
    2021-04-05