Servlet学习Chapter1

Servlet的创建

创建Servlet一共有3种方式:

  • 实现Servlet接口
  • 继承GenericServlet
  • 继承HttpServlet

在Servlet接口中,有5个方法需要我们实现的,而其中有3个方法是生命周期方法。

    void init(ServletConfig var1) throws ServletException; //生命周期方法,在创建Servlet的构造方法执行之后,就会执行这一个方法。只会执行1次
    
    ServletConfig getServletConfig();
    
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;//生命周期方法,每次调用都会执行这个方法,所以会执行多次
    
    String getServletInfo();
    
    void destroy();//生命周期方法,在销毁Servlet之前,需要执行这一步

所以我们要实现Servlet接口来创建Servlet的时候,那么就需要实现上面的方法。

   package bean;
   
   import javax.servlet.*;
   import java.io.IOException;
   
   public class MyServlet implements Servlet {
       @Override
       public void init(ServletConfig servletConfig) throws ServletException {
           System.out.println("init.....");
       }
   
       @Override
       public ServletConfig getServletConfig() {
           return null;
       }
   
       @Override
       public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
           System.out.println("service.....");
       }
   
       @Override
       public String getServletInfo() {
           return null;
       }
   
       @Override
       public void destroy() {
           System.out.println("destroy......");
       }
   }

创建完毕之后,在web.xml文件中配置相关的内容(将下面的代码添加到web.xml中),开始tomcat服务器,那么就可以看见控制台中输出对应的信息:

    <servlet>
        <servlet-name>MyServlet</servlet-name>
        <servlet-class>bean.MyServlet</servlet-class> <!--这个是我们定义的servlet的子类的路径-->
    </servlet>
    <servlet-mapping>
        <servlet-name>MyServlet</servlet-name> <!--这个名字和上面的servlet标签中servlet-name的值要相同,否则没有办法找到我们自定义的servlet-->
        <url-pattern>/MyServlet</url-pattern><!--这个是我们在浏览器中输入的url-->
    </servlet-mapping>

测试结果:

在这里插入图片描述

而如果我们需要利用继承GenericServlet的方式来创建Servlet,那么我们可以来看以下GenericServlet的方法有哪些:

   public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
        private static final long serialVersionUID = 1L;
        private transient ServletConfig config;
    
        public GenericServlet() {
        }
    
        public void destroy() {
            //生命周期方法,在销毁Servlet之前执行这一步。只会执行1次
        }
    
        public String getInitParameter(String name) {
            //获取初始参数name对应的值
            return this.getServletConfig().getInitParameter(name);
        }
    
        public Enumeration<String> getInitParameterNames() {
            //获取所有的初始化参数的名字
            return this.getServletConfig().getInitParameterNames();
        }
    
        public ServletConfig getServletConfig() {
            return this.config;
        }
    
        public ServletContext getServletContext() {
            return this.getServletConfig().getServletContext();
        }
    
        public String getServletInfo() {
            return "";
        }
    
        public void init(ServletConfig config) throws ServletException {
            //生命周期方法,当构造方法执行完毕之后,就会执行这一步。只会执行1次
            this.config = config;
            this.init();
        }
    
        public void init() throws ServletException {
            //自己定义的init方法,不会有服务器调用,所以不是生命周期方法
        }
    
        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; //生命周期方法,每次调用都会执行,所以会执行多次
    
        public String getServletName() {
            //获取Servlet的名字
            return this.config.getServletName();
        }
    }

可以知道GenericServlet实现了Servlet和ServletConfig接口,所以这个类中看可以含有Servlet接口的方法,同时也会含有ServletConfig的方法。

我们主要来看一下GenericServlet中的init(ServletConfig config)方法。

    public void init(ServletConfig config) throws ServletException {
            //生命周期方法,当构造方法执行完毕之后,就会执行这一步。只会执行1次
            this.config = config;
            this.init();
        }
    
        public void init() throws ServletException {
            //自己定义的init方法,不会有服务器调用,所以不是生命周期方法
        }

在GenericServlet类中,定义了一个全局变量名为config的ServletConfig对象,然后再这个方法将参数赋值给他,然后再调用自己定义init()。而在其他的方法中,就可以利用全局变量config来获取相关的信息,例如getServletContext().但是为什么还要自定义一个init()呢?

这是因为避免我们子类中将init(ServletConfig config)方法重写,从而导致父类中的config没有被赋值,从而导致config为null,然后再getServletContext()这些方法中由于config为null,直接调用getServletContext()就会发生报错。并且config这个变量是一个私有的,子类虽然可以拥有这个变量,但是不可以直接访问,所以即使在重写,操作也会相对麻烦一些。所以我们为了维护这个config,就定义了init无参方法,我们子类重写的就是这个无参的init方法

介绍完GenericServlet之后,我们就来学习继承HttpServlet来创建Servlet:

  public abstract class HttpServlet extends GenericServlet {
        private static final long serialVersionUID = 1L;
        private static final String METHOD_DELETE = "DELETE";
        private static final String METHOD_HEAD = "HEAD";
        private static final String METHOD_GET = "GET";
        private static final String METHOD_OPTIONS = "OPTIONS";
        private static final String METHOD_POST = "POST";
        private static final String METHOD_PUT = "PUT";
        private static final String METHOD_TRACE = "TRACE";
        private static final String HEADER_IFMODSINCE = "If-Modified-Since";
        private static final String HEADER_LASTMOD = "Last-Modified";
        private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
        private static final ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");
    
        public HttpServlet() {
        }
    
        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(405, msg);
            } else {
                resp.sendError(400, msg);
            }
    
        }
    
    
        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(405, msg);
            } else {
                resp.sendError(400, msg);
            }
    
        }
    
      
        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);
            }
    
        }
    
    
        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的源码,可以知道HttpServlet继承于GenericServlet,所以HttpServlet拥有GenenricServlet的所有属性和方法。同时HttpServlet的特殊之处在于它有2个Service方法。其中service(ServletRequest,ServletResponse)才是生命周期方法,在service(ServletRequest,ServletResponse)方法,将参数进行类型强制转换成为HttpServletRequest,HttpServletResponse,然后调用service(HttpServletRequest,HttpServletResponse),并且在service(HttpServletRequest,HttpServletResponse)方法中,有request来获取请求的方法,从而判断请求方式,从而执行对应的响应。例如请求的方法为GET,那么就会执行doGet方法。所以我们来继承HttpServlet来创建Servlet的时候,只需要重写doGet,或者doPost方法即可。如果我们请求的是GET,并且我们子类中没有重写doGet方法时,那么就会发生405错误。doPost同理

 public class MyHttpServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //如果没有重写doGet方法,那么发送的是GET请求的时候,就会发生405错误
            //System.out.println("doGet");
            super.doGet(req,resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //如果没有重写doGet方法,那么发送的是POST请求的时候,就会发生405错误
            System.out.println("doPost");
            //super.doPost(req,resp);
        }
    }

测试结果:
在这里插入图片描述

ServletConfig的介绍

ServletConfig接口中,主要含有几个方法:

    public interface ServletConfig {
        String getServletName();
    
        ServletContext getServletContext();
    
        String getInitParameter(String var1);
    
        Enumeration<String> getInitParameterNames();
    }

所以GenenricServlet实现了Servlet、ServletConfig,自然会拥有这些方法了。

ServletContext

多个项目中会共享ServletContext。同时ServletContext会和Tomcat共存亡,一旦Tomcat启动,那么ServletContext就会创建,一旦关闭,那么ServletContext也会消亡。

而获取ServletContext对象的方法主要有:

  • 实现Servlet接口的时候,可以通过init(ServletConfig config)方法中的config调用getServletContext得到
  • GenericServlet实现了ServletConfig接口,所以拥有了getServletContext方法,因此可以直接调用这个方法来返回ServletContext。同样的,因为HttpServlet继承了GenericServlet,所以同样会拥有getServletContext方法。
  • HttpSession中的getServletContext方法
  • ServletContextEvent中的getServletContext方法

在web-xml中,init-param是只能被一个servlet使用的,不可以被其他的servlet使用的。那么如果我们需要定义初始参数,所有的servlet都可以调用,应该怎样做呢?那么我们需要使用的是即可,然后我们通过ServletContext对象调用getInitParamer(name),就可以获取对应的name参数的值,通过ServletContext对象调用getInitParameterNames(),就可以获取所有的公共初始化参数的名字。

  <?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>bean.AServlet</servlet-class>
             </servlet>
            <servlet-mapping>
                <servlet-name>AServlet</servlet-name>
                <url-pattern>/AServlet</url-pattern>
            </servlet-mapping>
    
            <!--利用context-param来定义公共的初始参数,从而所有的Servlet都
            可以通过ServletContext来访问-->
            <context-param>
                <param-name>param1</param-name>
                <param-value>张三</param-value>
            </context-param>
            <context-param>
                 <param-name>param2</param-name>
                 <param-value>李四</param-value>
            </context-param>
    </web-app>
  public class AServlet extends HttpServlet {
      @Override
      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
          //获取公共的初始化参数
          //1、首先需要获取ServletContext对象
          ServletContext servletContext = this.getServletContext();
          //2、servletContext对象调用getAttribute方法,从而获取对应的参数
          System.out.println("获取所有的公共初始化参数的名字及值");
          Enumeration<String> init = servletContext.getInitParameterNames();
          while(init.hasMoreElements()){
              String name = init.nextElement();
              //获取这个初始参数对应的值
              String value = servletContext.getInitParameter(name);
              System.out.println("公共初始参数名字: " + name + ", 值: " + value);
          }
  
      }
  
      @Override
      protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
          System.out.println("doPost....")
      }
  }

测试结果:
在这里插入图片描述

通过ServletContext来获取相关的资源时:

  • getRealPath(target):可以获取target文件的真实路径。有盘符的,那么这时候将这个方法的返回值来构建一个文件,是不会发生报错的。
  • getResourceAsStream(target):在获取target文件的真实路径的同时,创建一个文件输入流。
  • getResourcePaths(target):获取target目录下面的所有资源路径。值得注意的是,这个方法中的参数需要以/开始。如果没有/,那么就会发生500的错误.
    <?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>BServlet</servlet-name>
            <servlet-class>bean.BServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>BServlet</servlet-name>
            <url-pattern>/BServlet</url-pattern>
        </servlet-mapping>
    </web-app>
package bean;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Set;

public class BServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //需要获取相关文件的真实路径,那么需要通过ServletContext调用getRealPath获得
        //1、获取servletContext对象
        ServletContext servletContext = this.getServletContext();
        //2、调用getRealPath,从而获取真实路径,包括盘符,从而可以新建对应的流对象
        String realPath = servletContext.getRealPath("index.jsp");
        System.out.println("index.jsp的真实路径为: " + realPath);
        FileInputStream in = new FileInputStream(realPath);
        BufferedReader bufr = new BufferedReader(new InputStreamReader(in));
        String line = null;
        while((line = bufr.readLine()) != null){
            System.out.println(line);
        }
        System.out.println("==========================");
        //上面可以调用getResourceAsStream,从而在获取真实路径的同时,之后会创建流对象
        InputStream rs = servletContext.getResourceAsStream("index.jsp");
        BufferedReader bufr2 = new BufferedReader(new InputStreamReader(rs));
        String line2 = null;
        while((line2 = bufr2.readLine()) != null){
            System.out.println(line2);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doPost....");
    }
}

运行结果:
在这里插入图片描述

练习: 利用ServletContext来统计访问量:

解题思路:

  1. 获取ServletContext对象
  2. servletContext对象调用getAttribute方法,从而获取count这个属性对应的值count,如果值为null,说明是第一次访问,那么将值count置为1,否则,如果值count不为null,说明不是第一次访问,那么就在原来的基础上加1
  3. servletContext对象调用setAttribute方法,从而将count属性对应的值变成了更新之后的count值。此时为了让count值显示在网页上,我们利用HttpServletRequest对象调用getWriter,获取一个PrintWriter对象,然后这个对象调用println方法,就可以将count显示在网页上面了。
  4. 由于ServletContext是和服务器同生共死的关系,那么当服务器关闭之后,在重启,那么就会从1重新计数了。
    <?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>CServlet</servlet-name>
            <servlet-class>bean.CServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>CServlet</servlet-name>
            <url-pattern>/CServlet</url-pattern>
        </servlet-mapping>
    </web-app>
   public class CServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ServletContext servletContext = this.getServletContext();
            Integer count = (Integer)servletContext.getAttribute("count");
            if(count == null){
                count = 1;
            }else{
                ++count;
            }
            servletContext.setAttribute("count",count);
            //保存之后,同时需要在页面中打印出当前的访问量
            PrintWriter writer = resp.getWriter();
            writer.println("<h1>" + count + "</h1>");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("doPost....");
        }
    }

测试结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值