GenericServlet类

继承GenericServlet类

在通过实现Servlet接口来定义一个Servlet类时存在一个很不方便的问题:有太多不需要的方法必须要实现。通常我们只在service()方法中完成业务逻辑,但由于Servlet 接口中还存在另外四个方法,所以也要必须实现。
为了解决这个问题JavaEE的API中提供了一个javax.servet.GenericServlet类,开发者在定义一个servlet时继承该GenericServlet类,此时只需要重写service方法即可。

package com.monkey1024.servlet;

import java.io.IOException;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * 继承GenericServlet
 *
 */
public class SimpleServlet extends GenericServlet {

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("SimpleServlet继承自GenericServlet");
    }

}

适配器模式

什么是适配器模式:
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
可以将这里的适配器看做是一个万能充电接口,该充电接口一端可以连接安卓手机的充电器,另一端连接苹果手机,这样就实现了使用安卓手机的充电器给苹果手机充电了。

GenericServlet类源码分析

GenericServlet类中就使用了适配器模式。如上面编写的SimpleServlet,使用了GenericServlet后,可以将SimpleServlet类和Servlet接口适配在一起,只重写service方法就能够创建一个servlet类了。

通过查看GenericServlet的源码可以看到,该类是一个抽象类,实现了servlet接口和ServletConfig接口并重写了除了service方法以外的全部方法,这样子类在继承GenericServlet类时,只需重写service方法。如果想要使用destroy方法时,直接重写GenericServlet类中的destroy方法就行,而在GenericServlet类中的destroy方法本身就是一个空实现,里面没有代码。

GenericServlet类中的destroy方法:

/**
 * Called by the servlet container to indicate to a servlet that the servlet
 * is being taken out of service. See {@link Servlet#destroy}.
 */
@Override
public void destroy() {
    // NOOP by default
}

GenericServlet类中的init方法:

/**
 * Called by the servlet container to indicate to a servlet that the servlet
 * is being placed into service. See {@link Servlet#init}.
 * <p>
 * This implementation stores the {@link ServletConfig} object it receives
 * from the servlet container for later use. When overriding this form of
 * the method, call <code>super.init(config)</code>.
 *
 * @param config
 *            the <code>ServletConfig</code> object that contains
 *            configuration information for this servlet
 * @exception ServletException
 *                if an exception occurs that interrupts the servlet's
 *                normal operation
 * @see UnavailableException
 */
@Override
public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}

/**
 * A convenience method which can be overridden so that there's no need to
 * call <code>super.init(config)</code>.
 * <p>
 * Instead of overriding {@link #init(ServletConfig)}, simply override this
 * method and it will be called by
 * <code>GenericServlet.init(ServletConfig config)</code>. The
 * <code>ServletConfig</code> object can still be retrieved via
 * {@link #getServletConfig}.
 *
 * @exception ServletException
 *                if an exception occurs that interrupts the servlet's
 *                normal operation
 */
public void init() throws ServletException {
    // NOOP by default
}

上面的源码中有两个init方法,其中无参的init方法是GenericServlet中自己定义的,那为什么要这么做呢?
如果在其子类中想要调用init方法的话,需要重写其init方法,但这个重写的 init(ServletConfig) 方法必须要调用父类的 init(ServletConfig) 方法,即在第一句必须写上super.init(config);否则将无法获取到 ServletConfig 对象。若 ServletConfig 对象未获取,程序在运行时就有可能会出现空指针异常,但这个 init(ServletConfig)语句,有时候会忘写,例如下面程序就会出现问题:

package com.monkey1024.servlet;

import java.io.IOException;

import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * 继承GenericServlet
 *
 */
public class SimpleServlet extends GenericServlet {

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("SimpleServlet继承自GenericServlet");
        //这里会报错,因为没有获取父类中的config对象
        System.out.println(super.getServletName());

    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //忘记写这句代码
        //super.init(config);
        System.out.println("init方法");
    }
}

所以为了避免这个问题的出现,在GenericServlet类中自己定义了一个没有参数的init方法,该方法就是让子类去重写的,子类重写该方法时,无需编写super.init(config);为了保证该无参方法在初始化时执行,在init(ServletConfig config)方法中对其进行了调用。