Notes

JSP | 03 创建Servlet、配置、常见问题

🌸Author Echo Pan


✨Echo有话说

本章包含使用MyEclipse创建一个Servlet文件,配置好有关的参数,调试运行以及一些常见问题,包括我自己遇到的问题的解决办法。

配置说明

有两种方式设置初始化配置信息,一种是通过/WEB-INF下的web.xml文件,另一种是使用@WebServlet注解(annotation)。

部署描述符:web.xml

<?xml version="1.0" encoding="UTF-8"?>

说明⬇️

versionxml 版本为1.0
encoding:数据传输编码为UTF-8

根节点:<web-app> 标签

Servlet 2.5 /Tomcat 6.0.x 模板

<web-app 
  xmlns="http://java.sun.com/xml/ns/javaee" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  version="2.5">  

</web-app>

Servlet 3.0 /Tomcat 7.0.x 模板

<web-app 
  xmlns="http://java.sun.com/xml/ns/javaee" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
  version="3.0" >  

</web-app>

当一个xml文档包含两种定义不同,但是名称相同的元素时,xml解析器(parser)是无法解析的,因此我们通过在元素名称前添加前缀来避免该问题,比如<h:table><f:table>。但是这不能解决全部的命名冲突,尤其是当一个xml文档拥有众多元素或者多个xml文档互相包含或引用时。

这时,我们可以使用命名空间(namespace),而最好的全局唯一性字符串莫过于URI了。比如:

<h:table xmlns:h="https://html.spec.whatwg.org/multipage/"></h:table>

但是在我们的<web-app>标签中并没有添加前缀,这里实际上使用了默认的命名空间,即xmlns="namespaceURI"。该元素内部的子元素也会默认属于该命名空间下,无需分别添加前缀。

因此,xmlns:xsi实际是定义了一个前缀为xsi的命名空间。我们可以发现许多xml文档都用到了xsi(xml schema instance)这个前缀,这是因为它是行业默认的用于XSD(XML Schema Definition) 文件的命名空间。

xsi:schemaLocation="key value"

schemaLocation是命名空间xsi下的一个元素,其值实际上是一个由空格分开的键值对。通过这个元素指定命名空间xmlns与对应的XSD文件相关联。key 代表命名空间,需要和xmlns相匹配。value 指代XSD location URI,指示了key 所代表的的命名空间所对应的XSD文件的位置,XML解析器可以根据该信息获取到XSD文件,然后对所有属于key命名空间的元素结构进行校验。因此value一般是可以访问的。

version属性决定了XML解析器的解析规则(根据的Servlet版本)。

.xml头部详解xsi:schemaLocation详解


web.xml节点加载顺序

ServletContext–><context-param>–><listener>–><filter>–><servlet>

注: 书写顺序不影响加载顺序,同类节点按mapping顺序调用

顺序问题


⬇️下方 可选且仅能出现一次的元素用?标记,可出现零次或多次的元素用*标记。

<display-name?> 标签

<display-name>用于定义web应用的名称。

<display-name>TestProject</display-name>

<welcome-file-list?> 标签

<welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

子元素⬇️

<servlet> 和 <servlet-mapping> 标签

<servlet>
  <description>描述信息</description>
  <display-name>Servlet外显短名称</display-name>
  <servlet-name>MyServlet</servlet-name>
  <servlet-class>Echo.Defined.Test.MyServlet</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>MyServlet</servlet-name>
  <url-pattern>/servlet/MyServlet</url-pattern>
</servlet-mapping>

1️⃣ <servlet>标签用来声明一个Servlet

子元素⬇️

2️⃣ <servlet-mapping> Servlet映射,用来将URL地址映射到某个Servlet上。

子元素⬇️

<url-pattern> 匹配规则

<url-pattern>有其特定的匹配规则,从Servlet 2.5开始,一个Servlet支持匹配多个<url-pattern>规则。一个Servlet也可以设置多个<servlet-mapping>

规则: 容器去除请求的url中的上下文路径,只保留剩下的部分进行映射匹配。当请求地址匹配到一个Servlet后就不会再继续匹配了。

举例: 请求的地址http://localhost:8080/appDemo/index.html,容器会去除http://localhost:8080/appDemo,拿剩下的/index.html做映射匹配。

1️⃣ 精确匹配
<url-pattern>url完全匹配 。

<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/user/users.html</url-pattern>
    <url-pattern>/index.html</url-pattern>
    <url-pattern>/user/addUser.action</url-pattern>
</servlet-mapping>

2️⃣ 路径匹配
/开头,并以/*结尾。

<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/user/*</url-pattern>
    <url-pattern>/*</url-pattern>
    <url-pattern>/user/webapps/*</url-pattern>
</servlet-mapping>

3️⃣ 扩展名匹配
*.开头。

<servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.action</url-pattern>
</servlet-mapping>

4️⃣ 缺省匹配
<url-pattern>/</url-pattern>即缺省匹配。

其他Servlet不处理的请求都交给缺省Servlet来处理。比如404页面、500页面等都是由缺省Servlet来执行的,一般不自己配置。

匹配顺序优先级⬇️

精确匹配 > 路径匹配 > 扩展名匹配 > 缺省匹配

注意事项⬇️

  1. 三种匹配方法只能择其一,不能组合
  2. 如果*在路径的中间,则不是通配符而是精确匹配
  3. 关于//*的区别
    /是缺省匹配,只有一个缺省实例,优先级最低,不会覆盖其他的<url-pattern>
    /*是路径匹配,能匹配所有路径,包括扩展名,容易引起404错误,一般用于监听器(listener)

Servlet匹配规则Servlet映射匹配问题

<context-param*> 标签

<context-param>用于定义上下文初始化参数。

<context-param>
  <param-name>name</param-name>
  <param-value>Echo</param-value>
  <description>用户名参数</description>
</context-param>

说明⬇️

<param-name>:参数名为name
<param-value>:参数值为Echo
<description>:可选,描述参数

<session-config?> 标签

<session-config>用于设置容器的session参数。

<session-config> 
   <session-timeout>120</session-timeout> 
</session-config> 

说明⬇️

<session-timeout>session失效时间120分钟(默认30分钟),值只能为整数,为零或负数时会话永不过期


web.xml详解

Servlet 注解:@WebServlet

Servlet 3.0引入了注解的功能(javax.servlet.annotation.WebServlet),通过注解可以在类当中设置初始化参数,而无需再在部署描述符中写代码,两者不可以重复设置。使用注解要求Tomcat 7或更高版本。

常用属性:

属性名 类型 对应标签 描述 是否必需
name String <servlet-name> 指定 Servlet 的 name 属性,没有显示指定时默认为完全限定名(包名.类名)
value String[] <url-pattern> 等价于 urlPatterns 属性,同时指定时通常忽略value
urlPatterns String[] <url-pattern> 指定一组 Servlet 的 URL 匹配模式
loadOnStartUp int <load-on-startup> 指定 Servlet 加载顺序
initParams WebInirParam[] <init-param> 指定一组初始化参数
description String <description> 描述信息
displayName String <display-name> Servlet 显示名

开启/关闭注解

<web-app>标签中有一个属性metadata-complete,用来指示当前的web.xml文件是否是完全的。该属性的值默认为false,此时注解是可用的;当metadata-complete的值为true时,表示配置完整,则会忽略注解内容。

简单应用

//1.直接在括号内写出Servlet的相对路径
@WebServlet("/MyServlet");
//2.上面代码的完整写法
@WebServlet(urlPatterns = "/MyServlet");
//3.可以用value代替urlPatterns
@WebServlet(value = "/MyServlet")

具体应用

@WebServlet(
 description = "描述信息", 
 urlPatterns = { "/MyServlet" , "/*"}, 
 initParams = {
  @WebInitParam(name = "姓名", value = "Echo", description = "在校学生"), 
  @WebInitParam(name = "年龄", value = "21", description = "中青年") 
  })

优缺点分析

@WebServlet 注解

优: 直接写在 Servlet 类中,代码少,配置简单,与其他的 Servlet 类互不干扰,适合多人开发

缺: Servlet 较多时不便于查找和修改

web.xml 配置文件

优: 集中管理配置,便于查找和修改

缺: 代码繁琐,可读性不强,不易理解


Servlet注解Servlet-Annotations

Servlet 代码模板

导入的包

一般需要导入下面这些包:

import java.io.IOException;
import java.io.PrintWriter;//将对象的格式化表示打印到文本输出流
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;//引入Web注解功能
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

类方法

常用方法:

protected void doGet(HttpServletRequest request, HttpServletResponse response) 
                                        throws ServletException, IOException {
    PrintWriter out = response.getWriter();//获取response的输出流
    out.println("<HTML>");//通过输出流输出响应内容
    out.println("  <HEAD><TITLE>A Servlet</TITLE></HEAD>");
    out.println("  <BODY>");
    out.println("    This is a Servlet.");
    out.println("  </BODY>");
    out.println("</HTML>");
    out.flush();//刷新缓存
    out.close();//关闭输出流
}

protected void doPost(HttpServletRequest request, HttpServletResponse response) 
                                        throws ServletException, IOException {
    doGet(request, response);//当使用POST方式提交时,通过doPost()调用doGet()
}

获取初始化参数:

<!-- web.xml 代码片段 -->
  <context-param>
    <param-name>name</param-name>
    <param-value>echo</param-value>
  </context-param>

  <context-param>
    <param-name>age</param-name>
    <param-value>21</param-value>
  </context-param>

  <Servlet>
  <servlet-name>MyServlet</servlet-name>
  <servlet-class>servlet.Myservlet</servlet-class>
  <init-param>
  <param-name>date</param-name>
  <param-value>Sunday</param-value>
  </init-param>
  </servlet>

  <Servlet-mapping>
  <servlet-name>MyServlet</servlet-name>
  <url-pattern>/MyServlet</url-pattern>
  </servlet-mapping>  
//Servlet 代码段

//1.使用默认的ServletContext对象
protected void doGet(HttpServletRequest request, HttpServletResponse response) 
                                        throws ServletException, IOException {
    ServletContext sct1 = this.getServletContext();//调用父类方法获取对象
    ServletConfig scg = this.getServletconfig();
    ServletContext sct2 = scg.getServletContext();
    
    String name = sct1.getInitParameter("name");//name = echo
    String date = scg.getInitParameter("date");//date = Sunday
    String age = sct2.getInitParameter("age");//age = 21
}

//2.覆写init()方法,使用自定义config对象获取ServletContext对象
private ServletConfig config;//创建一个config对象
public void init(ServletConfig config) throws ServletException {
    this.config = config;//初始化config对象
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) 
                                        throws ServletException, IOException {
    ServletContext application = config.getServletContext();//获取ServletContext对象
    String name = application.getInitParameter("name");//name = echo
}

注: 全局参数可以被Config或Context调用,但是Servlet内部参数只能被该Servlet的Config调用

常见问题

中文乱码问题

responserequest的编码方式默认为ISO8859-1,而不同的浏览器的编码方式也有所不同,一些国内浏览器默认为GB2312。编码方式不同,在解码时就会造成乱码,所以我们要设置两个地方的编码,使得编码一致。

首先设置responserequest的编码方式(在调用前设置):

request.setCharacterEncoding("UTF-8");//大写!
response.setCharacterEncoding("UTF-8");

然后设置网页的编码格式:

response.setContextType("text/html;charset=utf-8");

上一章:02.Servlet生命周期

下一章:04.会话跟踪&过滤器

返回目录