Servlet是Sun公司提供的一门用于开发动态web资源的技术。html
Sun公司在其API中提供了一个Servlet接口,用户若想开发一个动态web资源(即开发一个Java程序向浏览器输出数据),须要完成如下两个步骤:java
按照一种也定俗称的称呼习惯,一般咱们也把实现了Servlet接口的Java程序,称之为Servlet。web
经过查看Servlet API文档,能够知道全部的请求都由service方法实现,只须要实现了sevice方法就能够向浏览器输出数据。因为Servlet是个接口,咱们本身建立类须要实现全部的方法,好在Sun公司已经提供了实现Servlet接口的两个类GenericServlet, HttpServlet,咱们只须要继承这两个中的其中一个便可。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,验证结果
Servlet程序是由web服务器调用,web服务器收到客户端的Servlet访问请求后:
web服务器首先检查是否已经装载并建立了该Servlet实例对象。若是是,则执行第4步,不然,执行第二步。
装载并建立该Servlet的一个实例对象。
调用Servlet实例对象的init()方法。
建立一个用于封装HTTP请求的HttpServletRequest对象和一个表明HTTP响应的HttpServletResponse对象,而后调用Servlet的service()方法,并将请求和响应对象做为参数传递进去
web应用程序被中止或从新启动以前,Servlet引擎将卸载Servlet,并在卸载前调用Servlet的destroy()方法
运行图解:
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文档。
工欲善其事,必先利其器。开发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,验证结果:
因为客户端是经过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>
同一个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>
在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>
若是咱们定义了以下一些映射关系:
问题:
若是某个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应用的静态资源就没法访问了)
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方法。
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。