美文网首页
渐行渐远的Servlet

渐行渐远的Servlet

作者: gringotts | 来源:发表于2018-12-27 19:38 被阅读0次

    Servlet简介

    Servlet 是一种基于 Java 技术的 Web 组件,用于生成动态内容,由容器管理。类似于其他 Java 技术组件,Servlet 是平台无 关的 Java 类组成,并且由 Java Web 服务器加载执行。通常情况,由 Servlet 容器提供运行时环境。Servlet 容器,有时候也 称作为 Servlet 引擎,作为Web服务器或应用服务器的一部分。通过请求和响应对话,提供Web 客户端与 Servlets 交互的能 力。容器管理Servlets实例以及它们的生命周期。

    从功能上,Servlet 介于 CGI(Common Gateway Interface)与服务扩展(如:Netscape Server API 或 Apache 模块)之 间。

    在体系上,Servlet 技术(或者规范)属于 Java EE 技术(规范)的一部分。不过 Servlet 并非一开始就隶属于 J2EE 或者 Java EE。接下来的小节将会介绍 Servlet 各个版本。

    Servlet 版本

    规范版本 发布时间 Java平台 主要更新
    Servlet 4.0 2017年9月 Java EE 8 支持Http/2
    Servlet 3.1 2013年5月 Java EE 7 非阻塞I/O、Http协议更新机制(WebSocket)
    Servlet 3.0 2009年12月 Jave EE 6 可插拔、简化部署、异步Servlet、安全、文件上传
    Servlet 2.5 2005年9月 Java EE 5 Annotation支持
    Servlet 2.4 2003年11月 J2EE 1.4 web.xml 支持 XML Scheme
    Servlet 2.3 2001年8月 J2EE 1.3 新增Filter、事件/监听器、Wrapper
    Servlet 2.2 1999年8月 J2EE 1.2 作为J2EE的一部分,以.war 文件作为独立web应用

    Servlet 核心API

    核心组件API 说明 起始版本 SpringFramework代表实现
    javax.servlet.Servlet 动态内容组件 1.0 DispatcherServlet
    javax.servlet.Filter Servlet过滤器 2.3 CharacterEncodingFilter
    javax.servlet.ServletContext Servlet应用上下文
    javax.servlet.AsyncContext 异步上下文 3.0
    javax.servlet.ServletContextListener ServletContext生命周期监听器 2.3 ContextLoaderListener
    javax.servlet.ServletRequestListener ServletRequest生命周期监听器 2.3 RequestContextListener
    javax.servlet.http.HttpSessionListener HttpSession生命周期监听器 2.3 HttpSessionMutexListener
    javax.servlet.AsyncListener 异步上下文监听器 3.0 StandardServletAsyncWebRequest
    javax.servlet.ServletContainerInitializer Servlet容器初始化器 3.0 SpringServletContainerInitializer

    Servlet 组件注册

    Servlet注册

    注册方式 传统方式 注解方式 编程方式
    Servlet注册 web.xml 部署<servlet> + <servlet-mapping> @WebServlet ServletContext#addServlet
    Fileter注册 web.xml部署<filter> + <filter-mapping> @WebFilter ServletContext#addFilter
    *Listener注册 web.xml 部署<listener> @WebListener ServletContext#addListener
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-
    app_2_5.xsd"
             metadata-complete="true" version="2.5">
        <context-param>
            <description>
    Spring 配置文件路径参数,
    该参数值将被 org.springframework.web.context.ContextLoaderListener 使用 </description>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                classpath*:/META-INF/spring/spring-context.xml
            </param-value>
        </context-param>
        <listener>
            <description>
    org.springframework.web.context.ContextLoaderListener 为可选申明Listener </description>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    </web-app>
    

    Spring Servlet Web

    理解Servlet生命周期

    • 初始化:init(ServletConfig)
    • 服务:service(ServletRequest,ServletResponse)
    • 销毁:destroy()

    DispatcherServlet 初始化过程

    Servlet初始化过程

    理解Filter生命周期

    • 初始化
    • 服务
    • 销毁

    理解ServletContext生命周期

    • 初始化:contextInitialized(ServletContextEvent)
    • 销毁:contextDestroyed(ServletContextEvent)

    Servlet 异步支持

    @WebServlet(asyncSupported = true,//激活异步特性
            name = "asyncServlet",// servlet 名字
            urlPatterns = "/async-servlet")
    public class AsyncServlet extends HttpServlet {
        @Override
        protected void service(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException {
            // 判断是否支持异步
            if (request.isAsyncSupported()) {
                // 创建 AsyncContext
                AsyncContext asyncContext = request.startAsync();
                // 设置超时时间
                asyncContext.setTimeout(50L);
                asyncContext.addListener(new AsyncListener() {
                    @Override
                    public void onComplete(AsyncEvent event) throws IOException {
                        println("执行完成");
                    }
    
                    @Override
                    public void onTimeout(AsyncEvent event) throws IOException {
                        HttpServletResponse servletResponse = (HttpServletResponse) event.getSuppliedResponse();
                        servletResponse.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
                        println("执行超时");
                    }
    
                    @Override
                    public void onError(AsyncEvent event) throws IOException {
                        println("执行错误");
                    }
    
                    @Override
                    public void onStartAsync(AsyncEvent event) throws IOException {
                        println("开始执行");
                    }
                });
            }
    
                println("Hello,World");
    //            ServletResponse servletResponse = asyncContext.getResponse();
    //            // 设置响应媒体类型
    //            servletResponse.setContentType("text/plain;charset=UTF-8");
    //            // 获取字符输出流
    //            PrintWriter writer = servletResponse.getWriter();
    //            writer.println("Hello,World");
    //            writer.flush();
        }
    
            private static void println(Object object) {
                String threadName = Thread.currentThread().getName();
                System.out.println("AsyncServlet[" + threadName + "]: " + object);
            }
    
    }
    

    DeferredResult 支持

    @GetMapping("/hello-world")
        public DeferredResult<String> helloWorld() {
            DeferredResult<String> result = new DeferredResult<>(50L);
    //        result.setResult("Hello,World");
            // 入队操作
    //        queue.offer(result);
            println("Hello,World");
            result.onCompletion(() -> {
                println("执行结束");
            });
    
            result.onTimeout(() -> {
                println("执行超时");
            });
    
            return result;
        }
    

    Callable支持

    @GetMapping("/callable-hello-world")
        public Callable<String> callableHelloWorld() {
            final long startTime = System.currentTimeMillis();
    
            println("Hello,World");
    
            return () -> {
                long costTime = System.currentTimeMillis() - startTime;
                println("执行计算结果,消耗:" + costTime + " ms.");
                return "Hello,World";
            };
        }
    

    CompletionStage支持

    @GetMapping("/completion-stage")
        public CompletionStage<String> completionStage(){
            final long startTime = System.currentTimeMillis();
    
            println("Hello,World");
    
            return CompletableFuture.supplyAsync(()->{
                long costTime = System.currentTimeMillis() - startTime;
                println("执行计算结果,消耗:" + costTime + " ms.");
                return "Hello,World"; // 异步执行结果
            });
        }
    

    Spring Web MVC异步Servlet实现原理

    Java Specification Requests (JSR) : https://github.com/mercyblitz/jsr

    Spring Boot Servlet Web

    Spring Boot嵌入式Servlet容器限制

    Servlet特性 兼容性 解决方案
    web.xml 不支持 RegistrationBean或 @Bean 注册
    ServletContainerInitializer 不支持 ServletContextInitializer
    @WebServlet等 有限支持 依赖@ServletComponentScan

    参考资料一

    87.2 Convert an Existing Application to Spring Boot

    you may need to add some configuration to your Application context, by replacing those elements from the web.xml , as follows:
    A @Bean of type Servlet or ServletRegistrationBean installs that bean in the container as if it were a <servlet/> and <servlet-mapping/> in web.xml .
    A @Bean of type Filter or FilterRegistrationBean behaves similarly (as a <filter/> and <filter- mapping/> ).
    An ApplicationContext in an XML file can be added through an @ImportResource in your Application . Alternatively, simple cases where annotation configuration is heavily used already can be recreated in a few lines as @Bean definitions.
    

    参考资料二

    27.4.2 Servlet Context Initialization

    Embedded servlet containers do not directly execute the Servlet 3.0+
    javax.servlet.ServletContainerInitializer interface or
    Spring’s org.springframework.web.WebApplicationInitializer interface. This is an intentional design decision intended to reduce the risk that third party libraries designed to run inside a war may break Spring Boot applications.
    

    参考资料三

    Scanning for Servlets, Filters, and listeners

    When using an embedded container, automatic registration of classes annotated
    with @WebServlet , @WebFilter , and @WebListener can be enabled by using @ServletComponentScan .
    

    Spring Boot Servlet注册

    通过RegistrationBean 注册

    • ServletContextInitializer

      • RegistrationBean
        • ServletListenerRegistrationBean
          • @WebListener
        • FilterRegistrationBean
          • @WebFilter
        • ServletRegistrationBean
          • @WebServlet

      @ServletComponentSacn 扫描package->@Web* ->RegistrationBean Bean定义 -> RegistrationBean Bean

    通过@Bean注册

    通过@ServletComponentScan注册

    Spring Boot应用传统Servlet容器部署

    基本原理

    扩展SpringBootServletInitializer

    public class DefaultSpringBootServletInitializer extends SpringBootServletInitializer {
    
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
            builder.sources(SpringBootServletBootstrap.class);
            return builder;
        }
    
    }
    
    @EnableAutoConfiguration
    @ServletComponentScan(basePackages = "com.imooc.spring.web.servlet")
    class SpringBootServletBootstrap {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringBootServletBootstrap.class, args);
        }
    
        @Bean
        @Order(Ordered.HIGHEST_PRECEDENCE)
        public ServletRegistrationBean asyncServletServletRegistrationBean(){
            ServletRegistrationBean registrationBean =  new ServletRegistrationBean(new AsyncServlet(),"/");
            registrationBean.setName("MyAsyncServlet");
            return registrationBean;
        }
    
        @Bean
        public ServletContextInitializer servletContextInitializer() {
            return servletContext -> {
                CharacterEncodingFilter filter = new CharacterEncodingFilter();
                FilterRegistration.Dynamic registration = servletContext.addFilter("filter", filter);
                registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/");
            };
        }
    }
    

    使用Tomcat 7插件(Servlet3.0)

     
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.1</version>
                <executions>
                    <execution>
                        <id>tomcat-run</id>
                        <goals>
                            <goal>exec-war-only</goal>
                        </goals>
                        <phase>package</phase>
                        <configuration>
                            <!-- ServletContext path -->
                            <path>/</path>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    

    使用 Tomcat 8 插件(Servlet 3.1)

    <build>
            <plugins>
                <!-- Tomcat 8 Maven 插件用于构建可执行 war -->
                <!-- https://mvnrepository.com/artifact/org.apache.tomcat.maven/tomcat8-maven-plugin --> <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat8-maven-plugin</artifactId>
                <version>3.0-r1655215</version>
                <executions>
                    <execution>
                        <id>tomcat-run</id>
                        <goals>
                            <!-- 最终打包成可执行的jar包 -->
                            <goal>exec-war-only</goal>
                        </goals>
                        <phase>package</phase>
                        <configuration>
                            <!-- ServletContext 路径 -->
                            <path>/</path>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            </plugins>
        </build>
        <pluginRepositories>
            <pluginRepository>
                <!-- tomcat8-maven-plugin 所在仓库 -->
                <id>Alfresco</id>
                <name>Alfresco Repository</name> <url>https://artifacts.alfresco.com/nexus/content/repositories/public/</url> <snapshots>
                <enabled>false</enabled>
            </snapshots>
            </pluginRepository>
        </pluginRepositories>
      
    

    相关文章

      网友评论

          本文标题:渐行渐远的Servlet

          本文链接:https://www.haomeiwen.com/subject/dcmdlqtx.html