接触了一小段时间的servlet,以下就总结一下关于servlet的两个方面的知识,一个是模板方法的应用。另外一个是servlet多线程产生的原因。
1. 模板方法设计模式
定义一个操作中的算法的骨架。而将步骤延迟到子类中。
模板方法使得子类能够不改变一个算法的结构就可以重定义算法的某些特定步骤。
2. Servlet中模板方法的应用
模板方法在servlet中的应用的类图
当client请求一个servlet的时候首先被调用的是service方法。Service方法就是定义骨架的方法,在service方法中会依据HTTP请求的类型(GET,POST还是其它)来调用详细的doGet和doPost等方法;即实际的处理委派给了doGet和doPost等方法。这些子类的方法来终于处理浏览器的请求。以下是HttpServlet对service方法的详细实现。
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); if (ifModifiedSince < (lastModified / 1000 * 1000)) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { // // Note that this means NO servlet supports whatever // method was requested, anywhere on this server. // String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } }
3. servlet并非严格的依照模板方法来设计的
我们常说详细问题详细分析。在这里也是。servlet 讲模板方法的精髓应用到了,可是却没有全然依照模板方法设计。
3.1 子类能够重写service()方法
模板方法模式是通过把不变的行为搬移到超类。去除了子类中的反复代码。所以超类中的模板方法普通情况下是不会被重写的。模板方法同意子类又一次定义算法的某些 步骤。而不改变算法的结构。可是在servlet中的service方法中是能够被子类重写的。当子类重写了service方法之后就破坏掉了原本定义好的模板方法。
3.2 doGet和doPost方法有默认的实现
在HttpServlet类中对于这两个被子类详细实现的方式是有实现的。
以下是HttpServlet对于这两个实现的代码
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String protocol = req.getProtocol(); String msg = lStrings.getString("http.method_post_not_supported"); if (protocol.endsWith("1.1")) { resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg); } else { resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg); } }
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String protocol = req.getProtocol(); String msg = lStrings.getString("http.method_get_not_supported"); if (protocol.endsWith("1.1")) { resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg); } else { resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg); } }
这些说明在使用设计模式的时候并非生搬硬套,而是依据详细的情况灵活变动。可是其本质是不变的。
4. 为什么会出现线程安全问题
在学习servlet过程中一直听到的一个问题就是线程问题。学习的资料中提到servlet是多线程。不安全的。
servlet是由web容器管理的,当请求这个servlet的时候。web容器会依据web.xml中的配置实例化这个servlet。
当再次请求这个servlet的时候不会再实例化此servlet而是多线程的使用第一次请求的时候实例化的servlet。也就是一个servlet仅仅实例化一次。
当面对多个请求的时候web容器就会使用多线程来处理这些请求。
以下的图形是servlet多线程的结构图形
从图中能够看出,当容器收到一个请求servlet的请求的时候会从线程池中分出一个工作线程来处理这个请求,当有另外一个请求的时候会从线程池中分出另外一个工作线程来处理这个请求。所以假设多个用户同一时候请求同一个servlet,那么在容器中就会出现一个servlet对象的service方法并发运行。多线程是共享资源的,当他们并发运行的时候就非常有可能出现訪问资源冲突问题。
这样的处理请求的方式就是我们常常说的单实例多线程的处理方式。
以下是自己查找到的一个作者:http://blog.csdn.net/zljjava/article/details/6887266从内存的角度来分析的servlet的多线程问题。
假设有兴趣的话能够看看。
从内存中看一下这个问题能够总结以下的两种图形。当中一张是我们都知道JMM模型图
另外一张是多个用户请求同一个servlet的多线程运行的过程是:
主内存中的实例变量是在多个线程之间共享的。每一个线程在自己的工作线程中拷贝自己须要的实例变量到自己的缓存中。就如同上面图形中的A,当线程1改动了A,可是线程2中的A还是主存中的样子,并没有立即同步到线程2和线程3中就会出现线程安全问题。
5. 总结
对于servlet的单实例多线程问题知道的原因就能够对症下药的找解决方式了。
主要是针对变量要注意。对于模板方法能够看到我们的应用非常多可是也要注意活学活用。