🌸Author Echo Pan
本章包含使用MyEclipse创建一个Servlet文件,配置好有关的参数,调试运行以及一些常见问题,包括我自己遇到的问题的解决办法。
有两种方式设置初始化配置信息,一种是通过/WEB-INF下的web.xml文件,另一种是使用@WebServlet注解(annotation)。
web.xml<?xml version="1.0" encoding="UTF-8"?>
说明⬇️
version:xml 版本为1.0
encoding:数据传输编码为UTF-8
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版本)。
web.xml节点加载顺序ServletContext–><context-param>–><listener>–><filter>–><servlet>
注: 书写顺序不影响加载顺序,同类节点按mapping顺序调用
⬇️下方 可选且仅能出现一次的元素用?标记,可出现零次或多次的元素用*标记。
<display-name>用于定义web应用的名称。
<display-name>TestProject</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
子元素⬇️
<welcome-file> 用来指定首页文件,按顺序匹配<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。
子元素⬇️
<display-name> 外显名称,某些GUI工具(如web浏览器)、控制台可能会读取并显示<servlet-name> 内部名称,指定Servlet名称<description> 描述信息<icon> 图标,可在支持的控制台、GUI工具显示该Servlet,和网页的favicon无关
<small-icon> 16 x 16<large-icon> 32 x 32<servlet-class> 二选一,指定Servlet完整类名<jsp-file> 二选一,指定应用内JSP完整路径,以/开头<load-on-startup> 启动Web应用时决定Servlet的载入顺序,值为空或负数时当客户首次访问时载入<init-param> 初始参数,可被该类的ServletConfig调用
<param-name> 参数名<param-value> 参数值2️⃣ <servlet-mapping> Servlet映射,用来将URL地址映射到某个Servlet上。
子元素⬇️
<servlet-name> 指定Servlet名称<url-pattern> 指定Servlet对应的URL<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来执行的,一般不自己配置。
匹配顺序优先级⬇️
精确匹配 > 路径匹配 > 扩展名匹配 > 缺省匹配
注意事项⬇️
*在路径的中间,则不是通配符而是精确匹配/和/*的区别/是缺省匹配,只有一个缺省实例,优先级最低,不会覆盖其他的<url-pattern>/*是路径匹配,能匹配所有路径,包括扩展名,容易引起404错误,一般用于监听器(listener)<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参数。
<session-config>
<session-timeout>120</session-timeout>
</session-config>
说明⬇️
<session-timeout>:session失效时间120分钟(默认30分钟),值只能为整数,为零或负数时会话永不过期
@WebServletServlet 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 = "中青年")
})
优: 直接写在 Servlet 类中,代码少,配置简单,与其他的 Servlet 类互不干扰,适合多人开发
缺: 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调用
response和request的编码方式默认为ISO8859-1,而不同的浏览器的编码方式也有所不同,一些国内浏览器默认为GB2312。编码方式不同,在解码时就会造成乱码,所以我们要设置两个地方的编码,使得编码一致。
首先设置response和request的编码方式(在调用前设置):
request.setCharacterEncoding("UTF-8");//大写!
response.setCharacterEncoding("UTF-8");
然后设置网页的编码格式:
response.setContextType("text/html;charset=utf-8");