0%

Servlet详解

Servlet详解

在java web中不管是使用J2EE原生的servlet/jsp还是使用springmvc/springboot,在web服务器看来只是对外暴露出来的Servlet,而这个Servlet是javax.servlet.Servlet接口,该接口定义了Servlet引擎与Servlet程序之间通信的协议约定。

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface Servlet {
// 负责初始化Servlet对象,容器创建好Servlet对象后调用
void init(ServletConfig config) throws ServletException;
// 获取该Servlet的初始化参数信息
ServletConfig getServletConfig();
// 负责响应客户端的请求,当容器接收到客户端要求访问特定Servlet对象的请求时,会调用该Servlet对象的service()方法
void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
// 返回Servlet信息,包含创建者、版本、版权等信息
String getServletInfo();
// Servlet结束生命周期时调用,释放Servlet对象占用的资源
void destroy();
}

而为了简化开发,jdk中提供了一个实现Servlet接口的简单的Servlet类,javax.servlet.GenericServlet,该类实现了Servlet的基本功能

jdk针对HTTP协议专门提供了一个Servlet类,javax.servlet.http.HttpServlet,该类继承于GenericServlet类,在其基础上针对HTTP的特点进行扩充,一般编写程序时继承HttpServlet即可

Servlet继承关系

Servlet中涉及的主要对象

  • 请求对象 ServletRequest、HttpServletRequest
  • 响应对象 ServletResponse、HttpServletResponse
  • Servlet配置对象 ServletConfig
  • Servlet上下文对象 ServletConfig

Servlet注册与运行

Servlet编写好之后需要在web.xml中进行注册和映射才能被Servlet容器加载从而被外界访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 注册Servlet -->    
<servlet>
<servlet-name>HW</servlet-name>
<servlet-class>com.zhanghe.study.servlet.HelloWorldServlet</servlet-class>
<!-- 配置servlet初始化init时中ServletConfig参数-->
<init-param>
<param-name>name</param-name>
<param-value>john</param-value>
</init-param>
<!-- servlet加载时机 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 映射Servlet -->
<servlet-mapping>
<servlet-name>HW</servlet-name>
<!-- 开头的/表示web用用程序的根目录 -->
<url-pattern>/HelloWorld</url-pattern>
</servlet-mapping>

tomcat中的web.xml包含有一个缺省的Servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

ServletConfig

对于每个Servlet可能在启动时都需要一些初始化参数,而所有的Servlet是交由Servlet引擎去实例化的,那么也就是需要将每个Servlet的初始化参数也都配置到web.xml中,Servlet引擎将Servlet容器对象和Servlet的配置信息封装到ServletConfig中,并在Servlet初始化时将ServletConfig传递给该Servlet。javax.servlet.ServletConfig接口的作用就是用来定义ServletConfig对象所需要对外提供的方法

1
2
3
4
5
6
7
8
9
public interface ServletConfig {
String getServletName();

ServletContext getServletContext();

String getInitParameter(String var1);

Enumeration<String> getInitParameterNames();
}

Servlet引擎装载并创建一个Servlet对象后,会调用该对象的init(ServletConfig config)方法,Servlet中的ServletConfig getServletConfig()方法会返回init传入的ServletConfig对象

1
2
3
4
5
6
7
8
<servlet>
<servlet-name>HW</servlet-name>
<servlet-class>com.zhanghe.study.servlet.HelloWorldServlet</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>zhanghe</param-value>
</init-param>
</servlet>
1
2
3
4
// 获取指定的属性
config.getInitParameter("name")
// 获取所有的属性
config.getInitParameters()

ServletContext

每个Web应用程序在启动时都会创建一个ServletContext对象,每个Web应用程序都有一个唯一的ServletContext对象,javax.servlet.ServletContext接口定义了ServletContext需要对外提供的方法,Servlet通过这些方法来和ServletContext容器进行通信

  • 该对象代表当前WEB应用,可以获取到web应用的信息,一个Web应用只有一个ServletContext对象
  • 可以使用ServletConfig的getServletContext()获取到ServletContext
  • 可以获取web应用的初始化参数,这是全局的方法,在web.xml中配置
  • 获取web应用某个文件的绝对路径(在服务器上的路径,不是部署前的方法) getRealPath
  • 获取当前应用的名称 getContextPath
  • 获取当前web应用的某一个文件对应的输入流 getResourceAsStream() path是相对于当前web应用的根目录
1
2
3
4
<context-param>
<param-name>email</param-name>
<param-value>master@163.com</param-value>
</context-param>

servlet生命周期

生命周期相关方法,servlet生命周期中的方法全是由servlet容器来调用的

  • 构造器
  • init方法
  • service方法
  • destory方法

init方法–初始化

init方法在第一次创建servlet时被调用,在后续每次请求时都不会被调用。

当用户调用servlet的时候,该servlet的一个实例就会被创建,并且为每一个用户产生一个新的线程,init()用于进行一些初始化数据的加载和处理,这些数据会被用于servlet的整个生命周期

1
void init(ServletConfig config) throws ServletException;

该方法是由servlet容器调用的

什么时候触发初始化

有两种情况会进行Servlet的初始化

  • Servlet被客户端首次请求访问时触发初始化方法

  • 如果配置了load-on-startup元素,则在Servlet容器启动该Servlet所属Web应用时就会初始化该Servlet

    1
    2
    3
    4
    5
    6
    7
    <servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>
    org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <load-on-startup>1</load-on-startup>
    </servlet>
重写init方法

GenericServlet实现了Servlet和ServletConfig,是一个抽象类,并对init(ServletConfig var1)方法进行了一层封装,有一个ServletConfig成员变量,在init()方法中进行了初始化,使得可以直接在GenericServlet中直接使用ServletConfig方法

而我们平时写Servlet大多是继承于HttpServlet类的,在对init方法进行重写时,重写不带参的init()方法即可

1
2
3
4
5
6
7
8
9
//GenericServlet类

public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}

public void init() throws ServletException {
}

该方法由GenericServlet的调用,如果需要使用到ServletConfig则调用getServletConfig()方法来获取

service方法

service方法是实际处理请求的方法,servlet容器调用service方法来处理请求,并将响应写回到客户端,每次服务器接收到一个新的servlet请求时,服务器会产生一个新的线程来调用服务

1
void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
处理请求逻辑

HttpServlet继承了GenericServlet,重写了service方法,将ServletRequest和ServletResponse转换为HttpServletRequest和HttpServletResponse,并根据不同的请求方式进行分发,doGet/doPost/doHead等

servlet可以在任何协议下访问 ,写的Servlet必须实现Servlet接口,在http协议下可以使用HttpServlet ,用来给Web访问的

在HttpServlet类中对于service()方法进行了处理,根据请求方式的不同,将请求分发到了不同的方法,而我们一般情况下写Servlet也是继承自HttpServlet的,所以在写请求处理逻辑时,只需要重写doGet()和doPost()方法即可

1
2
3
4
5
6
7
8
9
10
11
// HttpServlet类

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_get_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String msg = lStrings.getString("http.method_post_not_supported");
this.sendMethodNotAllowed(req, resp, msg);
}
GET方法

GET方法时浏览器向web服务器传递信息的默认方法,会在地址栏上产生很长的字符串,且GET方法有大小限制,请求字符串中最多只能有1024个字符

POST方法

POST方法不将请求信息放到地址中

destroy方法

destory()方法只在servlet生命周期结束时被调用一次。可以在destory()方法中进行一些资源的清理,如关闭数据库连接、停止后台线程等