• 什么是Servlet


    Servlet

    什么是Servlet

    Servlet是JavaWeb三大组件之一,它属于动态资源。接收浏览器请求,并作出响应。

    Servlet由我们自己编写,必须实现javax.servlet.Servlet接口

    public interface Servlet {
        /** Called by the servlet container to indicate to a servlet that the servlet is being placed into service. 生命周期方法,调用一次,注意传入的参数 ServletConfig,我们可以在init中将其保存下来。*/
        void init(ServletConfig var1) throws ServletException;
    	
        /** Returns a ServletConfig object, which contains initialization and startup parameters for this servlet. */
        ServletConfig getServletConfig();
    	
        /** Called by the servlet container to allow the servlet to respond to a request.  具体的处理浏览器的请求的方法。 */
        void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
    	
        /**  Returns information about the servlet, such as author, version, and copyright. */
        String getServletInfo();
    	
        /** Called by the servlet container to indicate to a servlet that the servlet is being taken out of service. 生命周期方法,销毁时调用,可做一些资源释放操作。*/
        void destroy();
    }
    
    

    Servlet实现

    完全手动实现

    1. 新建AServlet实现Servlet interface

      public class AServlet implements Servlet {
      
          /**
           * 生命周期之内,只会执行一次。此方法并不是服务器启动即开始调用,默认只有在第一次访问时创建。
           * 但可以通过在web.xml中配置达到一启动服务就加载此Servlet的方法
           */
          @Override
          public void init(ServletConfig servletConfig) throws ServletException {
              System.out.println("Init success");
          }
      
          @Override
          public ServletConfig getServletConfig() {
              return null;
          }
      
          @Override
          public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
      		PrintWriter writer = servletResponse.getWriter();
              writer.println("<h1> Hello My Servlet <h1>");
          }
      
          @Override
          public String getServletInfo() {
              return null;
          }
      
          /**
           * 临死之前,执行一次。可以用来做一些资源释放的操作。
           */
          @Override
          public void destroy() {
              System.out.println("I am done.");
          }
      }
      
    2. 在web.xml中注册

      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
               version="4.0">
          <servlet>
              <servlet-name>AServlet</servlet-name>
              <servlet-class>cn.edu.ustc.AServlet</servlet-class>
          </servlet>
      
          <servlet-mapping>
              <servlet-name>AServlet</servlet-name>
              <url-pattern>/AServlet</url-pattern>
          </servlet-mapping>
      </web-app>
      
    3. 访问

      Servlet的生命周期

      • 出生:一个Servlet类型,服务器只创建一个实例对象。 例如在我们首次访问时,服务器通过“/AServlet”找到了绑定的Servlet名称为AServlet,然后服务器查看这个类型的Servlet是否已经创建过,如果没有创建过,那么服务器才会通过反射来创建AServlet的实例。当我们再次访问时,服务器就不会再次创建AServlet实例了,而是直接使用上次创建的实例
      • 服务:服务器接收到一次请求,就会调用service() 方法一次
      • 销毁:Servlet是不会轻易离去的,通常都是在服务器关闭时Servlet才会离去!在服务器被关闭时,服务器会去销毁Servlet,在销毁Servlet之前服务器会先去调用Servlet的destroy()方法

      ServletConfig

      ServletConfig对应web.xml中的<servlet>元素,由服务器创建。我们可以在init方法中,像GenericServlet那样保存下来。

      public interface ServletConfig {
          /** Returns the name of this servlet instance. */
          String getServletName();
      	
          /** Returns a reference to the ServletContext in which the caller is executing. */
          ServletContext getServletContext();
      	
          /** Gets the value of the initialization parameter with the given name. */
          String getInitParameter(String var1);
      	
          /** 
          * Returns the names of the servlet's initialization parameters as an Enumeration 	 * of String objects, or an empty Enumeration if the servlet has no initialization 	   * parameters.
          */
          Enumeration<String> getInitParameterNames();
      }
      

      如果在web.xml的servlet标签下面我们添加了额外的参数,我们就可以通过getInitParameter()方法获得

      <servlet>
              <servlet-name>AServlet</servlet-name>
              <servlet-class>cn.edu.ustc.AServlet</servlet-class>
              <init-param>
                  <param-name>param-name</param-name>
                  <param-value>param-value</param-value>
              </init-param>
          </servlet>
      

    JavaEE自带实现

    GenericServlet

    public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
        private static final long serialVersionUID = 1L;
        private transient ServletConfig config;
    	
        /**
        * 保存ServletConfig
        */
        public void init(ServletConfig config) throws ServletException {
            this.config = config;
            this.init();
        }
    	
        /**
        * 子类继承时如果需要重写init方法,最好覆盖此方法。覆盖上面init也可以,但一定要有super,否则成员变量
        * config不会被赋值,其他的config相关方法也无法正确使用。
        */
        public void init() throws ServletException {
        }
        
        public String getInitParameter(String name) {
            return this.getServletConfig().getInitParameter(name);
        }
    
        public Enumeration<String> getInitParameterNames() {
            return this.getServletConfig().getInitParameterNames();
        }
    
        public String getServletName() {
            return this.config.getServletName();
        }
        
        public ServletContext getServletContext() {
            return this.getServletConfig().getServletContext();
        }
    
        public ServletConfig getServletConfig() {
            return this.config;
        }
    
        public void log(String msg) {
            this.getServletContext().log(this.getServletName() + ": " + msg);
        }
    
        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;
    
    }
    

    通过分析代码可以发现,GenericServlet主要做了两件事

    • 保存系统生成的ServletConfig,并实现ServletConfig接口,子类无需再获取ServletConfig对象后再调用其中方法,直接调用GenericServlet中的ServletConfig实现接口方法即可。
    • 增加了日志方法。注意:ServletContext.log()方法,是根据tomcat目录下conf中logging.properties写入本地文档,一般来讲可以在tomcat下的logs中找到。但是如果是使用的idea,我们配置的tomcat目录并不会被直接使用,而是复制了一份放在了~/.intelliJIdea/system/tomcat中,所以如果调用了servletContext.log()或者GenericServlet中的log方法,需要注意日志所在的目录。

    HttpServlet

    HttpServlet是GenericServlet的子类,提供了对Http协议的特殊支持,而这正是我们BS所需要的!所以我们一般会通过继承HttpServlet来完成自定义的Servlet。

    public abstract class HttpServlet extends GenericServlet {
        
         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(lStrings.getString("http.non_http"));
            }
    
            this.service(request, response);
        }
        
        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.doGet(req, resp);
                } else {
                    long ifModifiedSince;
                    try {
                        ifModifiedSince = req.getDateHeader("If-Modified-Since");
                    } catch (IllegalArgumentException var9) {
                        ifModifiedSince = -1L;
                    }
    
                    if (ifModifiedSince < lastModified / 1000L * 1000L) {
                        this.maybeSetLastModified(resp, lastModified);
                        this.doGet(req, resp);
                    } else {
                        resp.setStatus(304);
                    }
                }
            } else if (method.equals("HEAD")) {
                lastModified = this.getLastModified(req);
                this.maybeSetLastModified(resp, lastModified);
                this.doHead(req, resp);
            } else if (method.equals("POST")) {
                this.doPost(req, resp);
            } else if (method.equals("PUT")) {
                this.doPut(req, resp);
            } else if (method.equals("DELETE")) {
                this.doDelete(req, resp);
            } else if (method.equals("OPTIONS")) {
                this.doOptions(req, resp);
            } else if (method.equals("TRACE")) {
                this.doTrace(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核心操作

    1. 重写service方法,首先判断是否是Http层的请求,是则进入本类中的service方法,否则抛出异常
    2. 本类中的service方法,对Http请求方式判别后,调用相应的处理方法

    主要关心下get方式所作的工作,可以通过一张流程图理解

    ServletContext

    一个项目只能有一个ServletContext,不管使用的什么方法获取的ServletContext都是同一个。

    作用:在整个Web应用的动态资源中传递数据。

    • ServletContext在项目启动时创建
    • 在项目关闭时销毁

    ServletContext获取

    1. ServletConfig # getServletContext();
    2. GenericServlet#getServletContext();
    3. HttpSession#getServletContext();
    4. ServletContextEvent#getServletContext();

    域对象

    域对象就是在多个Servlet中传递数据,ServletContext即为域对象之一。

    四个域对象

    1. PageContext;
    2. ServletRequest;
    3. HttpSession;
    4. ServletContext;

    存取方法:

    1. void setAttribute(String name, Object value)
    2. Object getAttribute(String name)
    3. void removeAttribute(String name)
    4. Enumeration getAttributeNames():获取所有域属性的名称

    在Servlet中获取资源的方法

    在获取指定资源之前,一定要搞清楚资源所在位置,以及资源相对于用来获取资源的对象之间的相对位置关系。

    1. 获取真实路径

      getServletContext().getRealPath("/b.txt");
      getServletContext().getRealPath("/WEB-INF/a.text");
      
    2. 获取资源流

      InputStream in = servletContext.getResourceAsStream(“/a.txt”);
      InputStream in = servletContext.getResourceAsStream(“/WEB-INF/b.txt”);
      
      
    3. 获取指定目录下所有资源路径

      /** 注意此方法必须以斜杠开头 */
      Set set = context.getResourcePaths("/WEB-INF");
      
      
    4. 通过ClassLoad获取资源(注意与通过class获取的区别)

      ClassLoader classLoader = this.getClass().getClassLoader();
       
      // 以斜杠开头:斜杠表示classes目录  
      // 若想过去b.txt a.txt
      classLoader.getResourceAsStream("/../../b.txtt");
      classLoader.getResourceAsStream("/../a.txt");
      
      

    Servlet总结

    1. Servlet是线程安全的吗?

      一个类型的Servlet只会有一个实例对象,那么必然会出现一个Servlet对象处理多个请求,请求可能来自多个线程,那就面临并发修改问题。

      所以:不应该在Servlet中便宜创建成员变量,因为可能会存在一个线程对这个成员变量进行写操作,另一个线程对这个成员变量进行读操作。那为什么不做成线程安全的?因为效率问题。

    2. 让服务器在启动时就创建Servlet

      默认情况下,服务器会在某个Servlet第一次收到请求时创建它。也可以在web.xml中对Servlet进行配置,使服务器启动时就创建Servlet。

      	<servlet>
              <servlet-name>AServlet</servlet-name>
              <servlet-class>cn.edu.ustc.AServlet</servlet-class>
              <load-on-startup>0</load-on-startup>
          </servlet>
      
          <servlet>
              <servlet-name>BServlet</servlet-name>
              <servlet-class>cn.edu.ustc.BServlet</servlet-class>
              <load-on-startup>1</load-on-startup>
          </servlet>
      
      

      在<servlet>元素中配置<load-on-startup>元素可以让服务器在启动时就创建该Servlet,其中<load-on-startup>元素的值必须是大于等于0整数,它的使用是服务器启动时创建Servlet的顺序。上例中,根据<load-on-startup>的值可以得知服务器创建Servlet的顺序为AServlet、bServlet。

    3. url-pattern

      servlet的url-pattern匹配规则

  • 相关阅读:
    stm32自带的flash分布图
    leetcode21
    使用redis缓存数据需要注意的问题以及个人的一些思考和理解
    Chapter 2 Open Book——2
    spring管理事务需要注意的
    如何避免在简单业务逻辑上面的细节上面出错
    leetcode387
    黑天鹅-简记
    java方法中只有值传递,没有引用传递
    Chapter 2 Open Book——1
  • 原文地址:https://www.cnblogs.com/hitomeng/p/10711299.html
Copyright © 2020-2023  润新知