前面介绍了java web三要素中filter的使用指南与常见的易错事项,接下来我们来看一下Servlet的使用姿势,本篇主要带来在SpringBoot环境下,注册自定义的Servelt的四种姿势
@WebServlet
注解ServletRegistrationBean
bean定义ServletContext
动态添加- 普通的spring bean模式
I. 环境配置
1. 项目搭建
首先我们需要搭建一个web工程,以方便后续的servelt注册的实例演示,可以通过spring boot官网创建工程,也可以建立一个maven工程,在pom.xml中如下配置
1 | <parent> |
特别说明:
为了紧跟SpringBoot的最新版本,从本篇文章开始,博文对应的示例工程中SpringBoot版本升级到2.2.1.RELEASE
II. Servlet注册
自定义一个Servlet比较简单,一般常见的操作是继承HttpServlet
,然后覆盖doGet
, doPost
等方法即可;然而重点是我们自定义的这些Servlet如何才能被SpringBoot识别并使用才是关键,下面介绍四种注册方式
1. @WebServlet
在自定义的servlet上添加Servlet3+的注解@WebServlet
,来声明这个类是一个Servlet
和Fitler的注册方式一样,使用这个注解,需要配合Spring Boot的@ServletComponentScan
,否则单纯的添加上面的注解并不会生效
1 | /** |
上面是一个简单的测试Servlet,接收请求参数name
, 并返回 welcome xxx
;为了让上面的的注解生效,需要设置下启动类
1 |
|
然后启动测试,输出结果如:
1 | ➜ ~ curl http://localhost:8080/annotation\?name\=yihuihui |
2. ServletRegistrationBean
在Filter的注册中,我们知道有一种方式是定义一个Spring的Bean FilterRegistrationBean
来包装我们的自定义Filter,从而让Spring容器来管理我们的过滤器;同样的在Servlet中,也有类似的包装bean: ServletRegistrationBean
自定义的bean如下,注意类上没有任何注解
1 | /** |
接下来我们需要定义一个ServletRegistrationBean
,让它持有RegisterBeanServlet
的实例
1 |
|
测试请求输出如下:
1 | ➜ ~ curl 'http://localhost:8080/register?name=yihuihui' |
3. ServletContext
这种姿势,在实际的Servlet注册中,其实用得并不太多,主要思路是在ServletContext初始化后,借助javax.servlet.ServletContext#addServlet(java.lang.String, java.lang.Class<? extends javax.servlet.Servlet>)
方法来主动添加一个Servlet
所以我们需要找一个合适的时机,获取ServletContext
实例,并注册Servlet,在SpringBoot生态下,可以借助ServletContextInitializer
ServletContextInitializer主要被RegistrationBean实现用于往ServletContext容器中注册Servlet,Filter或者EventListener。这些ServletContextInitializer的设计目的主要是用于这些实例被Spring IoC容器管理
1 | /** |
测试结果如下
1 | ➜ ~ curl 'http://localhost:8080/context?name=yihuihui' |
4. bean
接下来的这种注册方式,并不优雅,但是也可以实现Servlet的注册目的,但是有坑,请各位大佬谨慎使用
看过我的前一篇博文191016-SpringBoot系列教程web篇之过滤器Filter使用指南的同学,可能会有一点映象,可以在Filter上直接添加@Component
注解,Spring容器扫描bean时,会查找所有实现Filter的子类,并主动将它包装到FilterRegistrationBean
,实现注册的目的
我们的Servlet是否也可以这样呢?接下来我们实测一下
1 |
|
现在问题来了,上面这个Servlet没有定义urlMapping规则,怎么请求呢?
为了确定上面的Servlet被注册了,借着前面Filter的源码分析的关键链路,我们找到了实际注册的地方ServletContextInitializerBeans#addAsRegistrationBean
1 | // org.springframework.boot.web.servlet.ServletContextInitializerBeans#addAsRegistrationBean(org.springframework.beans.factory.ListableBeanFactory, java.lang.Class<T>, java.lang.Class<B>, org.springframework.boot.web.servlet.ServletContextInitializerBeans.RegistrationBeanAdapter<T>) |
从上面的源码上可以看到,这个Servlet的url要么是/
, 要么是/beanName/
接下来进行实测,全是404
1 | ➜ ~ curl 'http://localhost:8080/?name=yihuihui' |
然后再定义一个Servlet时
1 |
|
再次测试
1 | ➜ ~ curl 'http://localhost:8080/beanServlet1?name=yihuihui' |
从实际的测试结果可以看出,使用这种定义方式时,这个servlet相应的url为beanName + '/'
注意事项
然后问题来了,只定义一个Servlet的时候,根据前面的源码分析,这个Servlet应该会相应http://localhost:8080/
的请求,然而测试的时候为啥是404?
这个问题也好解答,主要就是Servlet的优先级问题,上面这种方式的Servlet的相应优先级低于Spring Web的Servelt优先级,相同的url请求先分配给Spring的Servlet了,为了验证这个也简单,两步
- 先注释
BeanServlet2
类上的注解@Component
- 在
BeanServlet1
的类上,添加注解@Order(-10000)
然后再次启动测试,输出如下
1 | ➜ ~ curl 'http://localhost:8080/?name=yihuihui' |
5. 小结
本文主要介绍了四种Servlet的注册方式,至于Servlet的使用指南则静待下篇
常见的两种注册case:
@WebServlet
注解放在Servlet类上,然后启动类上添加@ServletComponentScan
,确保Serlvet3+的注解可以被Spring识别- 将自定义Servlet实例委托给bean
ServletRegistrationBean
不常见的两种注册case:
- 实现接口
ServletContextInitializer
,通过ServletContext.addServlet
来注册自定义Servlet - 直接将Serlvet当做普通的bean注册给Spring
- 当项目中只有一个此种case的servlet时,它响应url: ‘/‘, 但是需要注意不指定优先级时,默认场景下Spring的Servlet优先级更高,所以它接收不到请求
- 当项目有多个此种case的servlet时,响应的url为
beanName + '/'
, 注意后面的’/‘必须有
II. 其他
0. 项目
web系列博文
- 191120-SpringBoot系列教程Web篇之开启GZIP数据压缩
- 191018-SpringBoot系列教程web篇之过滤器Filter使用指南扩展篇
- 191016-SpringBoot系列教程web篇之过滤器Filter使用指南
- 191012-SpringBoot系列教程web篇之自定义异常处理HandlerExceptionResolver
- 191010-SpringBoot系列教程web篇之全局异常处理
- 190930-SpringBoot系列教程web篇之404、500异常页面配置
- 190929-SpringBoot系列教程web篇之重定向
- 190913-SpringBoot系列教程web篇之返回文本、网页、图片的操作姿势
- 190905-SpringBoot系列教程web篇之中文乱码问题解决
- 190831-SpringBoot系列教程web篇之如何自定义参数解析器
- 190828-SpringBoot系列教程web篇之Post请求参数解析姿势汇总
- 190824-SpringBoot系列教程web篇之Get请求参数解析姿势汇总
- 190822-SpringBoot系列教程web篇之Beetl环境搭建
- 190820-SpringBoot系列教程web篇之Thymeleaf环境搭建
- 190816-SpringBoot系列教程web篇之Freemaker环境搭建
- 190421-SpringBoot高级篇WEB之websocket的使用说明
- 190327-Spring-RestTemplate之urlencode参数解析异常全程分析
- 190317-Spring MVC之基于java config无xml配置的web应用构建
- 190316-Spring MVC之基于xml配置的web应用构建
- 190213-SpringBoot文件上传异常之提示The temporary upload location xxx is not valid
项目源码
- 工程:https://github.com/liuyueyi/spring-boot-demo
- 项目:https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/211-web-servlet
1. 一灰灰Blog
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
- 一灰灰Blog个人博客 https://blog.hhui.top
- 一灰灰Blog-Spring专题博客 http://spring.hhui.top