Servlet(一 基础介绍)

 Servlet(Server Applet),全称Java Servlet。   对这个Servlet一直不了解,也没有学习过,如今进行各基础了解学习。
html

 Servlet是用Java编写的服务端程序,其主要功能在于交互式地浏览和修改数据,生成动态的Web内容。Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet能够响应任何类型的请求,但绝大多数状况下Servlet只用来扩展基于HTTP协议的Web服务器。java

 Servlet的工做模式:程序员

1:客户端发送求情到服务器web

2:服务器启动并调用Servlet, Servlet根据客户端请求生成响应内容并将其传给服务器数据库

3:服务器将响应返回给客户端安全

Servlet的主要相关类服务器

其中GenericServlet下面还有一个HttpServlet class.网络

Servlet接口中定义的方法多线程

 1 public interface Servlet {
 2 
 3 public void init(ServletConfig config) throws ServletException;
 4 
 5 public ServletConfig getServletConfig();
 6 
 7 public void service(ServletRequest req, ServletResponse res)
 8             throws ServletException, IOException;
 9 
10 public String getServletInfo();
11 
12 public void destroy();
13 
14 }

 根据上面Servlet的接口函数,描述一下Servlet的运行过程并发

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

1:web服务器首先检查是否已经装在并建立了该Servlet的实例对象,若是已经建立,则直接到第四步;不然执行第二步;

2:装载并建立该servlet的一个实例对象;

3:调用Servlet实例对象的init()方法;

4:建立一个用于封装Http请求消息的HttpServletRequest对象和一个表明HttpServletResponse对象,而后调用Servlet的Service()方法并将请求和响应对象做为参数传进去。

5:web应用程序被停职或从新启动以前,servlet引擎将卸载servlet,并在卸载以前调用Servlet的destory()方法。

 Servlet 的生命周期

其中,init(), service(),destory()是servlet生命周期的方法。表明了Servlet从出生到工做,再到死亡的过程。Servlet容器(例如Tomcat)会根据下面的规则来调用这三个方法。

1,init(),当Servlet第一次被请求时,Servlet容器就会开始调用这个方法来初始化一个Servlet对象出来,可是这个方法不会再后续请求中被Servlet容器调用,就像人是能出生一次同样。咱们能够利用init()方法来执行相应的初始化工做。调用这个方法时,Servlet容器会传入一个ServletConfig对象进来从而对Servlet对象进行初始化。

2,Service()方法,每当请求Servlet时,Servlet就会调用这个方法。就像人同样,须要不停的接受老板的指令而且工做。第一次请求时,Servlet容器会先调用init()方法初始化一个Servlet对象出来,而后调用它的service()方法进行工做。但在后续请求中,servlet容器只会调用service方法了。

3,destory, 当要销毁Servlet时,Servlet容器就会调用这个方法,就如人同样。到时候就会死亡。在卸载应用程序或者关闭Servlet容器时,就会发生这种状况,通常在这个方法中会写一些清除代码。

Servlet 的其它两个方法

getServletInfo( ),这个方法会返回Servlet的一段描述,能够返回一段字符串。

getServletConfig( ),这个方法会返回由Servlet容器传给init( )方法的ServletConfig对象。

 此处应该增长例子,可是在我添加例子过程当中,常常出现找不到servlet,出现404错误。

后面找到真正缘由后,再添加例子

按照博客中给的例子进行验证,经常出现http404错误。

 

GenericServlet抽象类

前面咱们编写Servlet一直是经过实现Servlet接口来编写的,可是,这种方法必须实现接口servlet中定义的全部方法。即便有一些方法中没有任何东西也要实现。而且还须要本身手动的维护ServletConfig这个对象的引用。所以,这样去实现Servlet是比较麻烦的。幸亏,GenericServlet抽象类的出现很好的解决了这个问题。本着尽量使代码简洁的原则,GenericServlet实现了Servlet和ServletConfig接口。 GenericServlet类实现:

 1 public abstract class GenericServlet implements Servlet, ServletConfig,
 2         java.io.Serializable {
 3    
 4  @Override
 5     public void destroy() {
 6         // NOOP by default
 7     }
 8 
 9     @Override
10     public ServletConfig getServletConfig() {
11         return config;
12     }
13 
14     @Override
15     public String getServletInfo() {
16         return "";
17     }
18 
19     @Override
20     public void init(ServletConfig config) throws ServletException {
21         this.config = config;
22         this.init();
23     }
24 
25     public void init() throws ServletException {
26         // NOOP by default
27     }
28 
29     @Override
30     public abstract void service(ServletRequest req, ServletResponse res)
31             throws ServletException, IOException;
32 
33 }
其中,GenericServlet抽象类相比于直接实现Servlet接口,有如下几个好处:

 1.为Servlet接口中的大部分方法提供了默认的实现,则程序员须要什么就直接改什么,再也不须要把全部的方法都本身实现了。

 2.将init( )方法中的ServletConfig参数赋给了一个内部的ServletConfig引用从而来保存ServletConfig对象,不须要程序员本身去维护ServletConfig了。

可是,咱们发如今GenericServlet抽象类中还存在着另外一个没有任何参数的Init()方法:

public void init() throws ServletException {
}

设计者的初衷究竟是为了什么呢?在第一个带参数的init()方法中就已经把ServletConfig对象传入而且经过引用保存好了,完成了Servlet的初始化过程,那么为何后面还要加上一个不带任何参数的init()方法呢?

抽象类是没法直接产生实例的,须要另外一个类去继承这个抽象类,那么就会发生方法覆盖的问题,若是在类中覆盖了GenericServlet抽象类的init()方法,那么程序员就必须手动的去维护ServletConfig对象了,还得调用super.init(servletConfig)方法去调用父类GenericServlet的初始化方法来保存ServletConfig对象,这样会给程序员带来很大的麻烦。GenericServlet提供的第二个不带参数的init( )方法,就是为了解决上述问题的。这个不带参数的init()方法,是在ServletConfig对象被赋给ServletConfig引用后,由第一个带参数的init(ServletConfig servletconfig)方法调用的,那么这意味着,当程序员若是须要覆盖这个GenericServlet的初始化方法,则只须要覆盖那个不带参数的init( )方法就行了,此时,servletConfig对象仍然有GenericServlet保存着。简而言之一句话,就是覆盖的时候能够不用管servletConfig。
可是GenericServlet没有处理Service(),  HttpServlet()才是主角。

javax.servlet.http包内容

之因此所HttpServlet要比GenericServlet强大,其实也是有道理的。HttpServlet是由GenericServlet抽象类扩展而来的,HttpServlet抽象类的声明以下所示:

1 public abstract class HttpServlet extends GenericServlet {}

HttpServlet之因此运用普遍的另外一个缘由是如今大部分的应用程序都要与HTTP结合起来使用。这意味着咱们能够利用HTTP的特性完成更多更强大的任务。Javax。servlet.http包是Servlet API中的第二个包,其中包含了用于编写Servlet应用程序的类和接口。Javax.servlet.http中的许多类型都覆盖了Javax.servlet中的类型。

HttpServlet抽象类

HttpServlet抽象类是继承于GenericServlet抽象类而来的。使用HttpServlet抽象类时,还须要借助分别表明Servlet请求和Servlet响应的HttpServletRequest和HttpServletResponse对象。

1 public interface HttpServletRequest extends ServletRequest {}
2 public interface HttpServletResponse extends ServletResponse {}

先看一下GenericServlet中service定义:

1     @Override
2     public abstract void service(ServletRequest req, ServletResponse res)
3             throws ServletException, IOException;

再来看看HttpServlet()是怎么来覆盖的:

 1     @Override
 2     public void service(ServletRequest req, ServletResponse res)
 3         throws ServletException, IOException {
 4 
 5         HttpServletRequest  request;
 6         HttpServletResponse response;
 7 
 8         try {
 9             request = (HttpServletRequest) req;
10             response = (HttpServletResponse) res;
11         } catch (ClassCastException e) {
12             throw new ServletException("non-HTTP request or response");
13         }
14         service(request, response);
15     }
16 
17 protected void service(HttpServletRequest req, HttpServletResponse resp)
18         throws ServletException, IOException {
19 
20         String method = req.getMethod();
21 
22         if (method.equals(METHOD_GET)) {
23             long lastModified = getLastModified(req);
24             if (lastModified == -1) {
25                 // servlet doesn't support if-modified-since, no reason
26                 // to go through further expensive logic
27                 doGet(req, resp);
28             } else {
29                 long ifModifiedSince;
30                 try {
31                     ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
32                 } catch (IllegalArgumentException iae) {
33                     // Invalid date header - proceed as if none was set
34                     ifModifiedSince = -1;
35                 }
36                 if (ifModifiedSince < (lastModified / 1000 * 1000)) {
37                     // If the servlet mod time is later, call doGet()
38                     // Round down to the nearest second for a proper compare
39                     // A ifModifiedSince of -1 will always be less
40                     maybeSetLastModified(resp, lastModified);
41                     doGet(req, resp);
42                 } else {
43                     resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
44                 }
45             }
46 
47         } else if (method.equals(METHOD_HEAD)) {
48             long lastModified = getLastModified(req);
49             maybeSetLastModified(resp, lastModified);
50             doHead(req, resp);
51 
52         } else if (method.equals(METHOD_POST)) {
53             doPost(req, resp);
54 
55         } else if (method.equals(METHOD_PUT)) {
56             doPut(req, resp);
57 
58         } else if (method.equals(METHOD_DELETE)) {
59             doDelete(req, resp);
60 
61         } else if (method.equals(METHOD_OPTIONS)) {
62             doOptions(req,resp);
63 
64         } else if (method.equals(METHOD_TRACE)) {
65             doTrace(req,resp);
66 
67         } else {
68             //
69             // Note that this means NO servlet supports whatever
70             // method was requested, anywhere on this server.
71             //
72 
73             String errMsg = lStrings.getString("http.method_not_implemented");
74             Object[] errArgs = new Object[1];
75             errArgs[0] = method;
76             errMsg = MessageFormat.format(errMsg, errArgs);
77 
78             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
79         }
80     }

    咱们发现,HttpServlet中的service方法把接收到的ServletRequsest类型的对象转换成了HttpServletRequest类型的对象,把ServletResponse类型的对象转换成了HttpServletResponse类型的对象。之因此可以这样强制的转换,是由于在调用Servlet的Service方法时,Servlet容器总会传入一个HttpServletRequest对象和HttpServletResponse对象,预备使用HTTP。所以,转换类型固然不会出错了。
转换以后,service方法把两个转换后的对象传入了本身定义的service方法进行运行。下面看怎么运行的:

 咱们会发如今service方法中仍是没有任何的服务逻辑,可是却在解析HttpServletRequest中的方法参数,并调用如下方法之

 一:doGet,doPost,doHead,doPut,doTrace,doOptions和doDelete。这7种方法中,每一种方法都表示一个Http方法。doGet和doPost是最经常使用的。因此,若是咱们须要实现具体的服务逻辑,再也不须要覆盖service方法了,只须要覆盖doGet或者doPost就行了。

    总之,HttpServlet有两个特性是GenericServlet所不具有的:

    1.不用覆盖service方法,而是覆盖doGet或者doPost方法。在少数状况,还会覆盖其余的5个方法。

    2.使用的是HttpServletRequest和HttpServletResponse对象

下面讲述如何解析HttpServletRequest以及如何组装HttpServletResponse

 再次先省略这两部份内容。

 

Servlet的工做流程

 

 

 Servlet线程安全

Servlet多线程体系结构是创建在Java多线程机制之上的,它的生命周期是由Web容器负责的。

 当客户端第一次请求某个Servlet时,Servlet容器将会根据web.xml配置文件实例化这个Servlet类,此时它贮存于内存中。。当有新的客户端请求该Servlet时,通常不会再实例化该Servlet类,也就是有多个线程在使用这个实例。 这样,当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的状况,数据可能会变得不一致。因此在用Servlet构建的Web应用时要注意线程安全的问题。每个请求都是一个线程,而不是进程,所以,Servlet对请求的处理的性能很是高。

 对于Servlet,它被设计为多线程的(若是它是单线程的,你就能够想象,当1000我的同时请求一个网页时,在第一我的得到请求结果以前,其它999我的都在郁闷地等待),若是为每一个用户的每一次请求都建立 一个新的线程对象来运行的话,系统就会在建立线程和销毁线程上耗费很大的开销,大大下降系统的效率。

所以,Servlet多线程机制背后有一个线程池在支持,线程池在初始化初期就建立了必定数量的线程对象,经过提升对这些对象的利用率,避免高频率地建立对象,从而达到提升程序的效率的目的。(由线程来执行Servlet的service方法,servlet在Tomcat中是以单例模式存在的, Servlet的线程安全问题只有在大量的并发访问时才会显现出来,而且很难发现,所以在编写Servlet程序时要特别注意。线程安全问题主要是由实例变量形成的,所以在Servlet中应避免使用实例变量。若是应用程设计没法避免使用实例变量,那么使用同步来保护要使用的实例变量,但为保证系统的最佳性能,应该同步可用性最小的代码路径)

 

从创建工程,到配置web.xml文件。编写Servlet,启动Tomcat等基本都没有问题。可是常常会出现http404错误。这个过程后面会在前面补例子。

 

Servlet的生命周期

Servlet的生命周期是由Tomcat容器管理。

a)客户发出请求->web服务器转发到web容器Tomcat;

b)Tomcat主线程对转发来用户的请求作出响应建立两个对象:HttpServletRequset和HttpServletResponse;

c)从请求中的URL中找到正确的Servlet,Tomcat 为其建立或者分配一个线程,同时把2建立的两个对象传递给该线程。

d)Tomcat调用Servlet的Service()方法,根据请求参数的不一样调用doGet()或者doPost()方法;

e)假设是HTTP GET请求,doGet()方法生成静态页面。并组合到响应对象里;

Servlet线程结束后,Tomcat将响应对象转换为Http响应发回给客户,同时删去请求和响应对象。

从该过程当中,咱们能够理解Servlet的生命周期:Servlet类加载(对应第3步);调用init方法(对应第3步);调用service方法(对应第四、5步);调用destory()方法(对应第6步)。

五:Servlet生命周期的各个阶段

 

Servlet的生命周期包含了下面4个阶段: 

(1)加载和实例化

Servlet容器负责加载和实例化Servlet。当Servlet容器启动时,或者在容器检测到须要这个Servlet来响应第一个请求时,建立Servlet实例。当Servlet容器启动后,它必需要知道所需的Servlet类在什么位置,Servlet容器能够从本地文件系统、远程文件系统或者其余的网络服务中经过类加载器加载Servlet类,成功加载后,容器建立Servlet的实例。由于容器是经过Java的反射API来建立Servlet实例,调用的是Servlet的默认构造方法(即不带参数的构造方法),因此咱们在编写Servlet类的时候,不该该提供带参数的构造方法。

(2)初始化

在Servlet实例化以后,容器将调用Servlet的init()方法初始化这个对象。初始化的目的是为了让Servlet对象在处理客户端请求前完成一些初始化的工做,如创建数据库的链接,获取配置信息等。对于每个Servlet实例,init()方法只被调用一次。在初始化期间,Servlet实例可使用容器为它准备的ServletConfig对象从Web应用程序的配置信息(在web.xml中配置)中获取初始化的参数信息。在初始化期间,若是发生错误,Servlet实例能够抛出ServletException异常或者UnavailableException异常来通知容器。ServletException异经常使用于指明通常的初始化失败,例如没有找到初始化参数;而UnavailableException异经常使用于通知容器该Servlet实例不可用。例如,数据库服务器没有启动,数据库链接没法创建,Servlet就能够抛出UnavailableException异常向容器指出它暂时或永久不可用。

(3)请求处理

Servlet容器调用Servlet的service()方法对请求进行处理。要注意的是,在service()方法调用以前,init()方法必须成功执行。在service()方法中,Servlet实例经过ServletRequest对象获得客户端的相关信息和请求信息,在对请求进行处理后,调用ServletResponse对象的方法设置响应信息。在service()方法执行期间,若是发生错误,Servlet实例能够抛出ServletException异常或者UnavailableException异常。若是UnavailableException异常指示了该实例永久不可用,Servlet容器将调用实例的destroy()方法,释放该实例。此后对该实例的任何请求,都将收到容器发送的HTTP 404(请求的资源不可用)响应。若是UnavailableException异常指示了该实例暂时不可用,那么在暂时不可用的时间段内,对该实例的任何请求,都将收到容器发送的HTTP 503(服务器暂时忙,不能处理请求)响应。

(4)服务终止

当容器检测到一个Servlet实例应该从服务中被移除的时候,容器就会调用实例的destroy()方法,以便让该实例能够释放它所使用的资源,保存数据到持久存储设备中。当须要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()方法。在destroy()方法调用以后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收。若是再次须要这个Servlet处理请求,Servlet容器会建立一个新的Servlet实例。

在整个Servlet的生命周期过程当中,建立Servlet实例、调用实例的init()和destroy()方法都只进行一次,当初始化完成后,Servlet容器会将该实例保存在内存中,经过调用它的service()方法,为接收到的请求服务。下面给出Servlet整个生命周期过程的UML序列图

 

本节Servlet学习在这。看了好多博客,贴的有点乱,总体对serclet有了理解。后面再加一贴针对具体例子。

 

https://www.cnblogs.com/gaoxiangde/p/4339571.html-----后面部分主要参考

https://blog.csdn.net/qq_19782019/article/details/80292110------主要参考很是好,后面一部分ServletContext没有理解。

https://www.cnblogs.com/whgk/p/6399262.html-----源码解释的很清楚

https://www.cnblogs.com/xdp-gacl/p/3729033.html  ------Java web开发入门

相关文章
相关标签/搜索