转载

java基础学习:JavaWeb之Servlet

Servlet是一个特殊的Java类, 是运行在 Web 服务器中的小型 Java 程序(即:服务器端的小应用程序)。servlet 通常通过 HTTP(超文本传输协议)接收和响应来自 Web 客户端的请求。这个Java类必须继承HttpServlet。每个Servlet可以响应客户端的请求,Servlet提供不同的方法用于响应客户端请求,例如doGet,doPost,doPut等

Tomcat与Servlet的关系

Tomcat 是Web应用服务器,是一个Servlet/JSP容器. Tomcat 作为Servlet容器,负责处理客户请求,把请求传送给Servlet,并将Servlet的响应传送回给客户.而Servlet是一种运行在支持Java语言的服务器上的组件.。

Servlet最常见的用途是扩展Java Web服务器功能,提供非常安全的,可移植的,易于使用的CGI替代品。 从http协议中的请求和响应可以得知,浏览器发出的请求是一个请求文本,而浏览器接收到的也应该是一个响应文本。

java基础学习:JavaWeb之Servlet
  1. Tomcat将http请求文本接收并解析,然后封装成HttpServletRequest类型的request对象,所有的HTTP头数据读可以通过request对象调用对应的方法查询到。
  2. Tomcat同时会要响应的信息封装为HttpServletResponse类型的response对象,通过设置response属性就可以控制要输出到浏览器的内容,然后将response交给tomcat,tomcat就会将其变成响应文本的格式发送给浏览器。

Java Servlet API 是Servlet容器(tomcat)和servlet之间的接口,它定义了serlvet的各种方法,还定义了Servlet容器传送给Servlet的对象类,其中最重要的就是ServletRequest和ServletResponse。所以说我们在编写servlet时,需要实现Servlet接口,按照其规范进行操作。

Servlet执行过程

在浏览器的地址栏输入:http://ip:port/appNames/servlet

1)通过浏览器和ip:port和这个服务器建立连接。

2) 浏览器会生成一个请求数据包(路径appNames/servlet)向服务器发送请求。

3) 服务器收到请求数据包,分析请求资源路径做精准定位,通过请求的appName查找webapps文件下面的appName做匹配,匹配上了需要获取web.xml中的servlet(mapping)。

4) 服务器创建两个对象:

第一个对象:请求对象,该对象实现了HttpServletRequest接口,服务器会将请求数据包中的数据解析出来,存储在该对象里。这样做的好处是没有必要理解http协议,只需要读取request。

第二个对象:响应对象,实现了HttpServletResponse接口,作用是servlet处理完成后的结果可以存放到该对象上,然后服务器依据该对象的数据生成响应数据包。

5) servlet在执行servlet()方法时,可以通过request获取请求数据,也可以将处理结果存放到response上。然后服务器与响应对象直接形成一个默契,生成一个响应数据包给浏览器。

6)浏览器解析服务器返回的响应数据包,生成响应的结果。

java基础学习:JavaWeb之Servlet

Servlet访问的过程:

Http请求---->web.xml--------> url -pattern----->servlet-name----->servlet-class-----> QuickStratServlet(对应的Class文件)

Servlet的生命周期

Servlet生命周期可分为 5 个步骤

  1. 加载Servlet。当Tomcat第一次访问Servlet的时候,Tomcat会负责创建Servlet的实例
  2. 初始化。当Servlet被实例化后,Tomcat会调用init()方法初始化这个对象
  3. 处理服务。当浏览器访问Servlet的时候,Servlet 会调用service()方法处理请求
  4. 销毁。当Tomcat关闭时或者检测到Servlet要从Tomcat删除的时候会自动调用destroy()方法,让该实例释放掉所占的资源。一个Servlet如果长时间不被使用的话,也会被Tomcat自动销毁
  5. 卸载。当Servlet调用完destroy()方法后,等待垃圾回收。如果有需要再次使用这个Servlet,会重新调用init()方法进行初始化操作。

简单总结: 只要访问Servlet,service()就会被调用。init()只有第一次访问Servlet的时候才会被调用。 destroy()只有在Tomcat关闭的时候才会被调用。

Servlet的配置

使用配置文件

  1. 写Servlet类
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.service(req, resp);
    }
}
复制代码
  1. 在web.xml中配置Servlet
    java基础学习:JavaWeb之Servlet
    ######使用springboot
  2. 写servlet类
  3. 在springboot启动类配置
@SpringBootApplication
public class ServletApp {

    @Bean
    public ServletRegistrationBean MyServlet(){
        return new ServletRegistrationBean(new MyServlet(),"/myserv/*");
    }

    public static void main(String[] args){
        SpringApplication.run(ServletApp.class, args);
    }
}
复制代码

#Servlet细节

  • Servlet的url匹配顺序

当一个请求发送到servlet容器的时候,容器先会将请求的url减去当前应用上下文的路径作为servlet的映射url,比如我访问的是http://localhost/hiway/user/aaa.html,我的应用上下文是hiway,容器会将http://localhost/hiway去掉,剩下的/user/aaa.html部分拿来做servlet的映射匹配。这个映射匹配过程是有顺序的,而且当有一个servlet匹配成功以后,就不会去理会剩下的servlet了。其匹配规则和顺序如下:

  1. 精确路径匹配 。例子:比如servletA 的url-pattern为 /test,servletB的url-pattern为 /* ,这个时候,如果我访问的url为http://localhost/test ,这个时候容器就会先进行精确路径匹配,发现/test正好被servletA精确匹配,那么就去调用servletA,也不会去理会其他的servlet了。
  2. 最长路径匹配 。例子:servletA的url-pattern为/test/ ,而servletB的url-pattern为/test/a/ ,此时访问http://localhost/test/a时,容器会选择路径最长的servlet来匹配,也就是这里的servletB。
  3. 扩展匹配。 如果url最后一段包含扩展,容器将会根据扩展选择合适的servlet。例子:servletA的url-pattern:*.action
  4. 最后, 如果前面三条规则都没有找到一个servlet,容器会根据url选择对应的请求资源。如果应用定义了一个default servlet,则容器会将请求丢给default servlet
  • Servlet是单例的吗

在Servlet规范中,对于Servlet单例与多例定义如下:

“Deployment Descriptor”, controls how the servlet container provides instances of the servlet.For a servlet not hosted in a distributed environment (the default), the servlet container must use only one instance per servlet declaration. However, for a servlet implementing the SingleThreadModel interface, the servlet container may instantiate multiple instances to handle a heavy request load and serialize requests to a particular instance.

上面规范提到: 如果一个Servlet没有被部署在分布式的环境中,一般web.xml中声明的一个Servlet只对应一个实例。 而如果一个Servlet实现了SingleThreadModel接口,就会被初始化多个实例。默认20个

所以个人理解Servlet不算单例,只是容器让它只实例化一次,变现出来的是单例的效果而已

  • #####如何开发线程安全的Servlet
  1. 实现 SingleThreadModel 接口
  2. 使用synchronized同步对共享数据的操作
  3. 避免使用实例变量

对上面的三种方法进行测试,可以表明用它们都能设计出线程安全的Servlet程序。但是,如果一个Servlet实现了SingleThreadModel接口,Servlet引擎将为每个新的请求创建一个单独的Servlet实例,这将引起大量的系统开销。SingleThreadModel在Servlet2.4中已不再提倡使用;同样如果在程序中使用同步来保护要使用的共享的数据,也会使系统的性能大大下降。这是因为被同步的代码块在同一时刻只能有一个线程执行它,使得其同时处理客户请求的吞吐量降低,而且很多客户处于阻塞状态。另外为保证主存内容和线程的工作内存中的数据的一致性,要频繁地刷新缓存,这也会大大地影响系统的性能。所以在实际的开发中也应避免或最小化 Servlet 中的同步代码;**在Serlet中避免使用实例变量是保证Servlet线程安全的最佳选择。**从Java 内存模型也可以知道,方法中的临时变量是在栈上分配空间,而且每个线程都有自己私有的栈空间,所以它们不会影响线程的安全

学习资料: lixh1986.iteye.com/blog/235569…

  • Servlet的<load-on-startup>

在servlet的配置当中,

<load-on-startup>1</load-on-startup>
复制代码

的含义是:

标记容器是否在启动的时候就加载这个servlet。当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;当是一个负数时或者没有指定时,则指示容器在该servlet被选择时才加载。正数的值越小,启动该servlet的优先级越高。

配置load-on-startup后,servlet在startup后立即加载,但只是调用servlet的init()方法,用以初始化该servlet相关的资源。初始化成功后,该servlet可响应web请求;如未配置load-on-startup,容器一般在第一次响应web请求时,会先检测该servlet是否初始化,如未初始化,则调用servlet的init()先初始化,初始化成功后,再响应请求。

原文  https://juejin.im/post/5c160422e51d45453c1cdc30
正文到此结束
Loading...