alex_lo

导航

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 常用的服务器:

1IIS

2Apache linux用的最多的)是一种web服务器,使用C语言写的

3TomcatApache 总项目的一个,也是一种web服务器,是用java语言写的。叫做jspservelet的容器。

 

三个概念的理解:

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 动态的网页技术:

CGICommon 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

 

 

posted on 2013-03-11 17:01  alex_lo  阅读(499)  评论(0编辑  收藏  举报