Servlet
什么是Servlet
A servlet is a small Java program that runs within a Web server. Servlets receive and respond to requests from Web clients, usually across HTTP, the HyperText Transfer Protocol.
运行在服务器上的Java小程序,通过HTTP协议接收浏览器发送的请求,并且做出响应。
Servlet生命周期
Servlet创建的时机:
用户第一次访问Servlet地址的时候(),Tomcat通过**反射实例化**Servlet对象(servlet-class)(Servlet对象由Tomcat创建)。
Servlet的运行过程
Servlet的生命周期方法
方法 | 作用 | 运行次数 |
---|---|---|
构造方法 | 实例化Servlet的时候 | 1次 |
void init(ServletConfig config) | 初始化方法 | 1次 |
void service(ServletRequest req, ServletResponse res) | 服务方法 | 每次请求都会执行 |
void destroy() | 销毁 | 1次 |
Servlet一旦实例化以后就常驻内存,Web服务器关闭或重启才销毁
演示:
直接实现Servlet接口,并重写所有的方法
/**
* Servlet生命周期的方法
* @author NewBoy
*
*/
public class Demo1HelloServlet implements Servlet {
//实例化:构造方法
public Demo1HelloServlet() {
System.out.println("1. 执行了构造方法");
}
//初始化方法
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("2. 执行了初始化的方法");
}
//返回一个ServletConfig对象
@Override
public ServletConfig getServletConfig() {
return null;
}
//服务的方法:每次请求都会执行
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException,
IOException {
System.out.println("服务的方法:每次请求都会执行");
}
//返回与Servlet有关的一些信息
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
System.out.println("Servlet销毁");
}
}
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>com.itheima.servlet.Demo1HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>demo1</servlet-name>
<!-- / 代表服务器端的根目录: webapps\day36-servlet\ -->
<url-pattern>/demo1</url-pattern>
</servlet-mapping>
问:在service方法中输出request和response对象,这两个对象从哪里来的?
在Servlet中ServletRequest和ServletResponse是2个接口,Sun没有具体的实现,具体的实现类由Web容器去实现。并且通过service()方法做为参数传递进来。
Servlet的实现类
Servlet继承结构
GenericServlet类
Defines a generic, protocol-independent servlet. To write an HTTP servlet for use on the Web, extend HttpServlet instead.
定义了一个通用的,独立于协议的Servlet,实现了2个接口:Servlet、ServletConfig接口,实现了这两个接口中的所有方法,子类是HttpServlet,它是一个抽象类。如果我们要写自己的Servlet,建议继承于HttpServlet。
HttpServlet类
A subclass of HttpServlet must override at least one method, usually one of these:
doGet, if the servlet supports HTTP GET requests
doPost, for HTTP POST requests
如果我们要写Servlet,继承于HttpServlet,至少要重写下面一个方法:doGet或doPost方法
service()方法的源码的简单分析:
//定义了常量
private static final String METHOD_GET = "GET";
private static final String METHOD_POST = "POST";
//HttpServletRequest 是 ServletRequest的子接口, HttpServletResponse是ServletResponse子接口
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 得到请求的方式
String method = req.getMethod();
// 如果是GET方法
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
//判断是否使用缓存
if (lastModified == -1) {
//不使用缓存,调用doGet方法
doGet(req, resp);
} else {
//使用缓存
}
//如果是POST方法,则调用doPost()
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else {
//如果上面所有的方法都没有,报服务器异常(501错误)
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
结论:以后我们只需要继承于HttpServlet,并且重写doGet或doPost方法。
- doGet用于处理get请求
- doPost用于处理post方法,
如果没有重写相应的方法,则会出现501的错误。
如果在地址栏上直接输入的访问地址或表单使用get方法提交,则调用doGet方法。通过表单post方式提交的,则使用doPost方法
Servlet的访问地址
通常一个Servlet只有一个访问地址,但是可以通过url-pattern
和servlet-mapping
给一个Servlet配置多个访问地址。
Servlet映射多个路径
- 方式1:
一个servlet-mapping中包含多个url-pattern
<servlet>
<servlet-name>Demo2MyServlet</servlet-name>
<servlet-class>com.itheima.servlet.Demo2MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Demo2MyServlet</servlet-name>
<url-pattern>/demo2</url-pattern>
<!-- 映射多个url-pattern -->
<url-pattern>/myservlet</url-pattern>
</servlet-mapping>
- 方式2:
一个servlet对应多个servlet-mapping
<servlet>
<servlet-name>Demo2MyServlet</servlet-name>
<servlet-class>com.itheima.servlet.Demo2MyServlet</servlet-class>
</servlet>
<!-- 一个servlet对应多个servlet-mapping -->
<servlet-mapping>
<servlet-name>Demo2MyServlet</servlet-name>
<url-pattern>/demo2</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Demo2MyServlet</servlet-name>
<url-pattern>/mydemo</url-pattern>
</servlet-mapping>
的访问方式
配置方式 | 格式 | 说明 |
---|---|---|
完全路径匹配 | /aaaa/aaa/bbb | 访问地址与url-pattern中精确匹配,有一个虚拟路径 |
目录匹配 | /ccc/* /* 通配符访问 | * 匹配任意多个字符, 不建议使用这种/* ,会导致其它所有的资源都无法访问 |
扩展名匹配 | *.扩展名 | 匹配某一类扩展名 |
从格式上分有2种方式:
- 以/
开头的方式
- 以.扩展名
结尾的方法
问:能不能同时写?
无效的url-pattern,不支持这种写法。导致整个web项目加载失败,所有的web资源都无法访问。
面试题:
1) 创建2个Servlet,一个Servlet1,一个Servlet2,在下列情况下,访问哪个Servlet,课后自己测试证明。
请求的URL | Servlet1的url-pattern | Servlet2的url-pattern | 响应的Servlet |
---|---|---|---|
/abc/a.html | /abc/* | /* | Servlet1 |
/abc | /abc/* | /abc | Servlet2 |
/abc/a.do | /abc/* | *.do | Servlet1 |
/a.do | /* | *.do | Servlet1 |
/xxx/yyy/a.do | /* | *.do | Servlet1 |
结论:
- 优先级:以/
开头优先级高于*.扩展名
结尾的格式
- 匹配原则:最佳匹配原则,哪个更加接近就匹配哪个
load-on-startup
作用:
正常来说,一个Servlet是在第一次用户访问的时候加载。如果我们需要在启动时加载某个Servlet,就可以使用这个配置参数
参数的使用说明:
<load-on-startup>1</load-on-startup>
参数是一个整数,数字越小越先加载。必须大于等于0,如果是负数,则不起作用,与没有写这个参数是一样的。
<servlet>
<servlet-name>demo1</servlet-name>
<servlet-class>com.itheima.servlet.Demo1HelloServlet</servlet-class>
<!-- 启动时加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
添加到配置文件中后,不用用户调用才实例化,服务器启动的时候就会加载,不过加载后不用会浪费资源
request接收请求参数
HttpServletRequest得到参数的方法
String request.getParameter(参数名)
- 通过参数名得到参数值,返回String类型
- 参数名是标签的name
String[] request.getParameterValues(参数名)
- 通过同名参数,得到一组值,返回字符串数组。用于复选框或多选的下拉列表
- 参数名是标签的name
request.setCharacterEncoding(编码)
- 用于解决POST提交的汉字乱码问题
接收请求参数案例:
表单上有用户名,性别,城市,爱好。使用POST方法提交数据给服务器,在Servlet中使用上面三个方法得到所有的值,并且在页面上输出提交的信息,注意汉字的乱码问题。
<!DOCTYPE html>
<html>
<head>
<title>register.html</title>
<meta charset="utf-8">
</head>
<body>
<h3>用户注册</h3>
<!-- action提交给servlet -->
<form action="register" method="post">
用户名:<input type="text" name="user"/><br/>
性别: <input type="radio" name="gender" value="男" checked="checked">男 <input type="radio" name="gender" value="女">女
<br/>
城市:
<select name="city">
<option value="广州">广州</option>
<option value="深圳">深圳</option>
<option value="上海">上海</option>
</select>
<br/>
爱好:
<input type="checkbox" name="hobby" value="上网" >上网
<input type="checkbox" name="hobby" value="游泳" >游泳
<input type="checkbox" name="hobby" value="旅游" >旅游
<hr/>
<input type="submit" value="注册">
</form>
</body>
</html>
/**
* 注册用户
* @author NewBoy
*
*/
public class RegisterServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//对于post方法,对提交的参数设置汉字编码
request.setCharacterEncoding("utf-8");
out.print("<h1>注册成功</h1>");
//得到一个参数
out.print("用户名:" + request.getParameter("user") + "<br/>");
out.print("性别:" + request.getParameter("gender") + "<br/>");
out.print("城市:" + request.getParameter("city") + "<br/>");
//得到多个参数
String[] values = request.getParameterValues("hobby");
out.print("爱好:" + Arrays.toString(values) + "<br/>");
out.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}