原始工程说明:
SpringMVC 4.2.4.RELEASE
Shiro 1.5.3
Log4j2 2.14.1
Mybatis 3.3.0
Mybatis-spring 1.2.2
JDK 1.8
C3P0 0.9.5.5
shiro-freemarker-tags 1.0.2
OJDBC6 11.2.0.3
tk.mapper 3.4.2
com.github.pagehelper 5.0.0
spring-data-redis 1.8.6.RELEASE
jedis 2.9.0
jms 2.0.1
artemis-jms-client 2.17.0
升级步骤:
一、引入SpringBoot,去除SpringMVC
修改pom,引入SpringBoot依赖,同时去除SpringMVC相关依赖
(说明:推荐一个IDEA插件Maven Helper,可以很好的查看和解决包的冲突)
示例如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-parent</artifactId>
<version>2.3.12.RELEASE</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
说明:因为工程使用了Log4j2日志,而SpringBoot默认也会开启日志(默认用的是logback),会导致冲突。所以在引入SpringBoot stater时,需要将log排除。
二、创建application类
该类是SpringBoot的启动入口类,为了复用之前已有的XML配置,此处需要将XML资源都引入进来。
示例代码:
@SpringBootApplication(proxyBeanMethods = false,
exclude = { RedisAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class })
@ImportResource(locations = { "classpath:spring-*.xml", "classpath:mybatis-config.xml" })
@PropertySource("classpath:config.properties")
public class TaApplication {
public static void main(String[] args) {
SpringApplication.run(TaApplication.class, args);
}
}
说明1:由于SpringBoot会自动给所有扫描到的Bean进行代理增强,但是老工程里面的Bean都已经被Spring托管了,所以会导致一些INFO的提示:
Bean '' of type [xxx] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
所以,此处使用proxyBeanMethods=false,禁用SpringBoot的自动代理。
说明2:SpringBoot引入data-Redis包后,由于会自动配置,导致与XML里的配置冲突,故此处通过exclude,禁用Redis的自动配置。
说明3:通过ImportResource,可以将老工程里的XML配置都引入到工程里。(后续可以再逐个将XML配置改为Configuration模式)
说明4:通过PropertySource引入老工程里的properties配置文件(SpringBoot工程默认的配置文件都是Application,为了兼容老的配置,此处先引入进来,后续可以逐步迁移到application配置文件中)
三、创建application.yml配置文件
SpringBoot工程相关的配置都在此文件中进行配置
示例代码如下:
server:
port: 81
logging:
config: classpath:log4j2.xml
spring:
main:
allow-bean-definition-overriding: true
说明1:通过server.port指定springboot默认的启动端口
说明2:因为springboot默认的log4j2配置文件名称为log4j2-spring.xml,故此处需要通过配置指定log4j2的配置文件
update 20220929:spring boot 默认支持log4j2-spring.xml 或者log4j2.xml名称,直接使用这两个文件名会导致一些问题。
因为log4j2默认会自动装配,当发现存在log4j2.xml或log4j2-spring.xml时就会使用配置文件进行配置,而此时spring还未完成创建上下文,将会导致spring look up失效。
可以修改配置的文件名为其他的,然后在yml里面指定即可
参考:
说明3:可能因为配置相关问题,部分类会被多次扫描并自动注入Spring,导致Spring Bean创建异常。故通过allow-bean-definition-overiding=true,解决重复扫描并创建的问题
四、解决Shiro兼容性问题
问题1描述:
经过前面几步,工程终于能够正常启动了。但是访问页面后,出现了很诡异的问题,页面跳转到CAS并经过认证后返回,但是无法正常进入页面,出现了无限重定向的问题。排查了好久(由于与问题2纠缠在一起,导致在排查该问题时有些混乱),终于发现原来是回调路径设置的有问题。
在开发环境,原有的工程在IDEA的Tomcat部署配置时设置了Application Context,所以在访问页面时,路径都会带上这个context,比如http://xxx/yy/index (yy就是配置的application context)。
而改为SpringBoot后,CAS回调URL仍然配置的是之前的,所以导致了CAS回调访问该路径时,被拦截器拦住,认为需要进行用户认证,又触发了CAS认证访问,从而导致无限循环。发现问题后,将yy去掉即可(SpringBoot默认的application context = /,即没有yy)
问题2描述:
解决了CAS无限循环的问题,接着发现访问一些静态页面,也走了CAS认证(SHIRO配置的是静态页面可以直接访问),查看了很久shiro的配置,应该没有问题,也没有报SHIRO相关的异常,说明配置应该是兼容的。但是又没有正常的走SHIRO配置好的拦截器。只能硬着头皮,设置断点,慢慢研究SpringBoot下SHIRO的处理流程。最后通过日志发现了一条很诡异的INFO日志:
org.springframework.boot.web.servlet.ServletContextInitializerBeans:235 - Mapping filters: filterRegistrationBean urls=[/*] order=2147483647, filterRegistrationBean urls=[/*] order=2147483647, filterRegistrationBean urls=[/*] order=2147483647, characterEncodingFilter urls=[/*] order=-2147483648, formContentFilter urls=[/*] order=-9900, requestContextFilter urls=[/*] order=-105, shiroFilterFactoryBean urls=[/*] order=2147483647, menuPermissionFilter urls=[/*] order=xxx
里面的menuPermissionFilter是工程里面自定义的一个Shiro Filter,按道理应该是在shiro里面进行管理和调用的,而且配置的拦截URL也不是/*。顿时感觉有可能问题在这里,因为这里把 menuPermissionFilter urls设置成了/*,所以导致访问所有页面,都被强制走CAS了。
继续跟踪调试,发现了很有意思的现象,访问一个页面后,menuPermissionFilter被调用了两遍。
经过查看相关代码,基本发现了问题所在:SpringBoot自动将自定义的filter类加入到了ServletContext下的filterChain里,而且默认设置urls就是/*,所以导致了所有的页面访问,都会被menuPermissionFilter拦截,然后校验不通过,导致了页面无法访问。
查看源码,我们可以在ServletContextInitializerBeans类下的ServletCont