前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【小家Spring】SpringBoot中使用Servlet、Filter、Listener三大组件的三种方式以及原理剖析

【小家Spring】SpringBoot中使用Servlet、Filter、Listener三大组件的三种方式以及原理剖析

作者头像
YourBatman
发布2019-09-03 15:17:41
1.5K0
发布2019-09-03 15:17:41
举报
文章被收录于专栏:BAT的乌托邦BAT的乌托邦
前提概要

web开发使用Controller基本能解决大部分的需求,但是有时候我们也需要使用Servlet,因为相对于拦截和监听来说,有时候原生的还是比较好用的。

因此本文就主要介绍web三大组件Servlet、Filter、Listener在SpringBoot中的使用做个介绍。本文重点做使用介绍,以及剖析SpringBoot是如何支持和解析这些方式的

Spring boot 的主 Servlet 为 DispatcherServlet,其默认的url-pattern为“/”。也许我们在应用中还需要定义更多的Servlet,该如何使用SpringBoot来完成呢?

三种方式

本文主要以使用Servelt为例子进行讲解,使用其余组件的方式也差不多。差异性比较大的地方我会尽量指出来,请举一反三

方式一: @ServletComponentScan 扫描的方式(推荐)

看看这个注解的javadoc

代码语言:javascript
复制
 * Enables scanning for Servlet components ({@link WebFilter filters}, {@link WebServlet
 * servlets}, and {@link WebListener listeners}). Scanning is only performed when using an
 * embedded web server.

从javadoc中可以看出,该扫描的方式只支持嵌入式的web容器

使用此种扫描方式,显然是基于Servlet3.0的注解的方式。而该注解的解析由SpringBoot提供的@ServletComponentScan来驱动的。

案例如下:

代码语言:javascript
复制
@ServletComponentScan
@SpringBootApplication
public class Boot2Demo1Application {

    public static void main(String[] args) {
        SpringApplication.run(Boot2Demo1Application.class, args);
    }
}

/**
 * @author fangshixiang
 * @description
 * @date 2019-01-28 14:39
 */
@WebServlet(urlPatterns = "/servlet/demo", asyncSupported = false)
public class DemoServlet extends HttpServlet {

    @Override
    public void init() throws ServletException {
        System.out.println("init my servlet");
        super.init();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("this is my servlet");
        super.doGet(req, resp);
    }
}

请求地址:http://localhost:8080/servlet/demo 控制台输出:

代码语言:javascript
复制
init my servlet
this is my servlet

有的朋友可能会问:为毛init方法也是此时才输出呢???为了答疑,这里之直接贴出来答案吧:

init 方法是随 Servlet 实例化而被调用的,因为 load-on-startup 就是用来设置Servlet 实例化时间的。因此,init 方法执行的时刻有两种:

  • load-on-startup 的值大于等于0,则伴随 Servlet 实例化后执行。
  • load-on-startup 的值小于0 或者 不配置(默认行为), 则在第一次 Servlet 请求的时候执行。

备注:看下面启动日志

代码语言:javascript
复制
2019-01-28 15:51:40.926  INFO 16144 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet com.sayabc.boot2demo1.servlet.DemoServlet mapped to [/servlet/demo]

可以看出,这种方式的底层原理还是转换成了ServletRegistrationBean,从而交给Spring容器管理了。同理:另外两大组件可以参照这个方式来定义和书写。

此方式优点:完全还原了源生servlet、filter、listener等功能,程序员自己也很方便的、更加细粒度。

方式二:通过ServletRegistrationBean进行组件注册

依托于SpringBoot提供的三个Bean:

代码语言:javascript
复制
ServletRegistrationBean
FilterRegistrationBean
ServletListenerRegistrationBean

实例代码:

代码语言:javascript
复制
    @Bean
    public ServletRegistrationBean MyServlet1() {
        return new ServletRegistrationBean(new DemoServlet(), "/servlet/*");
    }

访问路径:http://localhost:8080/servlet/demo控制台有对应日志输出(显然这种方式,init方法不会再执行了,因为自己new的嘛);

启动日志如下:

代码语言:javascript
复制
2019-01-28 15:59:37.412  INFO 14332 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet demoServlet mapped to [/servlet/*]

优点:内部可以直接@Autowired注入Spring的Bean,也可配合@Order调整优先级

方式三:@Component

采用此种方式是最简单的方式。Spring Boot也非常友好的给支持了。 示例:

代码语言:javascript
复制
@Component
public class DemoServlet extends HttpServlet {

    @Override
    public void init() throws ServletException {
        System.out.println("init my servlet");
        super.init();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("this is my servlet");
        super.doGet(req, resp);
    }
}

日志输出:

代码语言:javascript
复制
2019-01-28 17:20:19.601  INFO 8756 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet demoServlet mapped to [/]

显然可以看出,直接使用@Component虽然方便,但是无法自定义urlPatterns,还是收到很多局限的。

备注:别想着这么做能达到效果(虽然我建议SpringBoot支持,哈哈

代码语言:javascript
复制
@Component
@WebServlet(urlPatterns = "/servlet/demo", asyncSupported = false)
public class DemoServlet extends HttpServlet {}

这样直接做是不生效的。还是只能映射到跟路径。因为@WebServlet等注解是依赖于 @ServletComponentScan才能驱动的,因此此种方式虽然方便,但是还是有很多局限性的。

但是,但是,对应Filter,一般我们都希望他拦截所有的请求,因此这么来做是很方便的。比如TokenFilter

代码语言:javascript
复制
@Order(1)
@Component
public class TokenFilter implements Filter{}

这样默认拦截的路径为:Mapping filter: 'tokenFilter' to: [/*],从而会过滤所有的请求。

小知识点:

代码语言:javascript
复制
< url-pattern>/</url-pattern>  会匹配到/login这样的路径型url,不会匹配到模式为*.jsp这样的后缀型url
< url-pattern>/*</url-pattern> 会匹配所有url:路径型的和后缀型的url(包括/login,*.jsp,*.js和*.html等)

所以如果以后发现总是有404错误的时候,别忘了check一下 /的配置是否是/*.

优点:内部可以直接@Autowired注入Spring的Bean,也可配合@Order调整优先级

简单总结

虽然Spring已经足够强大,几乎可以屏蔽我们对Servlet的Api。(Spring4以及以下版本的底层原理还是Servlet技术,但到了Spring5以后,servlet从必选项已经成为可选项了)

但是有的时候我们自己使用原生的方案更为妥当,因此本文针对于此提出一些方案和原理分析,仅供参考

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年01月28日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前提概要
  • 三种方式
    • 方式一: @ServletComponentScan 扫描的方式(推荐)
      • 方式二:通过ServletRegistrationBean进行组件注册
        • 方式三:@Component
        • 简单总结
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档