JAVA 由浅及深之 Servlet
原文出处:
http://hxraid.iteye.com/blog/463701
http://www.cnblogs.com/200911/archive/2012/05/02/2479880.html(cookie , session)
http://www.cnblogs.com/cuiliang/archive/2011/10/21/2220671.html
http://blog.csdn.net/muyihuakai/article/details/5858792
http://www.cnblogs.com/JesseV/archive/2009/11/17/1605015.html
1 常用的服务器:
(1)IIS
(2)Apache (linux用的最多的)是一种web服务器,使用C语言写的
(3)Tomcat是Apache 总项目的一个,也是一种web服务器,是用java语言写的。叫做jsp和servelet的容器。
三个概念的理解:
Servlet容器<Web容器<应用服务器?
Servlet容器的主要任务就是管理Servlet的生命周期;
Web容器也称之为web服务器,主要任务就是管理和部署web应用的;
应用服务器的功能非常强大,不仅可以管理和部署web应用,也可以部署EJB应用,实现容器管理的事务等等。。。
Web服务器就是跟基于HTTP的请求打交道,而EJB容器更多是跟数据库,事务管理等服务接口交互,所以应用服务器的功能是很多的。
常见的web服务器就是Tomcat,但Tomcat同样也是Servlet服务器;
常见的应用服务器有WebLogic,WebSphere,但都是收费的;
没有Servlet容器,可以用Web容器直接访问静态Html页面,比如安装了apache等;如果需要显示Jsp/Servlet,就需要安装一个Servlet容器;但是光有servlet容器也是不够的,它需要被解析为html显示,所以仍需要一个web容器;所以,我们常把web容器和Servlet容器视为一体,因为他们两个容器都有对方的功能实现了,都没有独立的存在了,比如tomcat!
2 动态的网页技术:
CGI(Common GateWay Interface)
API(NSAPI ,ISAPI)
ASP(active server page)
PHP(personal home page)
JSP/servelet(java server page)
3 servelet 简介:
(1)servelet 是一个服务器小应用程序。
(2)用来完成B/S架构下,客户端请求的响应处理
(3)平台独立,性能优良,能以线程的方式运行。
(4)Servelet一般在容器中运行。
(5)常见的servlet容器:Tomcat
一个servlet就是Java编程语言中的一个类,它被用来扩展服务器的性能,服务器上驻留着可以通过“请求-响应”编程模型来访问的应用程序。虽然servlet可以对任何类型的请求产生响应,但通常只用来扩展Web服务器的应用程序。
4 Servlet的工作模式
(1)客户端(很可能是web浏览器)通过Http提出请求
(2)Web服务器接收请求并将其发给Servlet。
(3)Servlet程序将接收该http请求并执行某种处理。
(4)Servlet会将处理后的结果向web服务器返回应答。
(5)Web服务器将从Servlet收到的应答发回给客户端。
运行时阶段当容器接受到访问特定的servlet请求时,针对这个请求,创建对应的ServletRequest对象和 ServletResponse对象,并调用servlet的service()方法,service()根据从ServletRequest对象中获得 客户的请求信息并将调用相应的doxxx方法等进行响应,再通过ServletResponse对象生成响应结果,然后发送给客户端,最后销毁创建的ServletRequest 和ServletResponse
5 Servlet生命周期:
1) 装载Servlet : 在WEB.XML种配置<load-on-startup></load-on-startup>,指定当Web应用启动时,装载Servlet的次序。
当值为正数或零时:Servlet容器先加载数值小的servlet,再依次加载其他数值大的servlet.
当值为负或未定义:Servlet容器将在Web客户首次访问这个servlet时加载它
然而,Server通常会提供一个管理的选项,用于在Server启动时强制装载和初始化特定的Servlet。
2) Server创建一个Servlet的实例
3) Server调用Servlet的init()方法
4) 一个客户端的请求到达Server(要看配置文件中如何配置,也许是最先的步骤)
5) Server创建一个请求对象
6) Server创建一个响应对象
7) Server激活Servlet的service()方法,传递请求和响应对象作为参数
8) service()方法获得关于请求对象的信息,处理请求,访问其他资源,获得需要的信息
9) service()方法使用响应对象的方法,将响应传回Server,最终到达客户端。service()方法可能激活其它方法以处理请求,如doGet()或doPost()或程序员自己开发的新的方法。
10) 对于更多的客户端请求,Server创建新的请求和响应对象,仍然激活此Servlet的service()方法,将这两个对象作为参数传递给它。如此重复以上的循环,但无需再次调用init()方法。一般Servlet只初始化一次(只有一个对象),当Server不再需要Servlet时(一般当Server关闭时),Server调用Servlet的Destroy()方法。
6 Servlet 中service方法详解:
每一个自定义的Servlet都必须实现Servlet的接口,Servlet接口中定义了五个方法,其中比较重要的三个方法涉及到Servlet的生命周期,分别是上文提到的init(),service(),destroy()方法。GenericServlet是一个通用的,不特定于任何协议的Servlet,它实现了Servlet接口。而HttpServlet基于HTTP协议,继承于GenericServlet,因此HttpServlet也实现了Servlet接口。所以我们定义Servlet的时候只需要继承HttpServlet即可
HttpServlet 中重写了GenericServlet的service(),同时也对其进行了重载!源码如下:
/** * 通过参数的向下转型,然后调用重载的 */ //重写 public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest)req; response = (HttpServletResponse)res; } catch(ClassCastException e) { throw new ServletException("non-HTTP request or response"); } service(request, response);//调用重载的方法 } //重载 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if(method.equals("GET")) { long lastModified = getLastModified(req); if(lastModified == -1L) { doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader("If-Modified-Since"); if(ifModifiedSince < (lastModified / 1000L) * 1000L) { maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(304); } } } else if(method.equals("HEAD")) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if(method.equals("POST")) doPost(req, resp); else if(method.equals("PUT")) doPut(req, resp); else if(method.equals("DELETE")) doDelete(req, resp); else if(method.equals("OPTIONS")) doOptions(req, resp); else if(method.equals("TRACE")) { doTrace(req, resp); } else{ String errMsg = lStrings.getString("http.method_not_implemented"); Object errArgs[] = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(501, errMsg); } }
7 Servlet / Filter / Listener 的介绍和使用
从使用上看可以分为三种:简单Servlet、过滤Servlet(Filter) 和监听Servlet(Listener)
加载顺序为:context-param -> listener -> filter -> servlet
1) 过滤器:
是以一种组件的形式绑定在web应用程序当中的,与其他的Web应用程序组件不同的是,过滤器是采用链的方式进行处理的。一旦加入过滤器,所有的请求先交给过滤器处理,然后再访问相应的web资源的访问限制。
实现过滤器:如果定义一个过滤器,则直接让一个类实现javax.servlet.Filter接口即可。
Public void init(FilterConfig filterConfig)Throws ServletException
public void doFilter(ServletRequest request,Servlet response,FilterChain chain)
实现具体的过滤操作,然后通过FilterChain让请求继续向下传递。chain.doFilter(request,response);
过滤器的销毁 public void destroy()
2) 过滤器的应用:
实例一:编码过滤器EncodingFilter.java
为所有页面设置统一的编码:如果按照之前的做法,在每一个JSP或者Servlet中都重复编写request.setCharacterEncoding("GBK")的语句肯定是不可取的,会造成大量的代码重复。
<!-- 编码过滤 -->
方法1:
在web.xml中配置:
<filter> <filter-name>encoding</filter-name> <filter-class>com.myTest.setCharacterEncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
在servlet中写:
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding("GB18030"); response.setCharacterEncoding("GB18030"); chain.doFilter(request, response); }
方法2:
<!-- 编码过滤 -->
<filter> <filter-name>encoding</filter-name> <filter-class> com.myTest.setCharacterEncodingFilter </filter-class> <init-param> <param-name>charset</param-name> <param-value>GB18030</param-value> </init-param> </filter>
public void init(FilterConfig config) throws ServletException { // TODO Auto-generated method stub this.charSet=config.getInitParameter("charset");//取得初始化参数 }
实例二:登陆验证:最早的做法是通过session的方式完成,但是每个页面都这样的话,则肯定造成大量代码的重复,而通过过滤器的方法即可避免这种重复操作。
注意:向下转型:将ServletRequest转为HttpServletRequest。
总结:过滤器属于自动执行的一种servlet,过滤器依然需要在web.xml文件中进行配置。过滤器常用的功能是可以完成常用的编码过滤,及登录验证。
2) 监听器
第三种servlet程序称为监听servlet,主要功能负责Web的各种操作,当相关的事件触发后将产生事件并对此事和request三种操作进行监听。
(-)监听器主要对三个事件的动作监听:
1 对servletContext的监听
2 对session的监听
3 对request的监听
示例一:当你需要在处理任何客户端请求之前创建一个数据库连接,并且希望在整个应用过程中该连接都是可用的,这个时候ServletContextListener接口就会十分有用了。
http://chenchh.iteye.com/blog/669956
示例二:监听器:在线人员统计
http://blog.csdn.net/zhangkai08111/article/details/2787972
Session的操作:
当一个新用户打开一个动态页时,服务器会为新用户分配session,并触发HttpsessionLisener接口中的sessionCreate()事件,但是在用户销毁时时却有两种不同的方式触发sessionDestroy()事件。
方式一:调用HttpSession接口中的invalidate()方法,让一个session失效。
方式二:超过了配置session的超时时间,session超时时间可以直接在项目中的web.xml中配置。
<session-config>
<session-timeout>5</session-timeout>
<session_config>
默认的超时时间为30分钟。
3) Servlet跳转:
1.客户端跳转:在Servlet中要想进行客户端跳转,(地址栏会发生变化)直接使用HttpServletResponse接口的sendRedirect()方法即可。(但是要注意的是,此跳转只能传递session范围的属性,而无法传递request范围的属性,这是因为request属性范围只有在服务器端跳转中才可以使用)。
2.服务器端跳转:在Servlet中没有像JSP中的<jsp:forward>指令,所以如果要想执行服务器端跳转,就必须依靠RequestDispatcher接口完成。
提供两方法:forword()和include()可完成跳转实现。
但是还不够:如果要想使用此接口还要使用ServletRequest接口进行方法的实例化。
ServletRequest接口进行方法的实例化:Public RequestDispatcher getRequestDispatcher(String path){}
服务器端跳转之后,页面路径不会发生改变,而且此时可以在跳转后的JSP文件中接收到session及request范围的属性。
Servlet 单例多线程详解:
当Servlet容器收到一个Servlet请求,调度线程从线程池中选出一个工作者线程,将请求传递给该工作者线程,然后由该线程来执行Servlet的service方法。当这个线程正在执行的时候,容器收到另外一个请求,调度线程同样从线程池中选出另一个工作者线程来服务新的请求,容器并不关心这个请求是否访问的是同一个Servlet.当容器同时收到对同一个Servlet的多个请求的时候,那么这个Servlet的service()方法将在多线程中并发执行。
Servlet容器默认采用单实例多线程的方式来处理请求,这样减少产生Servlet实例的开销,提升了对请求的响应时间,对于Tomcat可以在server.xml中通过<Connector>元素设置线程池中线程的数目。
PS:
Servlet并非只是单例的. 当container开始启动,或是客户端发出请求服务时,Container会按照容器的配置负责加载和实例化一个Servlet(也可以配置为多个,不过一般不这么干).不过一般来说一个servlet只会有一个实例。
1) Struts2的Action是原型,非单实例的;会对每一个请求,产生一个Action的实例来处理。
2) Struts1的Action,Spring的Ioc容器管理的bean 默认是单实例的.
Struts1 Action是单实例的,spring mvc的controller也是如此。因此开发时要求必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts1 Action能作的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的。
Spring的Ioc容器管理的bean 默认是单实例的。
Struts2 Action对象为每一个请求产生一个实例,因此没有线程安全问题。(实际上,servlet容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃圾回收问题)。
当Spring管理Struts2的Action时,bean默认是单实例的,可以通过配置参数将其设置为原型。(scope="prototype )
http://www.2cto.com/kf/201302/189179.html
http://wenku.baidu.com/view/4bf3802cb4daa58da0114ad1.html
http://blog.csdn.net/ji_ju/article/details/8603387