🌸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分钟),值只能为整数,为零或负数时会话永不过期
@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 = "中青年")
})
优: 直接写在 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");