1、servlet应用实例
1、客户端提交一个表单数据访问服务端的整个流程图:
客户端访问add.html页面并通过Http Request携带表单请求数据,通过web.xml中的映射关系,找到AddServlet类中的doPost方法,并执行该方法。
2、编写一个add.html表单提交页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="add" method="post">
名称: <input type="text" name="fname"/></br/>
价格: <input type="text" name="price"/></br/>
库存: <input type="text" name="fcount"/></br/>
备注: <input type="text" name="remark"/></br/>
<input type="submit" value="添加" />
</form>
</body>
</html>
3、提交表单到AddServlet类,问题,add方法是如何映射到AddServlet类中的方法的?
- 在web.xml中配置映射关系
1、用户发送请求,action=add
2、项目中,web.xml中找到url-pattern = /add ->第12行
3、找到第11行的servlet-name = AddServlet
4、找和servlet-mapping中servlet-name一致的servlet,找到第8行
5、再找到第9行servlet-class = com.atguigu.servlets.AddServlet
6、用户发送的是post请求(method=post),因此tomcat会执行AddServlet类中的doPost方法
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>AddServlet</servlet-name>
<servlet-class>com.atguigu.servlets.AddServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AddServlet</servlet-name>
<url-pattern>/add</url-pattern>
</servlet-mapping>
<!--
1、用户发送请求,action=add
2、项目中,web.xml中找到url-pattern = /add ->第12行
3、找到第11行的servlet-name = AddServlet
4、找和servlet-mapping中servlet-name一致的servlet,找到第8行
5、再找到第9行servlet-class = com.atguigu.servlets.AddServlet
6、用户发送的是post请求(method=post),因此tomcat会执行AddServlet类中的doPost方法
-->
</web-app>
4、AddServlet类
- 该类需要继承javax.servlet.http中的HttpServlet类
- 由于表单提交时选择的是post方式请求,所有就会调用该类的doPost方法
- HttpServletRequest request对象就可以获取到表单提交的数据
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class AddServlet extends HttpServlet {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String fname = request.getParameter("fname");
String priceStr = request.getParameter("price");
int price = Integer.parseInt(priceStr);
String fcountStr = request.getParameter("fcount");
int fcount = Integer.parseInt(fcountStr);
String remark = request.getParameter("remark");
System.out.println("fname:" + fname);
System.out.println("price:" + price);
System.out.println("fcount:" + fcount);
System.out.println("remark:" + remark);
}
}
页面:
打印结果:
fname:apple
price:5
fcount:30
remark:ok
2、servlet中处理中文乱码问题
-
post请求
设置字符编码。防止中文乱码,需要注意的是,设置编码这一句代码必须在所有的获取参数动作之前request.setCharacterEncoding("UTF-8"); String fname = request.getParameter("fname");
-
get请求
在tomcat8之后不需要处理。
3、servlet的继承关系
3.1 继承关系
javax.servlet.Servlet接口
javax.servlet.GenericServlet抽象类
javax.servlet.http.HttpServlet抽象子类
1、javax.servlet.http.HttpServlet抽象子类:
-
void service(request.response) --不是抽象的
-
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); // 获取请求方式 long lastModified; if (method.equals("GET")) { lastModified = this.getLastModified(req); if (lastModified == -1L) { this.doGet(req, resp); } else { long ifModifiedSince; try { ifModifiedSince = req.getDateHeader("If-Modified-Since"); } catch (IllegalArgumentException var9) { ifModifiedSince = -1L; } if (ifModifiedSince < lastModified / 1000L * 1000L) { this.maybeSetLastModified(resp, lastModified); this.doGet(req, resp); } else { resp.setStatus(304); } } } else if (method.equals("HEAD")) { lastModified = this.getLastModified(req); this.maybeSetLastModified(resp, lastModified); this.doHead(req, resp); } else if (method.equals("POST")) { this.doPost(req, resp); } else if (method.equals("PUT")) { this.doPut(req, resp); } else if (method.equals("DELETE")) { this.doDelete(req, resp); } else if (method.equals("OPTIONS")) { this.doOptions(req, resp); } else if (method.equals("TRACE")) { this.doTrace(req, resp); } else { String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[]{method}; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(501, errMsg); } }
-
各种if判断,根据请求方式不同,决定去调用不同的do方法
-
在HttpServlet这个抽象类中,do方法都差不多
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String msg = lStrings.getString("http.method_post_not_supported"); this.sendMethodNotAllowed(req, resp, msg); } private void sendMethodNotAllowed(HttpServletRequest req, HttpServletResponse resp, String msg) throws IOException { String protocol = req.getProtocol(); // 获取协议 if (protocol.length() != 0 && !protocol.endsWith("0.9") && !protocol.endsWith("1.0")) { resp.sendError(405, msg); } else { resp.sendError(400, msg); } }
2、 浏览器页面出现405的原因
1)在AddServlet类中没有doGet()方法
2)页面模拟发送get请求,断点调试
由于是get请求,所有会执行HttpServlet类中service()方法中的doGet()方法
进入到doGet()方法
sendMethodNotAllowed()方法
页面显示
-
3、小结
1)继承关系:HttpServlet -> GenericServlet -> Servlet
2)Servlet中的核心方法:init(),service(),destroy()
3)服务方法:当有请求过来时,service方法会在那个响应(其实是tomcat容器调用的)
在HttpServlet中我们会去分析请求的方式:到底是get、post、head、delete方法等等
然后再决定调用的是哪个do开头的方法
那么在HttpServlet中这些do方法默认都是405实现风格 - 要我们子类去实现对应的方法,否则默认会把405错误。
4)因此,在新建Servlet时,我们才会去考虑请求方法,从而决定重写哪个do方法。
4、Servlet的生命周期
1)生命周期:从出生到死亡的过程就是生命周期。对应Servlet中的三个方法:init(),service() ,destroy()方法
2)默认情况下:
- 第一次接收请求时,这个Servlet会进行实例化(调用构造方法)、初始化(调用init()方法)、然后服务(调用service()方法)
- 从第二次请求开始。每一次都是调用服务方法
- 当容器关闭时,其中的所有的servlet实例会被销毁,调用销毁方法
3)通过案例发现:
- Servlet实例tomcat只会创建一个实例,所有的请求都是这一个实例去响应。
- 默认情况下,第一次请求时,tomcat才会去实例化,初始化,然后再服务,这样的好处提高服务的启动速度;缺点是第一次请求时耗时较长。
- 因此得出结论:如果需要提高系统的启动速度,当前默认情况就是这样。如果需要提高响应速度,我们应该设置Servlet启动时机。
4)Servlet的初始化时机
-
默认是第一次接收请求时,实例化,初始化
<servlet> <servlet-name>DemoServlet</servlet-name> <servlet-class>com.atguigu.servlets.DemoServlet</servlet-class> <!-- servlet启动的优先级,load-on-startup的值越小优先级越高 --> <load-on-startup>1</load-on-startup> </servlet>
5)Servlet在容器中是:单例的、线程不安全的
- 单例:所有的请求都是同一个实例去响应
- 线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。但是在中间某个时机,另外一个线程改变了这个值,从而导致这个线程的执行逻辑发生改变。
- 因为servlet是线程不安全的,尽量不要在servlet中定义成员变量。如果不得不定义成员变量的值,① 那么不要去根据成员变量的值做一些逻辑判断。
5、HTTP协议
1)Http称之为 超文本传输协议
2)HTTP是无状态的
3)HTTP包含两部分:请求和响应
-
请求:
请求包含三个部分:1.请求行;2.请求消息头;3.请求主体
1)请求行包含三个信息:1.请求的方式;2.请求的URL;3.请求的协议(一般都是HTTP1.1)
2)请求消息头包含很多客户端浏览器需要告诉服务器的信息。比如:浏览器的型号、版本…
3)请求主体,三种情况
get方式,没有请求体,但是有一个queryString
post方式,有请求体,form data
json格式,有请求体,request payload
-
响应
响应也包含三部分:1.响应行;2.响应头;3.响应体
1)响应行包含三个信息:1.协议 ;2.响应状态码(200);3.响应状态(ok)
2)响应头:包含服务器的信息;服务器发送给浏览器的信息
3)响应体:响应的实际内容(比如请求add.html页面时,响应的内容就是 …)
6、会话机制
6.1 案例分析
1、servlet类
public class Demo02Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
System.out.println("session ID:" + session.getId());
}
}
2、web.xml
<servlet>
<servlet-name>Demo02Servlet</servlet-name>
<servlet-class>com.atguigu.servlets.Demo02Servlet</servlet-class>
<!-- servlet启动的优先级 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Demo02Servlet</servlet-name>
<url-pattern>/demo02</url-pattern>
</servlet-mapping>
3、浏览器请求,服务端生成sessionID,第一次会放到响应头里面返回给客户端浏览器
浏览器响应头中的JSSIONID就是服务器生成的并返回给浏览器的
浏览器第二次请求时,打印断点查看session信息,发现session信息不是新的,还是第一次创建时的那个session
第二次查看浏览器时发现在响应头里面就没有了JSESSIONID,此时的JSESSIONID放到了请求头的Cookie里面啦。
6.2 会话跟踪技术
1)客户端第一次发请求给服务器,服务器获取session,获取不到,则创建新的,然后响应给客户端浏览器。下次客户端给服务器发请求时,会把sessionID带给服务器,那么服务器就能获取到,服务器就可以判断这一次请求和上一次请求是同一个客户端,从而能够区分开。
6.3 session保存作用域
-
session保存作用域和具体的某一个session对应的
-
常见的API
void session.setAttribute(k,v); Object session.getAttribute(k)
1、demo03Servlet:向session中设置值
public class Demo03Selrvlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getSession().setAttribute("uname","zhangsan");
}
}
2、demo04Servlet:从session中获取值
public class Demo04Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Object uname = request.getSession().getAttribute("uname");
System.out.println(uname);
}
}
3、第一次通过edgn浏览器请求demo03,向HttpSession里面设置值,再使用同样的浏览器访问demo04从HttpSession中获取值,发现可以获取到值。
4、再换用chrome浏览器请求demo04,发现没有获取到值。
7、服务器内部转发以及客户端重定向
7.1 服务器内部转发
一次请求响应的过程,对于客户端而言,内部经过了多少次转发,客户端是不知道的。
客户端请求demo06,执行以上代码,程序将会转发到demo07中执行代码,并由demo07返回结果给客户端。
代码演示:
Demo05Servlet:
public class Demo05Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo05.....");
// 服务器端内部转发
request.getRequestDispatcher("demo06").forward(request,response);
}
}
Demo06Servlet:
public class Demo06Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo06......");
}
}
客户端请求访问demo05,服务端首先请求demo05,在转发到demo06,客户端地址栏URL不会改变,整个过程客户端是不知道的
打印结果:
7.2 客户端重定向
两次请求响应的过程,客户端知道请求URL有变化。
代码:
public class Demo05Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo05.....");
// 演示客户端重定向
response.sendRedirect("demo06");
}
}
public class Demo06Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo06......");
}
}
客户端请求demo05,发现地址栏的URL变为demo06了。
8、保存作用域
8.1 保存作用域
原始情况下,保存作用域我们可以认为有四个
1)page(页面级别,现在几乎不用)
2)request(一次请求响应范围)
3)session(一次会话范围)
4)application(整个应用范围)
8.1.1 request(一次请求响应范围)
(1)客户端重定向,这种方式是获取不到demo01中设置的数据,原因是服务器内部使用的是客户端请求转发,是两次请求,所以不在一个请求的作用域里面,请求1和响应1是一个请求流程,请求2是另外一个流程。
代码demo01:
public class Demo01Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/**
* 演示request作用域
*
*/
// 向request作用域中添加数据
request.setAttribute("name","lili");
// 客户端重定向
response.sendRedirect("demo02");
}
}
demo02:
public class Demo06Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo06......");
Object name = request.getAttribute("name");
System.out.println("name===>" + name);
}
}
打印结果:
(2)服务器请求转发
demo01:
public class Demo01Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 向request作用域中添加数据
request.setAttribute("name","lili");
// 服务器端转发
request.getRequestDispatcher("demo06").forward(request,response);
}
}
打印结果:
8.1.2 session
一次会话范围有效
8.1.3 application
一次应用程序范围有效
9、路径问题
9.1 相对路径
../ 表示返回到上一级
``

#### 9.2 绝对路径

### 10、Filter过滤器

1)Filter也属于Servlet规范
2)Filter开发步骤:新建类实现Filter接口,然后实现其中的三个方法:init、doFilter、destroy
3)配置Filter,可以用注解@WebFilter,也可以使用xml文件<filter> <filter-mapper>
4)**Filter在配置时,和servlet一样,也可以配置通配符,如:@WebFilter("*.do"):表示拦截所有以.do结尾的请求**
代码:
* Filter过滤器
@WebFilter("*.do")
```java
@WebFilter("/demo07.do")
public class Demo07Filter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("helloA");
// 放行
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("helloA2");
}
@Override
public void destroy() {
}
}
-
Demo07Servlet
@WebServlet("/demo07.do") public class Demo07Servlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("执行demo07....."); request.getRequestDispatcher("add.html").forward(request,response); } }
打印结果:
5)过滤器链
- 如果采用的是注解的方式进行配置,那么过滤器链拦截顺序是按照全类名的先后顺序排序的
- 如果采取的是xml的方式进行配置,那么按照配置的先后顺序进行排序的
filter01
@WebFilter("*.do")
public class Filter01 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("A");
// 放行
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("A2");
}
}
filter02
@WebFilter("*.do")
public class Filter02 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("B");
// 放行
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("B2");
}
}
filter03
@WebFilter("*.do")
public class Filter03 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("C");
// 放行
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("C2");
}
}
demoServlet
@WebServlet("/demo07.do")
public class Demo07Servlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("执行demo07.....");
request.getRequestDispatcher("add.html").forward(request,response);
}
}
打印结果: