Servlet——分析和模拟实现Servlet继承关系

目录


Servlet继承关系图

跳转到目录
在这里插入图片描述

分析GenericServlet的底层实现

跳转到目录

  • Aservlet.java
public class AServlet implements Servlet {

    private ServletConfig servletConfig;

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        this.servletConfig = servletConfig;
    }

    @Override
    // 把ServletConfig对象暴露给子类访问
    public ServletConfig getServletConfig() {
        return this.servletConfig;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        String encoding = servletConfig.getInitParameter("encoding");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {
    }
}
  • BServlet.java
public class BServlet implements Servlet {

    private ServletConfig servletConfig;

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        this.servletConfig = servletConfig;
    }

    @Override
    // 把ServletConfig对象暴露给子类访问
    public ServletConfig getServletConfig() {
        return this.servletConfig;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        String encoding = servletConfig.getInitParameter("encoding");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

问题:
上面的每自定义的Servlet类都得编写一些重复的代码,如此一来,我们需要重构代码,消除重复.

解决方案:
定义MyGenericServlet,用来定义一些自定义Servlet中的重复代码.

  • MyGenericServlet.java
abstract  public class MyGenericServlet implements Servlet {
    
    private ServletConfig servletConfig;
    
    @Override
    public void init(ServletConfig servletConfig) throws ServletException { 
    	// 把tomcat传递的ServletConfig赋值给本类的成员servletConfig,就是把它保存起来,方便其他方法使用!
        this.servletConfig = servletConfig;
    }

    @Override
    public ServletConfig getServletConfig() {
        return this.servletConfig;
    }

    @Override
    abstract public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException,IOException;
            
    @Override
    public String getServletInfo() {
        return null;
    }
    
    @Override
    public void destroy() {
    }
}

写完MyGenericServlet类后,让AServlet类和Bservlet类继承它.

  • AServlet.java
public class AServlet extends MyGenericServlet {
	public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException,IOException{
		String encoding = super.getServletConfig().getInitParameter("encoding");
	}
}

在这里插入图片描述


问题:
此时在AServlet中的service方法获取初始化参数:
String encoding = super.getServletConfig().getInitParameter("encoding");
如果想更进一步, 想在AServlet中的service方法中,这样写:
String encoding = super.getInitParameter("encoding");
解决方案:
让MyGenericServlet实现ServletConfig接口,并实现其方法,在实现方法中让父类来完成.

@Override
 public String getInitParameter(String s) {
     return servletConfig.getInitParameter(s);
 }

在这里插入图片描述


问题:
此时,如果AServlet需要做自身的初始化操作,怎么办?
应该在AServlet中覆盖init(config)方法,并在该方法中写初始化代码
在这里插入图片描述
如果忘记调用父类的init方法, 就会报空指针异常,针对于没写父类init方法,看下面的解决方案.

解决方案:
为了解决开发在做自身初始化操作的时候, 可能会忘记super.init(config), 需要在MyGenericServlet中,专门提供一个无参数的初始化方法: init, 专门暴露给子类用于子类做自身的初始化操作
在这里插入图片描述
上图的执行步骤:

  • 首先调用Servlet1的构造器, 创建Servlet的对象 obj.
  • 调用带有参数的Init方法完成初始化操作. obj.init(config), 此时Servlet1的父类(MyGenericServlet)总去找带有config参数的init方法,并调用.
  • 在MyGenericServlet中的带有参数的init方法: 先执行this.config=config,再调用this.init();
    表面上看在调用MyGenericServlet中无参数的init()方法.但是此时: Servlet1覆盖了init方法,从多态时方法调用上分析,此时 this.init(),实际上调用的是Servlet1的init方法

如此一来, 就确保了子类如何完成自身的初始化操作;


分析HttpServlet的底层实现

跳转到目录
问题:
但是上面的Servlet1只能处理一般的请求和响应,一般B/S开发,都是基于浏览器所以要遵循HTTP协议, 所以我们应该处理HTTP类型的请求和响应,需要重新设计, 处理Http的service方法,要统一处理GET/POST请求. 提供doGet方法和doPost方法
在这里插入图片描述
根据上图这样处理, 每一个Servlet都得编写这么多的代码.

解决方案:
提取出一个专门用于处理HTTP协议的Servlet出来, 创建MyHttpServlet类并继承MyGenericServlet

  • MyHttpServlet.java
public class MyHttpServlet extends MyGenericServlet {
    // 只能处理一般的请求
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
        // 调用处理Http请求的方法
        service(httpServletRequest, httpServletResponse);
    }

    public void service(HttpServletRequest reg, HttpServletResponse resp) throws ServletException, IOException {
        // 获取请求方式 GET/POST
        String method = reg.getMethod();
        if ("GET".equals(method)){
            doGet(reg, resp);
        } else if("POST".equals(method)){
            doPost(reg, resp);
        }
    }

    // 专门用于处理POST请求
    private void doPost(HttpServletRequest reg, HttpServletResponse resp) {
        System.out.println("AServlet.doPost()");
    }

    // 专门用于处理GET请求
    private void doGet(HttpServletRequest reg, HttpServletResponse resp) {
        System.out.println("AServlet.doGet()");
    }
}

抽取MyHttpServlet之后,以后开发,只需要继承于MyHttpServlet即可,再提供处理请求的service/doGet/doPost方法. 这里也体现了模板方法设计模式


以后写的XxxServlet都要继承HttpServlet

在这里插入图片描述

public class TestServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        // 初始化代码
    }

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

        // 不要调用父类的service
//        super.service(req, resp);
    }
}

注意: 在service方法中不要调用父类的service方法
否则会报
在这里插入图片描述

发布了149 篇原创文章 · 获赞 68 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/m0_37989980/article/details/103899012