JavaWeb学习笔记(七)--Servlet入门

1. Servlet简介

Servlet是Sun公司提供的一门用于开发动态web资源的技术。html

Sun公司在其API中提供了一个Servlet接口,用户若想开发一个动态web资源(即开发一个Java程序向浏览器输出数据),须要完成如下两个步骤:java

  1. 编写一个Java类,实现Servlet接口
  2. 把开发好的Java类部署到web服务器上

按照一种也定俗称的称呼习惯,一般咱们也把实现了Servlet接口的Java程序,称之为Servlet。web

1.1 实现第一个Servlet

经过查看Servlet API文档,能够知道全部的请求都由service方法实现,只须要实现了sevice方法就能够向浏览器输出数据。因为Servlet是个接口,咱们本身建立类须要实现全部的方法,好在Sun公司已经提供了实现Servlet接口的两个类GenericServletHttpServlet,咱们只须要继承这两个中的其中一个便可。apache

void service(ServletRequest req, ServletResponse res) // Called by the servlet container to allow the servlet to respond to a request.

1. 在tomcat中建立一个名为firstServlet的web应用,在web应用中建立WEB-INF/classes文件夹api

2. 在classes文件夹中建立FirstServlet.java浏览器

 1 package com.servlet;  2 
 3 import java.io.*;  4 import javax.servlet.*;  5 
 6 public class FirstServlet extends GenericServlet {  7     
 8     public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {  9         OutputStream out =  res.getOutputStream(); // 获取输出流
10         out.write("hello servlet".getBytes()); // 向浏览器输出数据
11  } 12 }

3. 在classpath中增长Servlet API对应的jar包,编译FirstServlet.javatomcat

直接编译会报错:安全

设置classpath,编译成功:服务器

1 set classpath=%classpath%;E:\Program Files\apache-tomcat-8.5.37\lib\servlet-api.jar 2 javac -d . FirstServlet.java

4. 在WEB-INF中建立web.xml,配置Servlet的对外访问路径app

web.xml但是能够参考tomcat中web示例examples,拷贝头尾和Servlet配置部分便可,修改Servlet名称和对外访问路径:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
 3  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee  5  http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
 6  version="3.1"
 7  metadata-complete="true">
 8   
 9     <servlet>
10       <servlet-name>FirstServlet</servlet-name>
11       <servlet-class>com.servlet.FirstServlet</servlet-class>
12     </servlet>
13     <servlet-mapping>
14         <servlet-name>FirstServlet</servlet-name>
15         <url-pattern>/FirstServlet</url-pattern>
16     </servlet-mapping>
17     
18 </web-app>

5. 启动Tomcat,访问http://localhost:8080/firstServlet/FirstServlet,验证结果

2. Servlet调用过程和生命周期

2.1 UML描述调用过程:

2.2 Servlet运行过程:

Servlet程序是由web服务器调用,web服务器收到客户端的Servlet访问请求后:

web服务器首先检查是否已经装载并建立了该Servlet实例对象。若是是,则执行第4步,不然,执行第二步。

装载并建立该Servlet的一个实例对象。

调用Servlet实例对象的init()方法。

建立一个用于封装HTTP请求的HttpServletRequest对象和一个表明HTTP响应的HttpServletResponse对象,而后调用Servlet的service()方法,并将请求和响应对象做为参数传递进去

web应用程序被中止或从新启动以前,Servlet引擎将卸载Servlet,并在卸载前调用Servlet的destroy()方法

运行图解:

 

 

3. Servlet接口实现类

Servlet接口Sun公司定义了两个默认实现类,分别是:GenericServlet, HttpServlet

HttpServlet指可以处理HTTP请求的Servlet,它在原有Servlet接口上添加了一些与HTTP协议处理的方法,它比Servlet接口的功能更为强大。所以开发人员在编写Servlet时,一般应继承这个类,而避免直接去实现Servlet接口。

HttpServlet在实现Servlet接口时,覆写了service()方法,该方法体内的代码会自动判断用户的请求方式,如为GET请求,则调用HttpServlet的doGet方法;如为POST请求,则调用doPost方法。所以,开发人员在编写Servlet时,一般只须要覆写doGet或doPost方法,

而不要去覆写service方法。详细信息可查看Servlet API文档

4. 使用IDEA开发Servlet

工欲善其事,必先利其器。开发web应用时,若是每次都是咱们手动建立web应用目录,很麻烦,又浪费时间。咱们须要一款IDE去帮助咱们跳过这个步骤,推荐使用IDEA 专业版。

1. 在IDEA中新建一个java web工程(通常选择Java Enterprise),IDEA会自动建立以下目录结构:

2. 配置Tomcat

通常状况下IDEA已经把Tomcat已经配置好了,项目直接就能够运行。若是没有配置就须要手动添加:

1).点击菜单中Run-> Edit Configurations

2) 修改Tomcat名称(不改也不要紧,好看而已),增长war包,配置浏览器打开路径,和web应用映射路径    

3) 启动Tomcat(点击右上角的启动图标--右三角),验证结果

3. 建立Servlet

1)在src目录下增长Servlet

2)配置Servlet包路径,并给Servlet起名

3)补充Servlet代码

在doGet方法中向浏览器输出"Hello Servlet"

1  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 2         response.getOutputStream().write("Hello Servlet!!!".getBytes()); 3     }

4)在web.xml中配置Servlet的对外访问路径

 1  <!--
 2  映射的顺序: 浏览器获取到url,匹配到url-pattern(例如:/bbb),而后根据servlet-name去找咱们的servlet-class  3  因此servlet-name能够随便取,只要能映射上便可,可是习惯上仍是和Servlet的类名保持一一致  4     -->
 5     <servlet>
 6         <servlet-name>xxx</servlet-name>
 7         <servlet-class>com.servlet.ServletDemo</servlet-class>
 8     </servlet>
 9     
10     <servlet-mapping>
11         <servlet-name>xxx</servlet-name>
12         <url-pattern>/bbb</url-pattern>
13     </servlet-mapping>

5)打开浏览器,访问 http://localhost:8080/first/bbb,验证结果:

5. Servlet开发的一些重要细节

5.1 Servlet和URL的映射

因为客户端是经过URL地址访问web服务器中的资源,因此Servlet程序若想被外界访问,必须把Servlet程序映射到一个URL地址上,这个工做在web.xml中使用<servlet>元素和<servlet-mapping>元素完成。

<servlet>元素用于注册Servlet,它包含有两个主要的子元素:<servlet-name>和<servlet-class>,分别用于设置Servlet的注册名称和Servlet的完整类名

一个<servlet-mapping>元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素:</servlet-name>和<url-pattern>,分别用于指定Servlet的注册名称和Servlet的对外访问路径。例如:

<servlet>
    <servlet-name>AnyName</servlet-name>
    <servlet-class>com.servlet.ServletDemo</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>AnyName</servlet-name>
    <url-pattern>/index.html</url-pattern>
</servlet-mapping>

5.1.1 Servlet映射多个URL

同一个Servlet能够被映射到多个URL上,即多个<servlet-mapping>元素的<servlet-name>子元素的值能够是同一个Servlet的注册名。例如:

经过/ServletDemo、/servlet/helloServlet、/hello.html均可以访问到ServletDemo。

<servlet>
    <servlet-name>ServletDemo</servlet-name>
    <servlet-class>com.servlet.ServletDemo</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>ServletDemo</servlet-name>
    <url-pattern>/ServletDemo</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>ServletDemo</servlet-name>
    <url-pattern>/servlet/helloServlet</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>ServletDemo</servlet-name>
    <url-pattern>/hello.html</url-pattern>
</servlet-mapping>

5.1.2 URL使用*通配符

在Servlet映射到的URL中也可使用*通配符,可是只有两种固定的格式:一种格式是“*.扩展名”,另外一种格式是以正斜杠(/)开头并以“/*”结尾。例如:

<servlet-mapping>
    <servlet-name>ServletDemo</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>ServletDemo</servlet-name>
    <url-pattern>/action/*</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>ServletDemo</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

若是咱们定义了以下一些映射关系:

  • Servlet1 映射到 /abc/*
  • Servlet2 映射到 /*
  • Servlet3 映射到 /abc
  • Servlet4 映射到 *.do

问题:

  • 当请求URL为“/abc/a.html”,“/abc/*” 和 “/*” 都匹配,哪一个Servlet响应? Servlet1
  • 当请求URL为“/abc”,“/abc/*” 和 “/abc” 都匹配,哪一个Servlet响应?  Servlet3
  • 当请求URL为“/abc/a.do”,“/abc/*” 和 “*.do” 都匹配,哪一个Servlet响应? Servlet1
  • 当请求URL为“/a.do”,“/*” 和 “*.do” 都匹配,哪一个Servlet响应? Servlet2
  • 当请求URL为“/xxx/yyy/a.do”, “/*” 和 “*.do”都匹配,哪一个Servlet响应? Servlet2

5.1.3 配置缺省Servlet

若是某个Servlet的映射路径仅为一个正斜杠(/),那么这个Servlet就成为当前web应用程序的缺省Servlet。

凡是在web.xml中找不到匹配的<servlet-mapping>元素的URL,它们的访问请求都将交给缺省的Servlet处理,也就是说,缺省的Servlet用于处理其余Servlet都不处理的访问请求。

在Tomcat安装目录conf/web.xml中,注册了一个名为org.apache.catalina.servlets.DefaultServlet的Servlet,并将这个设置为了缺省Servlet。

当访问Tomcat服务器的某个静态的HTML文件和图片时,实际上在访问这个缺省的Servlet(因此通常状况下咱们不会本身去写缺省的Servlet,不然web应用的静态资源就没法访问了)

5.2 Servlet生命周期和调用方式

Servlet是一个供其余Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行彻底由Servlet引擎来控制和调度。

针对客户端的屡次Servlet请求,一般状况下,服务器只会建立一个Servlet实例对象,也就是说Servlet实例对象一旦建立,它就会驻留在内存中,为后续的其余请求服务,直至web容器退出,Servlet实例对象才会销毁。

在Servlet的整个生命周期过程当中,Servlet的init()方法只会被调用一次。而对一个Servlet的每次访问请求都致使Servlet引擎调用一次Servlet的service()方法。对于每次访问请求,Servlet引擎都会建立一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,而后将这两个对象做为参数传递给调用它的Servlet的service()方法,service()方法再根据请求方式分别调用doXXX方法。

若是在<servlet>元素中配置一个<load-on-startup>元素,那么Web应用程序在启动时,就会装载并建立Servlet的实例对象、以及调用Servlet实例对象的init()方法。<load-on-startup>元素的值(正整数)表示Servlet加载的顺序,值越小优先级越高。例如:

<servlet>
    <servlet-name>ServletDemo</servlet-name>
    <servlet-class>com.servlet.ServletDemo</servlet-class>

</servlet>
<servlet-mapping>
    <servlet-name>ServletDemo</servlet-name>
    <url-pattern>/ServletDemo</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>ServletDemo2</servlet-name>
    <servlet-class>com.servlet.ServletDemo2</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>ServletDemo2</servlet-name>
    <url-pattern>/ServletDemo2</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>ServletDemo1</servlet-name>
    <servlet-class>com.servlet.ServletDemo1</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>ServletDemo1</servlet-name>
    <url-pattern>/ServletDemo1</url-pattern>
</servlet-mapping>

能够看到配置<load-on-startup>元素的ServletDemo一、ServletDemo2在服务器启动的时候就调用了init方法,而且ServletDemo1优先加载,而ServletDemo在浏览器访问其URL映射时,才调用其init方法。

5.3 Servlet线程安全问题

Servlet是单实例对象,对于类实例变量,全部线程共享实例变量。当多个线程对共享资源同时访问就可能引起线程安全问题。

解决方案:使用局部变量,例如:

 1 public class ServletDemo5 extends HttpServlet {  2     private  int classVariable = 0;  3     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  4 
 5  }  6 
 7     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  8         int localVariable = 0;  9         try { 10             for (int i = 0; i < 1000; i++) { 11                 classVariable++; 12                 localVariable++; 13                 Thread.sleep(50); 14  } 15 
16         } catch (InterruptedException e) { 17  e.printStackTrace(); 18  } 19         String outPut = "classVariable: " + classVariable + ", localVariable: " + localVariable; 20  response.getOutputStream().write(outPut.getBytes()); 21  } 22 }

启动3个浏览器分别访问:

类实例变量classVariable不是指望的值1000,局部变量localVariable是指望的值1000。

相关文章
相关标签/搜索