SpringBoot集成WebServlet出现自定义单servlet请求失败的问题
一.导言
SpringBoot的真正核心是快速整合以及自动装配,所以在spring家族中springBoot不仅整合了Spring的IOC容器还兼容了WebServlet容器;这使得springBoot项目不仅支持快速开发微服务,同时具备开发MVC模式下的项目。
其中MVC模式的实现者之一就是WebServlet;由于springBoot的整合,在其项目中开发WebServlet也是可行方案之一。但是在使用servlet技术时我们遇到了一个问题:即在SpringBoot中,以Bean的方式注册servlet需要自定义两个及以上的servlet。
这是为什么呢?
二.测试验证问题
首先验证一个servlet的情况
先配置servlet实现类:
public class OneServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servlet 1 的实现类"); resp.getWriter().print("this is servlet 1"); } }
在config配置类中注册这个servletBean:
@Configuration public class RegisterBean { @Bean public OneServlet createOneServlet(){ //注册servlet 1 return new OneServlet(); } }
直接启动springBoot启动器查看是否可以完成一次servlet的请求
地址栏输入:
http://localhost:9000/createOneServlet/
或者:
http://localhost:9000/OneServlet/
显示都是错误页面:
验证两个servlet的情况
拓展一个servlet实现类:
public class TwoServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servlet 2 的实现类"); resp.getWriter().print("this is servlet 2"); } }
以及使用Bean的方式完成注入:
@Configuration public class RegisterBean { @Bean public OneServlet createOneServlet(){ //注册servlet 1 return new OneServlet(); } @Bean public TwoServlet createTwoServlet(){ //注册servlet 2 return new TwoServlet(); } }
再次启动springBoot启动器:
运行:
http://localhost:9000/createOneServlet/
结果可以拿到请求,正常处理
运行:
http://localhost:9000/createTwoServlet/
结果依旧可以拿到,正常处理请求:
这就出现了刚开始提到的那个问题:springBoot项目运行时,一个servlet无法进行请求,当有两个时却可以进行请求
解决这问题之前,我们需要一点前言知识,那就是需要先了解SpringMVC中的DispatcherServlet
三.解决单servlet无法正常请求的问题
DispatcherServlet
是 Spring MVC 框架的核心组件,它充当前端控制器(Front Controller)的角色,负责接收所有的 HTTP 请求并将其分发到相应的处理器(Handler)。通过这种方式,DispatcherServlet
实现了请求的集中管理和分发。请求分发:接收所有进入的应用程序的HTTP请求,并根据配置将它们分发给合适的处理器(Controller)。
当然DispatcherServlet的功能还有很多,我们只把关系到问题的部分单独拿出来。我们知道SpringMVC在JavaWeb的基础之上,演化了DispatcherServlet,它的本质依旧是一个servlet,在SpringMVC中将其单独拿出来作为前后端连接的大脑,通常它会将前端所有的请求都拦截下来,然后经过一系列验证之后移交给controller处理,处理完成之后又返回给它,返回给视图层。
由于springBoot中整合了SpringMVC,故而DispatcherServlet存在于spring容器中的。
回到刚刚的问题,一个servlet为什么执行不起来,这还得分析我们注入servlet的方式
@Bean public OneServlet createOneServlet(){ //注册servlet 1 return new OneServlet(); }
由于我们注入使用的是最简单的注入方式,并没有配置这个servlet的请求路径;
DeepSeek:如果没有显式配置路径映射,Spring Boot 会尝试为 Servlet 分配默认路径,通常是 Servlet 的名称或类名的小写形式。如果默认路径与其他映射冲突,或 Servlet 未正确注册,可能导致无法访问。
在第一种情况下,springBoot是在尝试进行为servlet命名的,只是为其命名的映射路径刚好和DispatcherServlet的拦截请求冲突了,故而失败了
我们在失败的情况下编写一个controller,看相同的请求是不是交给DispatcherServlet处理了
controller:
@RestController public class ServletTest { @GetMapping("/createOneServlet/") public String test(){ return "return from Spring MVC"; } }
和第一次失败测试一样的请求:
http://localhost:9000/createOneServlet/
结果
可以发现,配置一个controller之后确实请求被DispatcherServlet接管了,故而有springMVC框架进行返回
知道了问题所在就可以进行修改了,推荐两种解决方案
1.更改DispatcherServlet的默认拦截路径
在SpringBoot中DispatcherServlet的默认拦截路径是 "/*" ;故而将其改到其它位置后,他就不会影响SpringBoot为没有配置映射路径的servlet命名了
更改application.properties文件:
spring.mvc.servlet.path= /test/
将DispatcherServlet的默认匹配路径更改到 “ /test/* ” 下
注销controller类的实现:
// @GetMapping("/createOneServlet/") // public String test(){ // return "return from Spring MVC"; // }
再次启动,测试单servlet未配置路径是否有问题:
http://localhost:9000/createOneServlet/
结果:
可以看到,servlet成功将其请求处理了
2.配置单个servlet的请求路径
配置servlet实现类使用@WebServlet注解:
@WebServlet(urlPatterns = "/createOneServlet") public class OneServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("servlet 1 的实现类"); resp.getWriter().print("this is servlet 1"); } }
在springBoot启动器上配置自动扫描servlet的注解@ServletComponentScan:
@SpringBootApplication @ServletComponentScan public class ServletTestApplication { public static void main(String[] args) { SpringApplication.run(ServletTestApplication.class, args); } }
启动测试:
http://localhost:9000/createOneServlet/
结果:
可以发现依旧运行出来了结果
如上就是推荐在SpringBoot中注册servlet的两种方式
为什么两个自定义servlet不写映射却可以呢?
DeepSeek:两个自定义 Servlet 可以访问的原因
自动注册:当有多个 Servlet 时,Spring Boot 会为每个 Servlet 生成默认路径,通常基于类名或者Bean。
路径唯一性:多个 Servlet 的默认路径通常不会冲突,因此可以正常访问。
也就是基于这样的原因,DispatcherServlet没有产生拦截,因为这两个自定义servlet已经在Servlet容器中完成注册,根据Java servlet的匹配规范,即最长匹配原则,/(类名|Bean)的匹配长度比DispatcherServlet /* 精度更高,故而两个servlet的情况下,即使不修改SpringMVC的配置依旧可以正常访问。
------END------
本文虽经反复斟酌,但仍可能存在疏漏或不当之处,衷心希望得到各位同行的批评指正,以期进一步完善。