Servlet是使用Java编写的运行在服务端的程序。
Servlet主要用于处理浏览器端发送的Http请求,并返回一个响应。
Servlet开发需要使用到的依赖:
<!--添加servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- 引入jsp依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
一 Servlet注册
1 xml方式
1)基本用法
由于浏览器客户端是通过URL地址访问web服务器中的资源,所以Servlet程序若想被外界访问,必须把servlet程序映射到一个URL地址上,这个工作在web.xml文件中使用 <servlet>元素和<servlet-mapping>元素完成。
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.glls.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>元素用于注册Servlet,它包含有两个主要的子元素:
<servlet-name>: 设置Servlet的注册名称
<servlet-class>: 设置Servlet的完整类名,包名+类型形式
<servlet-mapping>元素用于映射已注册的Servlet的对外访问路径,它包含有两个子元素:
<servlet-name>:Servlet的注册名称,必须和配对的<servlet>中的<servlet-name>相同
<url-pattern>:设置访问该Servlet资源的路径
例如,访问:http://localhost:8080/test/hello时,
Tomcat服务器截取到访问的资源名称/hello,
先从web.xml的<url-pattern>中找匹配的url,找到后,再查找对应的<servlet-name>;
然后从<servlet>节点中找匹配的<servlet-name>,找到后,根据<servlet-class>的类名,创建Servlet对象,执行其中的方法
2)Servlet的多重映射
注意:
同一个Servlet可以被映射到多个URL上,即<servlet-mapping>中可以设置多个<url-pattern>
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.glls.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
3)<url-pattern>的匹配规则
格式 | 说明 | |
---|---|---|
精确匹配 | /具体的名称,如<url-pattern>/hello</url-pattern><url-pattern>/heh/hello</url-pattern><url-pattern>/hello.do</url-pattern> | 只有url路径是指定的具体的名称时才会触发Servlet |
后缀匹配 | .xxx, 如<url-pattern>.do</url-pattern> | 只要是以xxx结尾的资源,都会匹配触发Servlet |
路径匹配 | /, 如<url-pattern>/</url-pattern><url-pattern>/haha/*</url-pattern> | 匹配所有请求,包含服务器的所有资源 |
缺省匹配 | /, 如<url-pattern>/</url-pattern> | 匹配所有请求,不包括.jsp |
注意:<url-pattern>/*.do</url-pattern> 错误写法
2 @WebServlet注解方式
注解中使用主要属性:
属性名 | |
---|---|
name | Servlet名字,可以自定义 |
value | 访问Servlet资源时匹配的路径,和urlPatterns互斥 |
urlPatterns | 允许当前Servlet有多个匹配方式,和value互斥 |
loadOnStartup | 标记容器是否启动时加载该Servlet |
initParams | 当前Servlet的初始化配置WebInitParam 注解类型 ,属性包括name和value |
@WebServlet(name = "HelloServlet", value = "/HelloServlet")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("get hello");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("post hello");
}
}
二 Servlet开发
Servlet由Servlet容器提供,所谓的Servlet容器是指提供了Servlet 功能的Web服务器(比如Tomcat),Servlet容器将Servlet动态的加载到服务器上。
使用的主要接口和类:
Servlet接口
GenericServlet抽象类
HttpServlet类
Servlet接口中的主要方法:
方法 | |
---|---|
void init(ServletConfig config) | 初始化Servlet |
void service(ServletRequet req, ServletResponse res) | 负责响应客户端请求 |
void destroy() | 销毁servlet对象,用于释放资源等 |
ServletConfng getServletConfig() | 获得Servlet 的相关配置信息,该方法会返回一个指向ServletConfig的引用 |
java.lang.Sring getServletInfo() | 获得Servlet开发者定义的信息 |
Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。
request和response对象即然代表请求和响应,如果要获取客户机提交过来的数据,只需要找request对象。要向客户端输出数据,只需要找response对象。
1 HttpServletRequest接口
HttpServletRequest接口继承了ServletRequest接口
HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,开发人员通过这个对象中的方法,可以获得请求相关信息。
常用方法:
方 法 | 说 明 |
---|---|
String getParameter(String name) | 获取请求中的参数,该参数是由name指定的 |
Enumeration getParameterNames() | 获取请求中所有参数的名字 |
String[] Enumeration getParameterValues(String name) | 返回请求中的参数值,该参数值是由name指定的 |
Map<K, V> getParameterMap() | 返回此请求的参数的 java.util.Map对象 |
ServletInputStream getInputStream() | 获取请求的输入流中的数据 |
String getQueryString() | 返回包含在请求 URL 中路径后面的数据字符串 |
Enumeration getAttributeNames() | 返回当前请求的所有属性的名字集合 |
Object getAttribute(String name) | 返回name指定的属性值 |
void setAttribute(String name, Object value) | 设置属性值 |
void removeAttribute(String name) | 删除指定的属性值 |
Cookies getCookies() | 返回客户端发送的Cookie |
HttpSession getsession() | 返回和客户端相关的session,如果没有给客户端分session,则返回null |
HttpSession getsession(boolean create) | 返回和客户端相关的session,如果没有给客户端分session,则创建一个session并返回 |
String getCharacterEncoding() | 返回请求的字符编码方式 |
void setCharacterEncoding(String env) | 设置请求中使用的字符编码方式 |
String getMethod() | 获取发送请求的方式,如get、post |
int getContentLength() | 返回请求体的有效长度 |
String getProtocol() | 获取请求所使用的协议名称 |
String getRemoteAddr() | 获取客户端的IP地址 |
String getRemoteHost() | 获取客户端的名字 |
String getServerName() | 返回接受请求的服务器的名字 |
String getServerPath() | 获取请求的路径 |
String getHeader(String name) | 返回指定请求头的值 |
1.1 获取请求参数
1)get方式 了解
//读取get方式提交的数据
//获取请求参数
//如果参数中有中文,name=%E5%BC%A0%E4%B8%89&password=123
String queryString = request.getQueryString();
//中文乱码问题
queryString = URLDecoder.decode(queryString, "utf-8");
System.out.println(queryString);
2)post方式 了解
//读取post方式提交的数据
ServletInputStream inputStream = request.getInputStream();
byte[] buff = new byte[1024];
int len = inputStream.read(buff);
String string = new String(buff, 0, len);
string = URLDecoder.decode(string, "utf-8");
System.out.println(string);
3)推荐的方式
//根据表单中输入控件的name属性值,获取表单参数
//get和post方式都可以用该方法获取表单参数
//tomcat8下,get方式下,使用getParameter方法,中文不会乱码
String name = request.getParameter("name");
String pwd = request.getParameter("password");
System.out.println(name);
System.out.println(pwd);
1.2 post提交方式下中文乱码处理
出现乱码的原因:html页面使用utf8编码,传参的时候也是用该编码方式,但是,针对post方式,服务解码的时候使用iso-8859-1,编解码方式不一致,引起了乱码问题
//post提交方式,解决中文乱码的方案
request.setCharacterEncoding("utf-8");
注意:使用tomcat8以上版本后,get方式不存在中文乱码问题
2 HttpServletResponse
HttpServletResponse对象服务器的响应。该对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法
方 法 | 说 明 |
---|---|
PrintWriter getWriter() | 返回Servlet引擎创建的字符输出流对象 |
void addCookie(Cookie cookie) | 将指定的Cookie加入到当前的响应中 |
void addHeader(String name,String value) | 将指定的名字和值加入到响应的头信息中 |
String encodeURL(String url) | 编码指定的URL |
void sendRedirect(String location) | 重定向,发送一个临时的响应到客户端 |
void setHeader(String name,String value) | 将给出的名字和值设置响应的头部 |
void setStatus(int sc) | 给当前响应设置状态码 |
void setContentType(String ContentType) | 设置响应的MIME类型 |
2.1 返回响应内容
//获取打印流的对象
PrintWriter writer = response.getWriter();
//writer.write("hello");
//writer.write("<html><head><title>hello</title></head><body><font color='red'>hello world</font></body></html>");
String html = "<html>";
html += "<head>";
html += "</head>";
html += "<body>";
html += " 你好";
html += "</body>";
html += "</html>";
writer.write(html);
writer.close();
2.2 中文乱码处理
出现乱码的原因:不做任何处理时,返回的数据使用了iso-8859-1的编码,而该编码方式不支持中文,所以中文显示"???"。
方案1:
response.setCharacterEncoding("utf-8");
response.setHeader("Content-Type", "text/html");
注意:如果仅使用response.setCharacterEncoding("utf-8"),还是会显示乱码。这是由于虽然返回内容使用了utf8编码,但是浏览器加载的页面使用的是gbk编码,所以还需要设置content-type响应头。
方案2:
response.setContentType("text/html;charset=utf-8");
2.3 重定向
重新定位到一个资源,实现资源的跳转
response.setStatus(302);
response.setHeader("location", "/W06_Servlet2/index.html");
2.4 定时刷新
//定时刷新,每隔一秒刷新一次
//response.setHeader("refresh", "1");
//指定时间(秒)后,跳转到另外的界面
response.setHeader("refresh", "3;url=/W06_Servlet2/index.html");
三 Servlet的生命周期
1 生命周期
Servlet的生命周期主要有四个阶段:
- 创建对象
对同一个servlet,只会创建一次
- 初始化
创建对象后,init方法,只会调用一次,会获取一个ServletConfig对象
- 运行阶段
运行阶段,调用service()方法。
每调用一次servlet资源,都会调用该方法。对于每次访问请求,都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,在该方法中,根据请求方式,决定调用doGet还是doPost方法
- 销毁
关闭tomcat服务器,或者重新加载应用,都会调用destroy方法
针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说一个Servlet的实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,servlet实例对象才会销毁。
2 Servlet自动加载
它的取值必须是一个整数;
当值小于 0 或者没有指定时,则表示容器在该 Servlet 被首次请求时才会被加载;
当值大于 0 或等于 0 时,表示容器在启动时就加载并初始化该 Servlet,取值越小,优先级越高;
当取值相同时,容器就会自行选择顺序进行加载
<servlet>
<description></description>
<display-name>LifeServlet</display-name>
<servlet-name>LifeServlet</servlet-name>
<servlet-class>com.rr.life.LifeServlet</servlet-class>
<!-- tomcat服务器加载we应用时,创建对应的servlet对象
值越小,优先级越高-->
<load-on-startup>1</load-on-startup>
</servlet>
注解的写法
@WebServlet(name = "HelloServlet", value = "/HelloServlet", loadOnStartup = 1)
四 转发和重定向
1 请求转发
一个web资源收到客户端请求后,通知服务器去调用另外一个web资源进行处理,称之为请求转发。
请求转发属于服务器行为
转发需要通过RequestDispatcher 接口的对象,调用 ServletRequest 的 getRequestDispatcher(String path) 方法可以获取该对象
RequestDispatcher 接口中提供的实现转发的方法:
方法 | 功能描述 |
---|---|
void forward(ServletRequest request,ServletResponse response) | 用于将请求转发给另一个 Web 资源。该方法必须在响应提交给客户端之前被调用 |
//request域
request.setAttribute("age", 10);
//转发
request.getRequestDispatcher("/ForwardServlet2").forward(request, response);
转发后,可以实现request对象中的数据的共享
2 重定向
一个web资源收到客户端请求后,通知浏览器去访问另外一个web资源,称之为请求重定向。
重定向属于客户端行为。本质上是两次 HTTP 请求,对应两个 request 对象和两个 response 对象。
//重定向
//response.sendRedirect("/W08_2_Servlet/RedirectServlet2");
//response.sendRedirect("RedirectServlet2");
//重定向时,不能共享request对象中的数据
request.setAttribute("id", 100);
//推荐如下写法
response.sendRedirect(request.getContextPath() + "/RedirectServlet2");
重定向时,多个Servlet不能共享request对象中的数据
3 重定向和转发区别
1)重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变;转发过程结束后,浏览器地址栏保持初始的URL地址不变。
2)重定向时,通过响应的结果告诉浏览器去重新发出请求访问另外一个资源;而转发是在服务器端内部将请求转发给另外一个资源。
3)转发时,调用者与被调用者之间共享相同的request对象和response对象,它们属于同一个访问请求和响应过程;而重定向时调用者与被调用者使用各自的request对象和response对象,它们属于两个独立的访问请求和响应过程。
4)重定向时,重定向的URL以“/”开头,它是相对于WEB应用所在的服务器;转发时的URL以“/”开头,它是相对于当前WEB应用程序。
5)转发只能将请求转发给同一个WEB应用中的资源;而重定向还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。
区别 | 转发 | 重定向 |
---|---|---|
行为 | 服务器行为 | 客户端行为 |
浏览器地址栏 URL 是否发生改变 | 否 | 是 |
是否共享 request 对象和 response 对象 | 是 | 否 |
是否能通过 request 域对象传递数据 | 是 | 否 |
是否支持跳转到其他应用的资源 | 否 | 是 |
路径中/ | 相对于当前web应用 | 相对于服务器 |
五 Servlet的线程安全
Servlet体系结构是建立在Java多线程机制之上的,它的生命周期是由Web容器负责的。
当客户端第一次请求某个Servlet时,Servlet容器将会实例化这个Servlet类。
当有新的客户端请求该Servlet时,不会再实例化该Servlet类,也就是有多个线程在使用这个实例。
当多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的情况,出现线程安全的问题。
public class CountServlet extends HttpServlet {
private int count = 1;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
response.setContentType("text/html;charset=utf-8");
synchronized (CountServlet.class) {
response.getWriter().write("第" + count + "次访问");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
count++;
}
}
}
六 ServletConfig接口
Servlet 容器初始化 Servlet 时,会为这个 Servlet 创建一个 ServletConfig 对象,并将 ServletConfig 对象作为参数传递给 Servlet 。通过 ServletConfig 对象即可获得当前 Servlet 的初始化参数信息。
方法 | 功能描述 |
---|---|
String getInitParameter(String name) | 根据初始化参数名 name,返回对应的初始化参数值。 |
Enumeration<String> getInitParameterNames() | 返回 Servlet 所有的初始化参数名的枚举集合,如果该 Servlet 没有初始化参数,则返回一个空的集合。 |
ServletContext getServletContext() | 返回一个代表当前 Web 应用的 ServletContext 对象。 |
String getServletName() | 返回 Servlet 的名字 |
配置servlet的初始化参数
<servlet>
<servlet-name>ConfigServlet</servlet-name>
<servlet-class>com.glls.servletconfig.ConfigServlet</servlet-class>
<!-- servlet的初始化参数 -->
<init-param>
<param-name>user</param-name>
<param-value>zhangsan</param-value>
</init-param>
<init-param>
<param-name>pwd</param-name>
<param-value>12345</param-value>
</init-param>
</servlet>
public class ConfigServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
// 获取ServletConfig对象
// ServletConfig 不需要额外创建
// 每个Servlet都会对应一个ServletConfig对象
ServletConfig servletConfig = this.getServletConfig();
// 获取servlet中的初始化参数的值
String info = servletConfig.getInitParameter("user");
System.out.println(info);
String info2 = servletConfig.getInitParameter("pwd");
System.out.println(info2);
}
}
注解方式
@WebServlet(value = {"/config"}, initParams = {@WebInitParam(name = "user", value = "zhangsan"),
@WebInitParam(name = "pwd", value = "1234")})
public class ConfigServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
// 获取ServletConfig对象
// ServletConfig 不需要额外创建
// 每个Servlet都会对应一个ServletConfig对象
ServletConfig servletConfig = this.getServletConfig();
// 获取servlet中的初始化参数的值
String info = servletConfig.getInitParameter("user");
System.out.println(info);
String info2 = servletConfig.getInitParameter("pwd");
System.out.println(info2);
}
}
七 ServletContext 接口
Servlet 容器启动时,会为每个 Web 应用创建一个唯一的 ServletContext 对象,该对象一般被称为“Servlet 上下文对象”。 ServletContext 对象的生命周期从 Servlet 容器启动时开始,到容器关闭或应用被卸载时结束。
常用方法:
方法 | 描述 |
---|---|
String getInitParameter(String name) | 根据初始化参数名 name,返回对应的初始化参数值。 |
Enumeration getInitParameterNames() | 返回 Web 应用所有上下文初始化参数名的枚举集合,如果该 Web 应用没有上下文初始化参数,则返回一个空的枚举集合。 |
void setAttribute(String name, Object object) | 把一个 Java 对象与一个属性名绑定,并将它作为一个属性存放到 ServletContext 中。 参数 name 为属性名,参数 object 为属性值。 |
void removeAttribute(String name) | 从 ServletContext 中移除属性名为 name 的属性。 |
Object getAttribute(String name) | 根据指定的属性名 name,返回 ServletContext 中对应的属性值。 |
String getRealPath(String path) | 返回资源文件的真实路径(文件的绝对路径)。 |
InputStream getResourceAsStream(String path) | 返回映射到资源文件的 InputStream 输入流对象。 |
1 获取web应用路径
//获取servletcontext对象
ServletContext servletContext = this.getServletContext();
// /W08_2_Servlet
System.out.println(servletContext.getContextPath());
//通过request对象,获取应用的路径
System.out.println(request.getContextPath());
2 web应用的初始化参数
/*
*<!-- 针对整个应用的初始化参数 -->
<context-param>
<param-name>AAA</param-name>
<param-value>aaa</param-value>
</context-param>
*
* */
String v1 = servletContext.getInitParameter("AAA");
System.out.println(v1);
3 多个Servlet对象共享数据
同一个Web应用中,所有的Servlet对象共享一个ServletContext对象
ServletContext servletContext = this.getServletContext();
//向servletcontext域中写入数据
//在整个web应用的不同的资源之间共享数据
servletContext.setAttribute("name", "zhangsan");
Dog d = new Dog();
d.setAge(2);
d.setName("wangcai");
servletContext.setAttribute("dog", d);