本文记录旧项目重构成Spring Boot项目中遇到的一些问题。
Spring Boot使用web.xml
虽然Spring官方文档提供了用编程的方式替代web.xml配置文件,参考DispatcherServlet和Servlet Config,但是接受新东西确实总是后知后觉,还想保留原来的配置文件怎么办。
另外,官网说是基于Servlet 3.0的基础上,如果是Servlet 2.5则需要按照下面的方法做,如果是Servlet 3.0+就不用改了。
在pom.xml中加上依赖(注意版本,我写的版本是最新的):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-legacy</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
修改web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>cn.shrmus.demo20200528.DemoApplication20200528</param-value> <!-- 启动类 -->
</context-param>
<listener>
<listener-class>org.springframework.boot.legacy.context.web.SpringBootContextLoaderListener</listener-class>
</listener>
Spring Boot整合jsp
原项目中可能有jsp页面,还需要servlet、jsp、jstl的包。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>9.0.35</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jsp-api</artifactId>
<version>9.0.35</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
打包的文件
展示一下我写的Demo文件目录:

文件路径根据你的项目文件目录来写,这样Mavan在打包的时候才会把这些目录下的文件添加进去。不要照抄!不要照抄!不要照抄!
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/**</include>
</includes>
</resource>
<resource>
<directory>src/main/webapp</directory>
<includes>
<include>**/**</include>
</includes>
</resource>
</resources>
使用spring-boot-legacy带来的问题
后面的问题重现了很多遍,确认不是偶然发生才记录下来的。
访问webapp目录下的jsp文件是正常的。
能执行Controller中的方法,但是不能正确返回WEB-INF目录下的jsp文件,出现404。
我猜想可能是ViewResolver没被注册到Spring容器。
首先检查了一下springmvc.xml的配置路径,没问题。
在springmvc.xml中,注册一个命名为internalResourceViewResolver的bean:
<?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:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置模型视图的管理器 -->
<bean id="internalResourceViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/page/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
文件路径,文件内容都没问题,然后查看了一下Spring容器中的bean,beanDefinitionMap里面没有命名为internalResourceViewResolver的bean。
先解决问题吧,解决方法有两种。
第一种,在application.properties添加配置,这种方式使用的是org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator:
spring.mvc.view.prefix=/WEB-INF/page/
spring.mvc.view.suffix=.jsp
第二种,代码注册一个VeiwResolver:
@Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
internalResourceViewResolver.setViewClass(org.springframework.web.servlet.view.JstlView.class);
internalResourceViewResolver.setContentType("text/html");
String VIEW_PREFIX = "/WEB-INF/page/";
String VIEW_SUFFIX = ".jsp";
internalResourceViewResolver.setPrefix(VIEW_PREFIX);
internalResourceViewResolver.setSuffix(VIEW_SUFFIX);
return internalResourceViewResolver;
}
问题出现的原因
虽然上面这两种方式能解决问题,但我还是想找一下问题出在哪里,开始debug,源码中注册的ViewResolver命名为defaultViewResolver,虽然是返回InternalResourceViewResolver类型,但是@Bean是根据方法名注册进Spring容器的。

这也就解释了为什么我在Spring容器里找不到命名为internalResourceViewResolver的bean。
我尝试修改springmvc.xml中bean id,还是没用。
最后我注释了web.xml表示DispatcherServlet的这段代码。
<!--
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
-->
在启动类中引入springmvc.xml:
@ImportResource(value = "classpath:/spring/springmvc.xml")
将springmvc.xml中mvc开头的标签代码注释,如<mvc:annotation-driven>,<mvc:resources>,不注释会报一个这样的错:
Description:
The bean 'mvcContentNegotiationManager', defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class], could not be registered. A bean with that name has already been defined and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
然后重启外置Tomcat,查看Spring容器中的bean,有InternalResourceViewResolver,说明可以访问WEB-INF下的文件,果然可以。
我又把目光转向web.xml,为什么注释了这段代码就可以了,而且没有DispatcherServlet居然没有报错,我在Spring容器中找了一下DispatcherServlet。又是自动配置org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,顿时豁然开朗。
随后我取消web.xml的注释,启动类@ImportResource这行代码注释,随即在启动类加上:
@SpringBootApplication(exclude = DispatcherServletAutoConfiguration.class)
然后重启报错了:
Parameter 0 of method errorPageCustomizer in org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration required a bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath' that could not be found.
解决这个错误,在启动类加上:
@SpringBootApplication(exclude = {
DispatcherServletAutoConfiguration.class
,ErrorMvcAutoConfiguration.class
})
重启没有再报错,访问WEB-INF下的文件出现404,错误信息:
ERROR 5524 --- [nio-8080-exec-5] o.s.b.w.servlet.support.ErrorPageFilter : Cannot forward to error page for request [/user/addui] as the response has already been committed. As a result, the response may have the wrong status code. If your application is running on WebSphere Application Server you may be able to resolve this problem by setting com.ibm.ws.webcontainer.invokeFlushAfterService to false
最后一句setting com.ibm.ws.webcontainer.invokeFlushAfterService to false:
@Bean
public FilterRegistrationBean disableSpringBootErrorFilter(ErrorPageFilter errorPageFilter) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(errorPageFilter);
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
DispatcherServlet自动配置报错的问题虽然解决了,但还是访问不了WEB-INF下的文件,能进入Controller,却返回404,有人看到这里可能会问了,前面不是有两种解决方法了吗,加上view.prefix和view.suffix解决问题不就行了,还把时间浪费在DispatcherServlet上面。
我想说的是,如果硬要使用原来的配置文件,可能就碰不到这种问题,因为有编码的方式代替。也有可能你使用原来的配置文件遇到了另外的问题,仅仅让工程跑起来是不够的。
我还是把目光放在ViewResolver,springmvc.xml中的internalResourceViewResolver没有注册到容器中。
在springmvc.xml文件时,配置的自动扫描是有效的,解析其他的bean也能被注册到容器中,唯独没有internalResourceViewResolver,猜想还是自动配置导致的,前面说源码中将InternalResourceViewResolver类型注册为名为defaultViewResolver的bean。
修改启动类:
@SpringBootApplication(exclude = {
DispatcherServletAutoConfiguration.class,
ErrorMvcAutoConfiguration.class,
WebMvcAutoConfiguration.class
})
最后发现这样写就行了:
@SpringBootApplication(exclude = WebMvcAutoConfiguration.class})
这样确实解决问题了,自动配置也不是想象中那么好,如果要使用外部配置文件,首先得知道哪些自动配置替换了外部配置文件中的bean。或许以后还是会选择用编码方式代替配置文件。
Spring Boot 配置静态资源映射
顺便再说一下静态资源的问题,按照我前面的项目文件路径,用Spring Boot的方式启动,是访问不到静态资源的。参考官方文档Static Content。
官方文档说的默认静态路径/static,public,/resources,META-INF/resources是打包后的路径,参考target目录。

不管静态资源放在什么地方,spring.resources.static-locations填的路径参考打包后的目录就知道了。
本文详述了在将旧项目迁移到Spring Boot过程中遇到的使用spring-boot-legacy的问题,包括web.xml配置、JSP整合、静态资源映射及ViewResolver注册等难点。通过分析和调试,找到了问题的根源并提出了解决方案,强调了理解和适配Spring Boot自动配置的重要性。
2万+

被折叠的 条评论
为什么被折叠?



