目录
一.自启动Servlet
1.自启动Servlet特点
2.通过自启动Servlet实现配置信息的读取
二.Servlet线程安全问题
三.Servlet的url-pattern配置
1.URL的匹配规则
(1)精确匹配
(2)扩展名匹配
(3)路径匹配
(4)任意匹配
(5)匹配所有
2.优先顺序
3.考考你
四.Servlet的多URL映射方式
1.方式一
2.方式二
一.自启动Servlet
1.自启动Servlet特点
自动启动
Servlet
表示在
Tomcat
启动时就会实例化这个
Servlet
,他的实例化过程
不依赖于请求
,而是
依赖容器的启动
。
可以通过在
web.xml中的
标签中通过1
配置自启动Servlet
。
AutoStartServlet.java:
package com.first.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class AutoStartServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter pw=resp.getWriter();
pw.println("AutoServlet......");
pw.flush();
pw.close();
}
@Override
public void init() throws ServletException {
//看servlet什么时候初始化,请求时还是启动时
System.out.println("AutoStartServlet Init......");
}
}
web.xml配置:
AutoStartServlet
com.first.servlet.AutoStartServlet
1
AutoStartServlet
/autoStart.do
输出:
启动Tomcat时控制台就输出了
AutoStartServlet Init......
请求访问servlet:
2.通过自启动Servlet实现配置信息的读取
需求:修改文件下载案例,通过自启动
Servlet
读取配置信息。
实际应用原因:
之前写的文件下载案例是文件路径是硬编码,而实际工程现场,部署的不是源代码,而是编译好的字节码文件,字节码文件已经不能再根据实际工程环境再去改变文件路径了,但web.xml相当于一个文本文件可以随意修改,所以本节讲解将配置信息放在web.xml文件中,启动Tomcat时,自动读取配置信息。
AutoStartServlet.java配置好且FileDownServlet.java修改之后,直接运行FileDownServlet.java即可,因为由1.可知AutoStartServlet.java在服务器启动之时就自动启动了,此时web.xml的路径配置信息(在AutoStartServlet.java的servlet配置中)就配置好了。
输出:
此时访问FileDownServlet.java,发现文件可下载
二.Servlet线程安全问题
在
Servlet
中使用的是多线程方式来执行
service()
方法处理请求,所以我们在使用
Servlet
时需要考虑到线程安全问题,在多线程中对于
对象中的成员变量是最不安全的,所以不要在
Servlet
中通过成员变
量的方式来存放数据,如果一定要使用成员变量存储数据,在对数
据进行操作时需要使用线程同步的方式来解决线程安全问题,避免
出现数据张冠李戴现象。
比如执行了一个servlet,一个客户端请求之后,在一个线程中执行了一个 service()方法,又有一个客户端请求之后,又新建了一个线程,这个线程又调用了一个service()方法,但是这两个线程共享这个servlet的成员变量。所以尽量不要使用成员变量,能用局部变量最好用局部变量,非得用成员变量的话最好只读不写。
ThreadSafeServlet.java:
package com.first.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class ThreadSafeServlet extends HttpServlet {
//设置成员变量(多线程下不安全,即多个线程都可访问)
private PrintWriter pw;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取用户提交数据
String value=req.getParameter("name");
pw=resp.getWriter();
try {
//沉睡5s
Thread.sleep(5000);
pw.println(value);
pw.flush();
pw.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
配置web.xml:
ThreadSafeServlet
com.first.servlet.ThreadSafeServlet
ThreadSafeServlet
/safe.do
输出:
访问http://localhost:8888/ajaxDemo/safe.do?name=kd
Chrome:
Firefox:
先用Chrome访问ThreadSafeServlet.java,此时尽快再用Firefox浏览器访问此servlet,尽快的访问的原因是程序就出沉睡5s。此时发现Chrome没有输出,而在Firefox有输出,这是因为pw是成员变量(类变量),两个客户端进程皆可访问它,所以Firefox的浏览器线程就又创建了pw变量,即把Chrome创建的那个覆盖了,所以Chrome没有输出,而访问Firefox5s后Firefox输出了获得了name参数的值。
如果想要改进上面的情况,可作如下修改:
ThreadSafeServlet.java:
package com.first.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class ThreadSafeServlet extends HttpServlet {
//设置成员变量(多线程下不安全,即多个线程都可访问)
private PrintWriter pw;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取用户提交数据
String value=req.getParameter("name");
synchronized (this){
pw=resp.getWriter();
try {
//沉睡5s
Thread.sleep(5000);
pw.println(value);
pw.flush();
pw.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
再做上面相同的访问,结果如下,两个浏览器会依次输出name的值!
三.Servlet的url-pattern配置
1.URL的匹配规则
下面的示例都假设以Servlet为项目名
(1)精确匹配
精确匹配是指中配置的值必须与
url
完全精确匹配。
demoServlet
/demo.do
http://localhost:8888/Servlet/demo.do
匹配
http://localhost:8888/Servlet/suibian/demo.do
不匹配
可见我们之前配置的url-pattern都是精确匹配
(2)扩展名匹配
在允许使用统配符作为匹配规则,
“*”
表示匹配任意字符。在扩展名匹配中只要扩展名相同都会被匹配和路径无关。注意,在使用扩展
名匹配时在中不能使用
“/”
,否则容器启动就会抛出异常。
demoServlet
*.do
http://localhost:8888/demo/abc.do
匹配
http://localhost:8888/demo/suibian/haha.do
匹配(虽然又多加了层路径,但是以.do结尾就行)
http://localhost:8888/demo/abc
不匹配
(3)路径匹配
根据请求路径进行匹配,在请求中只要包含该路径都匹配。
“*”
表示任意路径以及子路径。
demoServlet
/suibian/*
http://localhost:8888/demo/suibian/haha.do
匹配
http://localhost:8888/demo/suibian/hehe/haha.do
匹配
http://localhost:8888/demo/hehe/heihei.do
不匹配
(4)任意匹配
匹配
“/”
。匹配所有但不包含
JSP
页面(即不会拦截jsp页面,但是其他的页面都会被拦截并转到此servlet)。
/
http://localhost:8888/demo/suibian.do
匹配
http://localhost:8888/demo/addUser.html
匹配
http://localhost:8888/demo/css/view.css
匹配
http://localhost:8888/demo/addUser.jsp
不匹配
http://localhost:8888/demo/user/addUser.jsp
不匹配
(5)匹配所有
/*
http://localhost:8888/demo/suibian.do
匹配
http://localhost:8888/demo/addUser.html
匹配
http://localhost:8888/demo/suibian/suibian.do
匹配
2.优先顺序
当一个
url
与多个
Servlet
的匹配规则可以匹配时,则按照
“
精确路径>
最长路径
>
扩展名
”
这样的优先级匹配到对应的
Servlet
。
3.考考你
Servlet1
映射到
/abc/*
Servlet2
映射到
/*
Servlet3
映射到
/abc
Servlet4
映射到
*.do (扩展名匹配,优先级最低)
当请求
URL
为
“/abc/a.html”
,
“/abc/* ”
和
“/* ”
都匹配,
Servlet
引擎将调用
Servlet1
。
当请求
URL
为
“/abc”
时,
“/abc/* ”
和
“/abc”
都匹配,
Servlet
引擎将调用
Servlet3
。
当请求
URL
为
“/abc/a.do”
时,
“/abc/* ”
和
“ *.do”
都匹配,
Servlet
引擎将调用
Servlet1
。
当请求
URL
为
“/a.do”
时,
“/* ”
和
“*.do”
都匹配,
Servlet
引擎将调用Servlet2
。
当请求
URL
为
“/xxx/yyy/a.do”
时,
“/* ”
和
“*.do”
都匹配,
Servlet
引擎将调用
Servlet2
。
四.Servlet的多URL映射方式
在
web.xml
文件中支持将多个
URL
映射到一个
Servlet
中,但是相同的
URL
不能同时映射到两个
Servlet
中。
1.方式一
demoServlet
/suibian/*
*.do
2.方式二
demoServlet
/suibian/*
demoServlet
*.do
AutoStartServlet.java:
package com.first.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class AutoStartServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter pw=resp.getWriter();
pw.println("AutoServlet......");
pw.flush();
pw.close();
}
@Override
public void init() throws ServletException {
//看servlet什么时候初始化,请求时还是启动时
System.out.println("AutoStartServlet Init......");
}
}
web.xml配置:
AutoStartServlet com.first.servlet.AutoStartServlet 1 AutoStartServlet /autoStart.do
输出:
启动Tomcat时控制台就输出了
AutoStartServlet Init......
请求访问servlet:
2.通过自启动Servlet实现配置信息的读取 需求:修改文件下载案例,通过自启动 Servlet 读取配置信息。
实际应用原因:
之前写的文件下载案例是文件路径是硬编码,而实际工程现场,部署的不是源代码,而是编译好的字节码文件,字节码文件已经不能再根据实际工程环境再去改变文件路径了,但web.xml相当于一个文本文件可以随意修改,所以本节讲解将配置信息放在web.xml文件中,启动Tomcat时,自动读取配置信息。
AutoStartServlet.java配置好且FileDownServlet.java修改之后,直接运行FileDownServlet.java即可,因为由1.可知AutoStartServlet.java在服务器启动之时就自动启动了,此时web.xml的路径配置信息(在AutoStartServlet.java的servlet配置中)就配置好了。
输出:
此时访问FileDownServlet.java,发现文件可下载
二.Servlet线程安全问题 在 Servlet 中使用的是多线程方式来执行 service() 方法处理请求,所以我们在使用 Servlet 时需要考虑到线程安全问题,在多线程中对于 对象中的成员变量是最不安全的,所以不要在 Servlet 中通过成员变 量的方式来存放数据,如果一定要使用成员变量存储数据,在对数 据进行操作时需要使用线程同步的方式来解决线程安全问题,避免 出现数据张冠李戴现象。
比如执行了一个servlet,一个客户端请求之后,在一个线程中执行了一个 service()方法,又有一个客户端请求之后,又新建了一个线程,这个线程又调用了一个service()方法,但是这两个线程共享这个servlet的成员变量。所以尽量不要使用成员变量,能用局部变量最好用局部变量,非得用成员变量的话最好只读不写。
ThreadSafeServlet.java:
package com.first.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class ThreadSafeServlet extends HttpServlet {
//设置成员变量(多线程下不安全,即多个线程都可访问)
private PrintWriter pw;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取用户提交数据
String value=req.getParameter("name");
pw=resp.getWriter();
try {
//沉睡5s
Thread.sleep(5000);
pw.println(value);
pw.flush();
pw.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
配置web.xml:
ThreadSafeServlet com.first.servlet.ThreadSafeServlet ThreadSafeServlet /safe.do
输出:
访问http://localhost:8888/ajaxDemo/safe.do?name=kd
Chrome:
Firefox:
先用Chrome访问ThreadSafeServlet.java,此时尽快再用Firefox浏览器访问此servlet,尽快的访问的原因是程序就出沉睡5s。此时发现Chrome没有输出,而在Firefox有输出,这是因为pw是成员变量(类变量),两个客户端进程皆可访问它,所以Firefox的浏览器线程就又创建了pw变量,即把Chrome创建的那个覆盖了,所以Chrome没有输出,而访问Firefox5s后Firefox输出了获得了name参数的值。
如果想要改进上面的情况,可作如下修改:
ThreadSafeServlet.java:
package com.first.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class ThreadSafeServlet extends HttpServlet {
//设置成员变量(多线程下不安全,即多个线程都可访问)
private PrintWriter pw;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取用户提交数据
String value=req.getParameter("name");
synchronized (this){
pw=resp.getWriter();
try {
//沉睡5s
Thread.sleep(5000);
pw.println(value);
pw.flush();
pw.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
再做上面相同的访问,结果如下,两个浏览器会依次输出name的值!
三.Servlet的url-pattern配置
1.URL的匹配规则
下面的示例都假设以Servlet为项目名
(1)精确匹配
精确匹配是指中配置的值必须与
url
完全精确匹配。
demoServlet
/demo.do
http://localhost:8888/Servlet/demo.do
匹配
http://localhost:8888/Servlet/suibian/demo.do
不匹配
可见我们之前配置的url-pattern都是精确匹配
(2)扩展名匹配
在允许使用统配符作为匹配规则,
“*”
表示匹配任意字符。在扩展名匹配中只要扩展名相同都会被匹配和路径无关。注意,在使用扩展
名匹配时在中不能使用
“/”
,否则容器启动就会抛出异常。
demoServlet
*.do
http://localhost:8888/demo/abc.do
匹配
http://localhost:8888/demo/suibian/haha.do
匹配(虽然又多加了层路径,但是以.do结尾就行)
http://localhost:8888/demo/abc
不匹配
(3)路径匹配
根据请求路径进行匹配,在请求中只要包含该路径都匹配。
“*”
表示任意路径以及子路径。
demoServlet
/suibian/*
http://localhost:8888/demo/suibian/haha.do
匹配
http://localhost:8888/demo/suibian/hehe/haha.do
匹配
http://localhost:8888/demo/hehe/heihei.do
不匹配
http://localhost:8888/demo/abc.do 匹配 http://localhost:8888/demo/suibian/haha.do 匹配(虽然又多加了层路径,但是以.do结尾就行) http://localhost:8888/demo/abc 不匹配demoServlet *.do
(3)路径匹配
根据请求路径进行匹配,在请求中只要包含该路径都匹配。
“*”
表示任意路径以及子路径。
demoServlet
/suibian/*
http://localhost:8888/demo/suibian/haha.do
匹配
http://localhost:8888/demo/suibian/hehe/haha.do
匹配
http://localhost:8888/demo/hehe/heihei.do
不匹配
(4)任意匹配
匹配
“/”
。匹配所有但不包含
JSP
页面(即不会拦截jsp页面,但是其他的页面都会被拦截并转到此servlet)。
/
http://localhost:8888/demo/suibian.do
匹配
http://localhost:8888/demo/addUser.html
匹配
http://localhost:8888/demo/css/view.css
匹配
http://localhost:8888/demo/addUser.jsp
不匹配
http://localhost:8888/demo/user/addUser.jsp
不匹配
(5)匹配所有
/*
http://localhost:8888/demo/suibian.do
匹配
http://localhost:8888/demo/addUser.html
匹配
http://localhost:8888/demo/suibian/suibian.do
匹配
http://localhost:8888/demo/suibian.do 匹配 http://localhost:8888/demo/addUser.html 匹配 http://localhost:8888/demo/suibian/suibian.do 匹配/*
2.优先顺序 当一个 url 与多个 Servlet 的匹配规则可以匹配时,则按照 “ 精确路径> 最长路径 > 扩展名 ” 这样的优先级匹配到对应的 Servlet 。
3.考考你 Servlet1 映射到 /abc/* Servlet2 映射到 /* Servlet3 映射到 /abc Servlet4 映射到 *.do (扩展名匹配,优先级最低) 当请求 URL 为 “/abc/a.html” , “/abc/* ” 和 “/* ” 都匹配, Servlet 引擎将调用 Servlet1 。 当请求 URL 为 “/abc” 时, “/abc/* ” 和 “/abc” 都匹配, Servlet 引擎将调用 Servlet3 。 当请求 URL 为 “/abc/a.do” 时, “/abc/* ” 和 “ *.do” 都匹配, Servlet 引擎将调用 Servlet1 。 当请求 URL 为 “/a.do” 时, “/* ” 和 “*.do” 都匹配, Servlet 引擎将调用Servlet2 。 当请求 URL 为 “/xxx/yyy/a.do” 时, “/* ” 和 “*.do” 都匹配, Servlet 引擎将调用 Servlet2 。
四.Servlet的多URL映射方式
在
web.xml
文件中支持将多个
URL
映射到一个
Servlet
中,但是相同的
URL
不能同时映射到两个
Servlet
中。
1.方式一
demoServlet
/suibian/*
*.do
2.方式二
demoServlet
/suibian/*
demoServlet
*.do
demoServlet /suibian/* *.do
2.方式二
demoServlet
/suibian/*
demoServlet
*.do



