Tomcat工作机制 访问的流程(省略连接)
客户端发出http请求。
http请求到达tomcat。
tomcat把servlet从磁盘中加载到servlet容器中并初始化。
创建好servlet之后,tomcat把http请求解析成request对象,并转发给对应的servlet进行处理。
servlet处理完成之后,返回response对象,返回给tomcat。
tomcat接收到response对象解析成http返回请求。
再由tomcat发送给客户端。
什么是Servlet Servlet(Server Applet),全称Java Servlet,未有中文译文。是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。 Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
Servlet API 概览 Servlet API 包含以下4个Java包:
javax.servlet 其中包含定义servlet和servlet容器之间契约的类和接口。
javax.servlet.http 其中包含定义HTTP Servlet 和Servlet容器之间的关系。
javax.servlet.annotation 其中包含标注servlet,Filter,Listener的标注。它还为被标注元件定义元数据。
javax.servlet.descriptor,其中包含提供程序化登录Web应用程序的配置信息的类型。
Servlet的使用方式 Servlet技术的核心是Servlet,它是所有Servlet类必须直接或者间接实现的一个接口。在编写实现Servlet的Servlet类时,直接实现它。在扩展实现这个这个接口的类时,间接实现它。
Servlet 的工作原理 Servlet接口定义了Servlet与servlet容器之间的契约。这个契约是:Servlet容器将Servlet类载入内存,并产生Servlet实例和调用它具体的方法。但是要注意的是,在一个应用程序中,每种Servlet类型只能有一个实例。
servlet的单例和多例问题
看是否实现了SingleThreadModel接口,实现之后可以实现多例,这个接口在servlet4.0时废弃了,官方不提倡使用。
还要看servlet容器,一般容器都是单一实现。
在xml配置的时候,servlet web是不允许配置同名,可以同名的话,可以配置多个。
用户请求致使Servlet容器调用Servlet的Service()方法,并传入一个ServletRequest对象和一个ServletResponse对象。ServletRequest对象和ServletResponse对象都是由Servlet容器(例如TomCat)封装好的,并不需要程序员去实现,程序员可以直接使用这两个对象。 ServletRequest中封装了当前的Http请求,因此,开发人员不必解析和操作原始的Http数据。ServletResponse表示当前用户的Http响应,程序员只需直接操作ServletResponse对象就能把响应轻松的发回给用户。 对于每一个应用程序,Servlet容器还会创建一个ServletContext对象。这个对象中封装了上下文(应用程序)的环境详情。每个应用程序只有一个ServletContext。每个Servlet对象也都有一个封装Servlet配置的ServletConfig对象。
Servlet接口 1 2 3 4 5 6 7 public interface Servlet { public abstract void init (ServletConfig var1) throws ServletException ; public abstract ServletConfig getServletConfig () ; public abstract void service (ServletRequest var1, ServletResponse var2) throws ServletException, IOException ; public abstract String getServletInfo () ; public abstract void destroy () ; }
Servlet 的生命周期 其中,init( ),service( ),destroy( )是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容器时,就会发生这种情况,一般在这个方法中会写一些清除代码。
getServletInfo(),这个方法会返回Servlet的一段描述,可以返回一段字符串。 getServletConfig(),这个方法会返回由Servlet容器传给init()方法的ServletConfig对象。
ServletRequset接口 Servlet容器对于接受到的每一个Http请求,都会创建一个ServletRequest对象,并把这个对象传递给Servlet的Sevice( )方法。
1 2 3 4 5 public interface ServletRequest { public int getContentLength () ; public String getContentType () ; public String getParameter (String var1) ; }
ServletResponse接口 javax.servlet.ServletResponse接口表示一个Servlet响应,在调用Servlet的Service( )方法前,Servlet容器会先创建一个ServletResponse对象,并把它作为第二个参数传给Service( )方法。ServletResponse隐藏了向浏览器发送响应的复杂过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public interface ServletResponse { public String getCharacterEncoding () ; public String getContentType () ; public ServletOutputStream getOutputStream () throws IOException ; public PrintWriter getWriter () throws IOException ; public void setCharacterEncoding (String var1) ; public void setContentLength (int var1) ; public void setContentType (String var1) ; public void setBufferSize (int var1) ; public int getBufferSize () ; public void flushBuffer () throws IOException ; public void resetBuffer () ; public boolean isCommitted () ; public void reset () ; public void setLocale (Locale var1) ; public Locale getLocale () ; }
在向客户端发送响应时,大多数都是使用该对象向客户端发送HTML。还有一个方法也可以用来向浏览器发送数据,它就是getOutputStream,从名字就可以看出这是一个二进制流对象,因此这个方法是用来发送二进制数据的。
ServletConfig接口 当Servlet容器初始化Servlet时,Servlet容器会给Servlet的init()方式传入一个ServletConfig对象。
1 2 3 4 5 6 7 public interface ServletConfig{ public String getServletName () ; public Enumeration getInitParameterNames () ; public ServletContext getServletContext () ; public String getInitParameter (String paramString) ; }
ServletContext对象 ServletContext对象表示Servlet应用程序。每个Web应用程序都只有一个ServletContext对象。在将一个应用程序同时部署到多个容器的分布式环境中,每台Java虚拟机上的Web应用都会有一个ServletContext对象。 ServletContext中存放所有有关共享数据和资源。所以只会有一个ServletContext。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public interface ServletContext{ public int getMajorVersion () ; public int getMinorVersion () ; public String getServerInfo () ; public String getServletContextName () ; public void log (String paramString) ; public void removeAttribute (String paramString) ; public Enumeration getAttributeNames () ; public Enumeration getInitParameterNames () ; public Enumeration getServletNames () ; public Enumeration getServlets () ; public InputStream getResourceAsStream (String paramString) ; public Object getAttribute (String paramString) ; public void setAttribute (String paramString, Object paramObject) ; public void log (Exception paramException, String paramString) ; public String getInitParameter (String paramString) ; public String getMimeType (String paramString) ; public String getRealPath (String paramString) ; public void log (String paramString, Throwable paramThrowable) ; public URL getResource (String paramString) throws MalformedURLException ; public Set getResourcePaths (String paramString) ; public RequestDispatcher getNamedDispatcher (String paramString) ; public RequestDispatcher getRequestDispatcher (String paramString) ; public Servlet getServlet (String paramString) throws ServletException ; public ServletContext getContext (String paramString) ; }
GenericServlet抽象类 前面我们编写Servlet一直是通过实现Servlet接口来编写的,但是,使用这种方法,则必须要实现Servlet接口中定义的所有的方法,即使有一些方法中没有任何东西也要去实现,并且还需要自己手动的维护ServletConfig这个对象的引用。因此,这样去实现Servlet是比较麻烦的。 所以出现了GenericServlet抽象类,只需关注业务,不需要关心servlet的初始问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 package javax.servlet;import java.io.IOException;import java.io.Serializable;import java.util.Enumeration;public abstract class GenericServlet implements Servlet , ServletConfig , Serializable { private transient ServletConfig config; public GenericServlet () { } public void destroy () { } public void init () throws ServletException { } public String getServletInfo () { return "" ; } public String getServletName () { return this .config.getServletName(); } public void log (String msg) { this .getServletContext().log(this .getServletName() + ": " + msg); } public Enumeration getInitParameterNames () { return this .getServletConfig().getInitParameterNames(); } public ServletConfig getServletConfig () { return this .config; } public void init (ServletConfig config) throws ServletException { this .config = config; this .init(); } public ServletContext getServletContext () { return this .getServletConfig().getServletContext(); } public String getInitParameter (String name) { return this .getServletConfig().getInitParameter(name); } public void log (String message, Throwable t) { this .getServletContext().log(this .getServletName() + ": " + message, t); } public abstract void service (ServletRequest var1, ServletResponse var2) throws ServletException, IOException ; }
这里的无参初始化,只需要关注ServletConfig之外的初始化操作即可,不需要自己维护ServletConfig; 然而,虽然GenricServlet是对Servlet一个很好的加强,但是也不经常用,因为他不像HttpServlet那么高级。HttpServlet才是主角,在现实的应用程序中被广泛使用。那么我们接下来就看看传说中的HttpServlet到底厉害在哪里吧。
HttpServlet抽象类 HttpServlet抽象类是继承于GenericServlet抽象类而来的。使用HttpServlet抽象类时,还需要借助分别代表Servlet请求和Servlet响应的HttpServletRequest和HttpServletResponse对象。
HttpServletRequest接口扩展于javax.servlet.ServletRequest接口,HttpServletResponse接口扩展于javax.servlet.servletResponse接口。
1 2 3 public interface HttpServletRequest extends ServletRequest public interface HttpServletResponse extends ServletResponse
HttpServlet抽象类覆盖了GenericServlet抽象类中的Service( )方法,并且添加了一个自己独有的Service(HttpServletRequest request,HttpServletResponse方法。 GenericServlet抽象类中的service方法:
1 public abstract void service (ServletRequest var1, ServletResponse var2) throws ServletException, IOException ;
HttpServlet抽象类中重写父类的service方法:
1 2 3 4 5 6 7 8 9 10 11 12 public void service (ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest)req; response = (HttpServletResponse)res; } catch (ClassCastException var6) { throw new ServletException("non-HTTP request or response" ); } this .service(request, response); }
HttpServlet抽象类中重载的service方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod() ; long lastModified; if (method .equals("GET" )) { lastModified = this.getLastModified(req ) ; if (lastModified == -1L ) { this.do Get(req , resp ) ; } else { long ifModifiedSince = req.getDateHeader("If-Modified-Since" ) ; if (ifModifiedSince < lastModified / 1000L * 1000L ) { this.maybeSetLastModified(resp , lastModified ) ; this.do Get(req , resp ) ; } else { resp.setStatus(304) ; } } } else if (method .equals("HEAD" )) { lastModified = this.getLastModified(req ) ; this.maybeSetLastModified(resp , lastModified ) ; this.do Head(req , resp ) ; } else if (method .equals("POST" )) { this.do Post(req , resp ) ; } else if (method .equals("PUT" )) { this.do Put(req , resp ) ; } else if (method .equals("DELETE" )) { this.do Delete(req , resp ) ; } else if (method .equals("OPTIONS" )) { this.do Options(req , resp ) ; } else if (method .equals("TRACE" )) { this.do Trace(req , resp ) ; } else { String errMsg = lStrings.getString("http.method_not_implemented" ) ; Object[] errArgs = new Object[] {method }; errMsg = MessageFormat . format(errMsg, errArgs); resp.sendError(501, errMsg ) ; } } }
HttpServlet在使用的时候,只需要重写doPost或doGet两个常用的方法即可。不需要太繁琐的实现。
HttpServletRequest接口 HttpServletRequest表示Http环境中的Servlet请求。它扩展于javax.servlet.ServletRequest接口,并添加了几个方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public interface HttpServletRequest extends ServletRequest { String BASIC_AUTH = "BASIC" ; String FORM_AUTH = "FORM" ; String CLIENT_CERT_AUTH = "CLIENT_CERT" ; String DIGEST_AUTH = "DIGEST" ; boolean isRequestedSessionIdFromCookie () ; boolean isRequestedSessionIdFromURL () ; boolean isRequestedSessionIdFromUrl () ; boolean isRequestedSessionIdValid () ; String getAuthType () ; String getContextPath () ; String getMethod () ; String getPathInfo () ; String getPathTranslated () ; String getQueryString () ; String getRemoteUser () ; String getRequestURI () ; String getRequestedSessionId () ; String getServletPath () ; int getIntHeader (String var1) ; long getDateHeader (String var1) ; boolean isUserInRole (String var1) ; StringBuffer getRequestURL () ; Principal getUserPrincipal () ; Enumeration getHeaderNames () ; Cookie[] getCookies(); HttpSession getSession () ; HttpSession getSession (boolean var1) ; String getHeader (String var1) ; Enumeration getHeaders (String var1) ; }
HttpServletResponse接口 在Service API中,定义了一个HttpServletResponse接口,它继承自ServletResponse接口,专门用来封装HTTP响应消息。 由于HTTP请求消息分为状态行,响应消息头,响应消息体三部分,因此,在HttpServletResponse接口中定义了向客户端发送响应状态码,响应消息头,响应消息体的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 public interface HttpServletResponse extends ServletResponse { int SC_CONTINUE = 100 ; int SC_SWITCHING_PROTOCOLS = 101 ; int SC_OK = 200 ; int SC_CREATED = 201 ; int SC_ACCEPTED = 202 ; int SC_NON_AUTHORITATIVE_INFORMATION = 203 ; int SC_NO_CONTENT = 204 ; int SC_RESET_CONTENT = 205 ; int SC_PARTIAL_CONTENT = 206 ; int SC_MULTIPLE_CHOICES = 300 ; int SC_MOVED_PERMANENTLY = 301 ; int SC_MOVED_TEMPORARILY = 302 ; int SC_FOUND = 302 ; int SC_SEE_OTHER = 303 ; int SC_NOT_MODIFIED = 304 ; int SC_USE_PROXY = 305 ; int SC_TEMPORARY_REDIRECT = 307 ; int SC_BAD_REQUEST = 400 ; int SC_UNAUTHORIZED = 401 ; int SC_PAYMENT_REQUIRED = 402 ; int SC_FORBIDDEN = 403 ; int SC_NOT_FOUND = 404 ; int SC_METHOD_NOT_ALLOWED = 405 ; int SC_NOT_ACCEPTABLE = 406 ; int SC_PROXY_AUTHENTICATION_REQUIRED = 407 ; int SC_REQUEST_TIMEOUT = 408 ; int SC_CONFLICT = 409 ; int SC_GONE = 410 ; int SC_LENGTH_REQUIRED = 411 ; int SC_PRECONDITION_FAILED = 412 ; int SC_REQUEST_ENTITY_TOO_LARGE = 413 ; int SC_REQUEST_URI_TOO_LONG = 414 ; int SC_UNSUPPORTED_MEDIA_TYPE = 415 ; int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416 ; int SC_EXPECTATION_FAILED = 417 ; int SC_INTERNAL_SERVER_ERROR = 500 ; int SC_NOT_IMPLEMENTED = 501 ; int SC_BAD_GATEWAY = 502 ; int SC_SERVICE_UNAVAILABLE = 503 ; int SC_GATEWAY_TIMEOUT = 504 ; int SC_HTTP_VERSION_NOT_SUPPORTED = 505 ; void sendError(int var1) throws IOException; void setStatus(int var1); void sendError(int var1, String var2) throws IOException; void setStatus(int var1, String var2); void sendRedirect(String var1) throws IOException; bool ean containsHeader(String var1); void addIntHeader(String var1, int var2); void setIntHeader(String var1, int var2); void addDateHeader(String var1, long var2); void setDateHeader(String var1, long var2); void addCookie(Cookie var1); String encodeRedirectURL(String var1); String encodeRedirectUrl(String var1); String encodeURL(String var1); String encodeUrl(String var1); void addHeader(String var1, String var2); void setHeader(String var1, String var2); }
虽然response对象的getOutSream()和getWriter()方法都可以发送响应消息体,但是他们之间相互排斥,不可以同时使用,否则会发生异常。
中文乱码问题 浏览器默认字节码为ISO-8859-1,是不支持中文的。http传输时也是默认ISO-8859-1的。所以在使用中文的时候,会出现乱码的情况。一般情况转UTF-8即可。特殊情况的生僻字的话,就需要用Unicode码了。unicode不能直接用,需要进行转换。 最终乱码问题都需要进行一个编码统一。
servlet的局限性 虽然servlet能输出html格式的字符串到页面,但是对复杂多变且几百行的html文件就有点力不从心了,这导致大量java转向php,这种内嵌html的动态语言,让做页面变得更为轻松。sun公司为了解决这一个问题,也研发出了一个动态网页生成技术jsp,同样可以在网页中去写java代码。 虽说jsp是动态页面生成技术,但是它还是基于servlet进行封装的。Post not found: daily/2200608 jsp和servlet有什么区别
ServletContextListener(Servlet全局监听器) servlet上下文(容器)监听接口。单纯的监听servlet上下文的创建和销毁。这就可以在servlet容器的创建和销毁的时候做一些事情。
1 2 3 4 public interface ServletContextListener extends EventListener { void contextDestroyed(ServletContextEvent var1); void contextInitialized(ServletContextEvent var1); }
那spring中核心用的就是ioc容器,那ioc是何时被加载的呢。这里spring提供了一个ContextLoaderListener的类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class ContextLoaderListener extends ContextLoader implements ServletContextListener { private ContextLoader contextLoader; public ContextLoaderListener () {} public ContextLoaderListener (WebApplicationContext context) { super (context); } public void contextInitialized (ServletContextEvent event) { this .contextLoader = this .createContextLoader(); if (this .contextLoader == null ) { this .contextLoader = this ; } this .contextLoader.initWebApplicationContext(event.getServletContext()); } @Deprecated protected ContextLoader createContextLoader () { return null ; } @Deprecated public ContextLoader getContextLoader () { return this .contextLoader; } public void contextDestroyed (ServletContextEvent event) { if (this .contextLoader != null ) { this .contextLoader.closeWebApplicationContext(event.getServletContext()); } ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }
ContextLoaderListener实现了ServletContextListener,在tomcat初始化后,ContextLoaderListener监听到servlet容器初始化完成之后,开始构建ioc容器。在tomcat关闭时,ContextLoaderListener监听到servlet容器被销毁后,开始ioc容器的销毁。