Servlet笔记

Servlet概述

1.什么是Servlet

Servlet是Javaweb的三大组件之一,它属于动态资源。servlet的作用是处理请求,服务器会把接收到的请求交给servlet来处理,在servlet中通常需要:

  • 接收请求数据﹔
  • 处理请求;
  • 完成响应。

例如客户端发出登录请求,或者输出注册请求,这些请求都应该由servlet来完成处理! servlet需要我们自己来编写,每个 servlet必须实现javax.servlet.Servlet接口。

2.实现Servlet的方式

实现servlet,有三种方式:

  • 实现javax.servlet.Servlet接口;
  • 继承 javax.servlet.GenericServlet类;
  • 继承 javax.servlet.http.HttpServlet类;

通常我们会去继承 HttpServlet类来完成我们的Servlet ,但学习Servlet还要从javax.servlet.Servlet,接口开始学习。

1.Servlet

Servlet.java代码

  • public class 类名 implements Servlet {
        /**
         * 它是生命周期方法
         * 它会在servlet对象创建之后马上执行,并只执行一次!(出生之后)
         */
        public void init (ServletConfig servletconfig) throws ServletException {
            System.out.println("init....");
        };
    
        /**
         *可以用来获取Servlet的配置信息
         */
        public ServletConfig getServletConfig(){
            return null;
        };
    
        /**
         *它是生命周期方法,它会被调用多次!!!
         * 每次处理请求都是在调用这个方法
         */
        public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {};
        /**
         *获取Servlet的信息
         */
        public String getServletInfo(){
            return null;
        };
    
        /**
         * 它也是生命周期方法
         * 它会在Servlet被销毁之前调用,并且它只会被调用一次!
         */
        public void destroy(){
            System.out.println("destroy....");
        };
    }
    

1.如何让浏览器访问Servlet

(1)给Servlet指定一个Servlet路径(让Servlet与一个路径绑定在一起)

(2)浏览器访问Servlet路径

(3)给Servlet配置Servlet路径,这需要在web.xml中对Servlet进行配置

<servlet>
    <servlet-name>xxx</servlet-name>
    <servlet-class>cn.web.servlet.AServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>xxx</servlet-name>
    <url-pattern>/AServlet</url-pattern>
</servlet-mapping>

(4)解释浏览器是如何访问的

  • 浏览器通过网址:http://localhost:8080/Servlet/AServlet 中“/AServlet“在标签去寻找到这个 /AServlet,然后通过xxx这里面”xxx“再去找到标签下的 xxx,最后去访问 标签下的”cn.web.servlet.AServlet”这个路径。
  • image-20240405140935750

2.生命周期

Servlet有5个方法,其中三个是生命周期方法

生命周期方法:

  • void init(Servlatconfig):出生之后(1次)

  • void service( ServletRequest request,ServletResponse response):每次处理请求时都会被调用;

  • void destroy():被销毁之前(1次)

特性:

  • 单例,一个类只有一个对象﹔当然可能存在多个SerMlet,类!

  • 线程不安全的,所以它的效率是高的

Serlet类我们来写,但对象由服务器来创建,并且由服务器调用相应的方法。

2.HttpServlet

1、下面截图展示的是HttpServlet的执行方式

image-20240405151512113

2、下面展示的HttpServlet的时序图

image-20240405152150037

3、了解405错误

405错误是HTTP协议中的一个状态码,表明服务器已经理解了请求方法,但是拒绝执行它。这种情况通常发生在你尝试使用一个特定URL不支持的请求方法时,比如GET或POST。这可能是由于网站开发者有意设置的某些限制,以防止客户端执行特定类型的操作,或者是由于服务器配置错误导致的。

分析405错误的原因

  1. HTTP请求方法不正确:最常见的原因是使用的HTTP请求方法(如GET、POST、PUT、DELETE等)不被服务器接受或支持。如果你向服务器发送的请求方法不被允许,服务器将返回405错误。
  2. 服务器配置问题:如果服务器配置不正确,它可能无法正确处理请求。例如,Web服务器软件的配置文件中可能存在无意的处理说明,导致405错误。
  3. 防火墙或安全软件限制:某些防火墙或安全软件可能阻止某些请求方法,如PUT和DELETE。这可能导致405错误。
  4. 插件或主题问题:如果你在使用一个内容管理系统(如WordPress),某些插件或主题可能与服务器配置不兼容,导致405错误。

3.ServletConfig简介

image-20240405143629161

演示代码

public void init (ServletConfig servletconfig) throws ServletException {
    System.out.println("init....");
    // 获取初始化参数
    System.out.println(servletconfig.getInitParameter("p1"));
    System.out.println(servletconfig.getInitParameter("p2"));
    // 获取初始化参数的名称
    Enumeration<String> e = servletconfig.getInitParameterNames();
    while (e.hasMoreElements()){
        System.out.println(e.nextElement());
    }
};

4.Servlet细节

4.1 Servlet与线程安全

因为一个类型的serlet.只有一个实例对象,那么就有可能会现时出一个Servlet同时处理多个请求

那么Servlet,是否为线程安全的呢?答案是:“不是线程安全的”,这说明servlet,的工作效率很高,但也存在线程安全问题

所以我们不应该在servlet中创建成员变量,因为可能会存在一个线程对这个成员变量进行写操作,另一个线程对这个成员变量进行读操作。

如果实在需要创建变量可以使用下面三种方法:

  • 不要在Servlet中创建成员,创建局部变量即可;
  • 可以创建无状态成员;
  • 可以创建有状态成员,但状态必须为只读的。

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

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

通过 标签内的非负整数的顺序依次创建Servlet。

<servlet>
    <servlet-name>hello1</servlet-name>
    <servlet-class>cn.web.servlet.Hello1Servlet</servlet-class>
    <load-on-startup>0</load-on-startup>
</servlet>
<servlet>
    <servlet-name>hello2</servlet-name>
    <servlet-class>cn.web.servlet.Hello2Servlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet>
    <servlet-name>hello3</servlet-name>
    <servlet-class>cn.web.servlet.Hello3Servlet</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>

4.3 url-pattern标签

的子元素,用来指定 Servlet的访问路径,即URL。它必须是以“/”开头
(1)可以在中给出多个,例如:

<servlet-mapping>
    <servlet-name>xxx</servlet-name>
    <url-pattern>/AServlet</url-pattern>
    <url-pattern>/do</url-pattern>
</servlet-mapping>

那么这说明一个Servlet,绑定了两个 URL,无论访问/AServlet,还是/Servlet,访问的都是AServlet

(2)还可以在中使用通配符,所谓通配符就是星号,星号可以匹配任何URL前缀或后缀,使用通配符可以命名一个 servlet,绑定一组URL,例如:

  • <url-pattern>/servlet/*<url-patter>
    
    • servlet/a、/servlet/b,都匹配/servlet/*; 这为匹配路径
  • <url-pattern>*.do</url-pattern>
    
    • /abc/def/ghi.do、/a.do,都匹配*.do; 这为匹配扩展名
  • <url-pattern>/*<url-pattern>
    
    • 匹配所有URL;
  • 请注意,通配符要么为前缀,要么为后缀,不能出现在URL中间位置,也不能只有通配符。例如:

    /*.do  错误的
    一个 URL中最多只能出现一个通配符
    

4 web.xml文件的继承(了解)

每个完整的Javaweb.应用中都需要有web.xml,但我们不知道所有的web.xml文件都有一个共同的父文件,它在 Tomcat的conf/web.xml路径。

5.ServletContext对象(重要)

  • 一个项目只有一个ServletContext对象!
  • 我们可以在N多个Servlet中来获取这个唯一的对象,使用它可以给多个 Servlet传递数据。
  • 这个对象在Tomcat启动时就创建,在Tomcat关闭时才会死去

5.1 SerxletContext概述

  • 概述:ServletContext对象是一个应用上下文对象,也是一个域对象。表示Servlet应用程序,**每个web应用程序都只有一个ServletContext对象 **。
  • ServletContext对象的作用:
    (1)有了ServletContext对象,就可以共享从应用程序中的所有资源访问到的数据信息,并且可以动态注册web对象。
    (2)可以获得应用域的全局初始化参数,以及达到Servlet之间的数据共享
    (3)可以作为域对象在整个应用中共享数据;域对象即在一定的作用范围内实现资源共享;
    (4)可以用来获取应用中的资源在服务器上的绝对路径;
    (5)获取文件的mime类型: 在网络传输中,并不是以扩展名来区分文件的类型,都是以mime类型;如:text/html;表示一个html文件。
  • 生命周期: 应用一加载则创建,应用被停止则销毁
  • 创建时间: 加载web应用时,创建ServletContext对象。

总结:

  • 服务器会为每个应用创建一个 ServletContext对象;
  • ServletContext对象的创建是在服务器启动时完成的;
  • ServletContext对象的销毁是在服务器关闭时完成的。
  • ServletContext对象的作用是在整个 web应用的动态资源之间共享数据!例如在 AServlet,中向ServletContext,对象中保存一个值,然后在BServlet.中就可以获取这个值,这就是共享数据了。

5.2 获取ServletContext对象

  • Servletconfig接口有getServletContext()方法;
  • GenaricServlet接口有getServletContext()方法;
  • Httpsession接口有getServletContext()方法;
  • ServletContextEvent接口有getServletContext()方法

在Servlet中获取ServletContext对象:

  • 在void init(ServletConfig config)中:

    ServletContext context = config.getServletContext();
    
    • ServletConfig类的 getServletContext()方法可以用来获取Servletcontext对象;
    • 在GenericeServlet 或 HttpServlet中获取ServletContext对象:
  • GenericServlet 类有 getServletContext()方法,所以可以直接使用this.getServletContext()来获取;

public class Myserlet implements Serylet {
    public void init(ServletConfig config){
        ServletContext context = config.getServletContext();
    }
}

5.3 域对象的功能

  • 域对象指的是对象有作用域,即有作用范围。域对象可以实现数据的共享,不同作用范围对象,共享数据的范围能力也不同。
  • 在Servlet规范中,一共有4个域对象。ServletContext就是其中的一个。是web应用中最大的作用域,也叫application应用域,可以实现整个应用之间的数据共享!

ServletContext是Javaweb,四大域对象之一:

  • PageContext; -- PageContext页面域
  • ServletReguest ; -- request请求域
  • Httpsession; -- session会话域
  • ServletContext; -- application应用域

所有域对象都有存取数据的功能,因为域对象内部有一个 Map,用来存储数据,下面是ServletContext对象用来操作数据的方法:

  • void setAttribute(String name, object value):用来存储一个对象,也可以称之为存储一个域属性,例如: servletContext.setAttribute("xxx" , "XXX"),在ServletContext中保存了一个域属性,域属性名称为xxx ,域属性的值为.XXX。请注意,如果多次调用该方法,并且使用相同的name,那么会覆盖上一次的值,这一特性与Map相同;

  • object getAttribute(String name):用来获取 ServletContext中的数据,当前在获取之前需要先去存储才行,例如:

    String value = (String)servletContext.getAttribute("xxx");
    

    ,获取名为xxx的域属性;

  • void removeAttribute(String name):用来移除 ServletContext中的域属性,如果参数name指定的域属性不存在,那么本方法什么都不做;

  • Enumeration getAttributeNames():获取所有域属性的名称;

示例

创建两个Servlet分别为存储数据的BServlet和获取数据的CServlet。

BServlet.java文件

/**
 * 演示从servletcontext中保存数据
 */
public class BServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /**
         * 1.获取SevletContext对象
         * 2.调用其setAttribute()方法完成保存数据
         */
        ServletContext application = this.getServletContext();
        application.setAttribute("name", "张三");
    }
}

CServlet.java文件

/**
 * 演示从servletcontext中获取数据出
 */
public class CServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /**
         *1.获取SevletContext对象
         *2.调用其getAttribute()方法完成获取数据
         */
        ServletContext appplication = this.getServletContext();
        String name = (String) appplication.getAttribute("name");
        System.out.println(name);
    }
}

5.4 ServletConfig接口

5.4.1 ServletConfig介绍

概述:ServletConfig是Servlet的配置参数对象,在Servlet的规范中,允许为每一个Servlet都提供一些初始化的配置,所以每一个Servlet都有一个ServletConfig

作用: 当Servlet容器初始化Servlet时,Servlet容器会给S** **,即传入一些配置信息给Servlet。

生命周期: 应用一加载则创建,应用停止则被销毁;

创建时间: 创建完Servlet对象时,接着创建Servletconfig对象。

5.4.2 ServletConfig常用方法

image-20240406161549019

5.4.3 ServletConfig的配置方式

  • 标签种,通过标签来配置。有两个子标

    • :代表初始化参数的key;

    • : 代表初始化参数的value。

    • <servlet>
          <servlet-name>BServlet</servlet-name>
          <servlet-class>cn.web.servlet.BServlet</servlet-class>
          <init-param>
              <param-name>init-name</param-name>
              <param-value>init-value<</param-value>
          </init-param>
      </servlet>
      

5.5 ServletContext配置方式

  • ServletContext不像ServletConfig接口在Servlet标签里配置,而是针对于整个应用的配置,也叫全局的初始化参数;

  • 标签中,通过标签来配置,有两个子标签:

    • : 代表全局初始化参数的key。

    • : 代表全局初始化参数的value。

    • <context-param>
          <param-name>context-Name</param-name>
          <param-value>context-Value</param-value>
      </context-param>
      

5.6 ServletContext常用方法

image-20240406162951137

5.7 ServletContext获取初始化参数

  • Servlet也可以获取初始化参数,但它是局部的参数,也就是说一个Servlet只能获取自己的初始化参数,不能获取别人的,即初始化参数只为一个 Servlet准备!
  • 可以配嚣公共的初始化参数,为所有Servlet而用!这需要使用SerxletContext()方法才能使用!

还可以使用ServletContext来获取在web.xml文件中配置的应用初始化参数!注意,应用初始化参数与Servlet初始化参数不同:

示例,已有一个web.xml文件和一个DServlet项目文件

ServletContext实现步骤

  • 1.定义一个类,继承 HttpServlet。

  • 2.重写doGet和doPost方法。

  • 3.在web.xml文件中配置Servlet和ServletContext。

  • 4.获取ServletContext对象。

  • 5.测试相关方法的使用。

  • 6.部署并启动项目。

  • 7.通过浏览器测试。

  • 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-name>DServlet</servlet-name>
            <servlet-class>cn.web.servlet.DServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>DServlet</servlet-name>
            <url-pattern>/DServlet</url-pattern>
    
        <context-param>
            <param-name>context-Name</param-name>
            <param-value>context-Value</param-value>
        </context-param>
    </web-app>
    
  • DServlet.java

    /**
     * 演示ServletContext获取公告的初始化参数
     */
    public class DServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            /**
             * 1.得到ServletContext
             * 2.调用它getInitParamter(String)得到初始化参数
             */
            ServletContext application = this.getServletContext();
            String value = application.getInitParameter("context-Name");
            System.out.println(value);
        }
    }
    

5.8 获取资源相关方法

5.8.1 获取真实路径String getRealPath(String)

可以使用ServletContext对象来获取web应用下的资源,

例如在 hello应用的根目录下创建a.txt文件,现在想在Servlet中获取这个资源,就可以使用ServletContext来获取。

  • 获取a.txt的真实路径:

    String realPath = ServletContext.getRealPath("/a.txt")
    

    realPath.的值为a.txt文件的绝对路经:F:\tomcat6\webapps\hellola.txt ;

  • 获取b.txt的真实路径:

    String realPath = ServletContext.getRealPath("/WEB-INF/b.txt"); 
    

5.8.2 获取资源流

可以通过ServletContext获取资源流,即把资源以输入流的方式获取:

  • 获取a.txt资源流:

    InputStream in = ServletContext.getResourceAsStream("/a.txt");
    
  • 获取b.txt资源流:

    InputStream in = ServletContext.getResourceAsStream("/WEB-INF/b.txt"); 
    

5.8.3 获取指定目录下所有的资源路径

可以使用ServletContext获取指定目录下所有资源路径,例如获取/WEB-INF下所有资源的路径:

Set<String> paths = this.getServletContext().getResourcePaths("/WEB-INF");

实现三种获取资源的代码

/**
 * 使用ServletContext获取资源路径
 */
public class EServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取真实路径 D:\learning\aliyun_Servlet\Servlet1\out\artifacts\Servlet1_war_exploded\index.jsp
        String realPath = this.getServletContext().getRealPath("/index.jsp");
        System.out.println(realPath);

        // 获取资源流的路径后,在创建出输入流对象!
        InputStream input = this.getServletContext().getResourceAsStream("/index.jsp");

        // 获取指定目录下的所有资源路径
        Set<String> paths = this.getServletContext().getResourcePaths("/WEB-INF");
        System.out.println(paths);
    }
}

案例

案例一:访问量统计

—个项目中所有的资源被访问都要对访问量进行累加。

创建一个 int,类型的变量,用来保存访问量,然后把它保存到ServletContext的域中,这样可以保存所有的Servlet都可以访问到.

  • 最初时,ServletContext中没有保存访问量相关的属性;

  • 当本站第一次被访问时,创建一个变量,设置其值为1,保存到ServletContext中;当以后的访问时,就可以从ServletContext中获取这个变量,然后在其基础之上加1。'

  • 获取 ServletContext对象,查看是否存在名为count的属性,如果存在,说明不是第一次访问,如果不存在,说明是第一次访问;

    • 第一次访问:调用ServletContext的setAttribute()传递一个属性,名为count,值为1;
    • 第2~N次访问:调用ServletContext的get.attribute()方法获取原来的访问量,给访问量加1,再调用ServletContext的setstribute()方法完成数据保存。

程序代码

public class FServlet extends HttpServlet {
    /**
     * 1.获取ServletContext对象
     * 2.从ServletContext对象中获取名为 count的变量
     * 3.如果存在,访问量count+1,然后保存;
     * 4.如果不存在,说明是第一次访问,向ServletContext中保存名为count的属性设值为1
     */
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext app = this.getServletContext();
        Integer count = (Integer) app.getAttribute("count");
        if (count == null){
            app.setAttribute("count",1);
        }else {
            app.setAttribute("count",count+1);
        }

        // 向浏览器中输出,需要响应对象
        PrintWriter pw = resp.getWriter();
        pw.println("<h3>" + count + "<h3>");
    }   
}

6.获取类路径下的资源

类路径对一个 javaWeb 项目而言,就是/WEB-INF/classes 和 /WEB-INF/lib/每个jar包。

两种获取方式:

  • Class
  • ClassLoader

6.1 得到Class

Class c = this.getClass();

6.2 得到ClassLoader

ClassLoader classLoader = this.getClass().getClassLoader();

6.3 实例代码

/**
 * 演示获取 类 路径下的资源
 */
public class GServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /**
         * 1.得到ClassLoader --> 先得到Class,在得到ClassLoader
         * 2.调用其getResourceAsStream(),得到一个输入流InputStream
         * 3.使用第三方commons-io.jar
         */
        //相对当前/classes下面的绝对路径
        //获取a.txt的内容,IOUtils是一个jar工具包
        ClassLoader classLoader = this.getClass().getClassLoader();
        InputStream input = classLoader.getResourceAsStream("cn/web/servlet/a.txt");
        String s = IOUtils. toString(input);//读取输入流内容,转换成字符串返回
        System.out.println("========获取classes文件里servlet下的a.txt==========");
        System. out.println(s);


        //相对.classes下文件所在的目录,a.txt是当前类编译以后所在的包下面
        Class  c = this.getClass();
        InputStream input1 = c.getResourceAsStream("a.txt");
        String s1 = IOUtils. toString(input1);//读取输入流内容,转换成字符串返回
        System.out.println("========获取当前classes文件下的a.txt==========");
        System. out.println(s1);

        //相对于classes,b.txt是相对于编译以后classes文件夹下的位置下面,与上面不同的是添加了“ / ”
        InputStream input2 = c.getResourceAsStream("/a.txt");
        String s2 = IOUtils. toString(input2);//读取输入流内容,转换成字符串返回
        System.out.println("========获取当前classes文件下的/a.txt==========");
        System. out.println(s2);

    }
}

7.BaseServlet

7.1 BaseServlet 的作用

让一个Servlet可以处理多种不同的请求,不同的请求调用Servlet的不同方法。

7.2 实现原理

客户端发送请求时, 必须多给出一个参数, 用来说明要调用的方法!! 这样BaseServlet 通过该参数来调用目标方法。

http://localhost:8080/xxx/AServlet?m=addUser  // m=addUser就是其中的参数用来说明调用addUser方法

请求处理方法的签名必须与 service 相同, 即方法的返回值和参数,以及声明的异常都相同.

image-20240407130407899

7.3 代码演示

7.3.1 客户端必须传递名为method的参数

public class AServlet extends HttpServlet {
    /**
     * 在这里给出多个请求处理方法 请求处理方法:除了名称以外,都与service方法相同!
     */
    public void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        /*
         * 1.获取参数,用来识别用户想请求的方法
         * 2.然后判断是哪一个方法,是哪一个我们就调用哪一个方法
         */
        String methodName = request.getParameter("method");

        if(methodName == null || methodName.trim().isEmpty()){
            throw new RuntimeException("您没有传递method参数!无法确定您想调用的方法。");
        }
        /*
         * 得到方法名称,是否可通过反射来调用方法?
         * 1.得到方法名,通过方法名在得到Method类的对象!
         *     >需要得到Class,然后调用它的方法进行查询!得到Method
         *     >我们要查询的是当前类的方法,所以我们需要得到当前类的Class
         */
        Class c = this.getClass();//得到当前类的class对象
        //查询方法-->参数:1.方法名  2.两个需要用到的对象的类
        Method method = null;
        try {
            method = c.getMethod(methodName, 
                               HttpServletRequest.class,HttpServletResponse.class);//方法名,参数
        } catch (Exception e) {
            throw new RuntimeException("您要调用的方法:"+methodName+",它不存在。"); 
        }
        /*
         * 反射调用
         * 调用method表示的方法--参数
         */
        try {
            method.invoke(this, request,response);
        } catch (Exception e) {
            System.out.println("您调用的方法:"+methodName+",它内部抛出了异常!");
            throw new RuntimeException(e);//继续向外抛,让用户看到抛出的异常
        } 

        /*//这段代码不可重用。每增加一个方法,这里就需要判断一下
        if(method.equals("addUser")){
            addUser(request,response);
        }else if(method.equals("editUser")){
            editUser(request,response);
        }else if(method.equals("deleteUser")){
            deleteUser(request, response);
        }*/
    }

    public void addUser(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {}

    public void editUser(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {}

    public void deleteUser(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {}

    public void selectUser(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {}
}

7.3.2 上边的代码封装到一个抽象类BaseServlet中

BaseServlet.java:--> 一个继承HttpServlet的抽象类(Class不是Servlet),不需要在web.xml中配置。

public abstract class BaseServlet extends HttpServlet {
    public void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        /*
         * 1.获取参数,用来识别用户想请求的方法
         * 2.然后判断是哪一个方法,是哪一个我们就调用哪一个方法
         */
        String methodName = request.getParameter("method");

        if(methodName == null || methodName.trim().isEmpty()){
            throw new RuntimeException("您没有传递method参数!无法确定您想调用的方法。");
        }
        /*
         * 得到方法名称,是否可通过反射来调用方法?
         * 1.得到方法名,通过方法名在得到Method类的对象!
         *     >需要得到Class,然后调用它的方法进行查询!得到Method
         *     >我们要查询的是当前类的方法,所以我们需要得到当前类的Class
         */
        Class c=this.getClass();//得到当前类的class对象
        //查询方法-->参数:1.方法名  2.两个需要用到的对象的类
        Method method = null;
        try {
            method=c.getMethod(methodName, 
                               HttpServletRequest.class,HttpServletResponse.class);//方法名,参数
        } catch (Exception e) {
            throw new RuntimeException("您要调用的方法:"+methodName+",它不存在。"); 
        }
        /*
         * 反射调用
         * 调用method表示的方法--参数
         */
        try {
            method.invoke(this, request,response);
        } catch (Exception e) {
            System.out.println("您调用的方法:"+methodName+",它内部抛出了异常!");
            throw new RuntimeException(e);//继续向外抛,让用户看到抛出的异常
        } 
    }
}

7.3.3 实现一个Servlet中有多个请求处理方法

AServlet继承BaseServlet抽象类

public class AServlet extends BaseServlet{
    
    public void addUser(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {}

    public void editUser(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {}

    public void deleteUser(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {}

    public void selectUser(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {}
}

7.3.4 通过BaseServlet实现Servlet完成转发或从重定向

修改BaseServlet抽象类文件

public abstract class BaseServlet extends HttpServlet {
    public void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        String methodName = request.getParameter("method");

        if(methodName == null || methodName.trim().isEmpty()){
            throw new RuntimeException("您没有传递method参数!无法确定您想调用的方法。");
        }
    	 //得到当前类的class对象
        Class c=this.getClass(); 
        //查询方法-->参数:1.方法名  2.两个需要用到的对象的类
        Method method = null;
        try {
            method=c.getMethod(methodName,                                HttpServletRequest.class,HttpServletResponse.class);//方法名,参数
        } catch (Exception e) {
            throw new RuntimeException("您要调用的方法:"+methodName+",它不存在。"); 
        }
        /*
         * 反射调用
         * 调用method表示的方法--参数
         */
        try {
            String result = (String)method.invoke(this, request,response);
            /*
             * 获取请求处理方法执行后返回的字符串,它表示转发或者重定向的路径!
             * 帮它完成转发或者重定向!
             */
            //如果用户返回的字符串为null,或为"",那么我们什么也不做!
            if(result == null || result.trim().isEmpty()){
                return;//方法结束
            }
            /*
             * 查看返回字符串中是否包含冒号,如果没有,表示转发
             * 如果有,使用冒号分割字符串,得到前缀和后缀!
             * 其中前缀如果是f,表示转发,如果是r表示重定向,后缀就是要转发或重定向的路径
             */
            if(result.contains(":")){
                //使用冒号分割字符串,得到前缀和后缀
                int index=result.indexOf(":");//获取冒号的位置
                String s = result.substring(0, index);//截取前缀,表示操作
                String path = result.substring(index+1);//截取后缀,表示路径
                if(s.equalsIgnoreCase("r")){//前缀r,重定向
                    response.sendRedirect(request.getContextPath()+path);
                }else if(s.equalsIgnoreCase("f")){//前缀f,转发
                    request.getRequestDispatcher(path).forward(request, response);
                }else{
                    throw new RuntimeException("你指定的操作:"+s+",当前版本不支持!");
                }
            }else{//没有冒号,默认为转发!
                request.getRequestDispatcher(result).forward(request, response);
            }
        } catch (Exception e) {
            System.out.println("您调用的方法:"+methodName+",它内部抛出了异常!");
            throw new RuntimeException(e);//继续向外抛,让用户看到抛出的异常
        } 
    }
}

只需自己写的Servlet(在这是BServlet)继承BaseServlet就行(则子类就有多个处理方法,以及转发和重定向的功能了)。

public class BServlet extends BaseServlet {

    public String fun1(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("fun1()...");
        return "/index.jsp";
    }

    public String fun2(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("fun2()...");
        return "r:/index.jsp";
    }
    
    public String fun3(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("fun3()...");
        return null;
    }
}

本文作者:粤先生

本文链接:https://www.cnblogs.com/magicYue/p/18173456

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   粤先生  阅读(23)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起