软件架构
C/S架构(Client/Server 客户端/服务器)
- 特点:必须在客户端安装特定软件
- 优点:图形效果显示较好(如:3D游戏)
- 缺点:服务器软件和功能升级,客户端也必须升级、不利于维护
- 常见的C/S程序:QQ、微信等
B/S架构(Browser/Server 浏览器/服务器)
- 特点:无需安装客户端,任何浏览器都可直接访问
- 优点:涉及到功能的升级,只需要升级服务器端
- 缺点:图形显示效果不如C/S架构
- 需要通过HTTP协议访问
服务器Web
Web(World Wide Web) 称为万维网
Internet上供外界访问的资源分为两大类
静态资源:指Web页面中供人们浏览的数据始终是不变的。(HTML、CSS)
动态资源:指Web页面中供人们浏览的数据是由程序产生的,不同时间点,甚至不同设备访问Web页面看到的内容各不相同(JSP/Servlet)
在Java中,动态Web资源开发技术我们统称为Java Web。
Tomcat
启动
bin下,双击startup.bat 启动程序(linux->.sh)
验证
浏览器,输入 http://localhost:8080/项目访问路径/资源路径
停止
双击shutdown.bat即可 关闭Tomcat启动窗口
文件夹 | 说明 | 备注 |
---|---|---|
bin | 该目录下存放的是二进制可执行文件 | startup.bat启动Tomcat、shutdown.bat 停止Tomcat |
conf | 这是一个非常重要的目录,这个目录下 有两个最为重要的文件server.xml和 web.xml | server.xml:配置整个服务器信息。例如 修改端口号,编码格式等。 web.xml:项目部署描述符文件,这个文 件中注册了很多MIME类型,即文档类型。 |
lib | Tomcat的类库,里面存放Tomcat运行 所需要的jar文件。 | |
logs | 存放日志文件,记录了Tomcat启动和关 闭的信息,如果启动Tomcat时有错误, 异常也会记录在日志文件中。 | |
temp | Tomcat的临时文件,这个目录下的东西 在停止Tomcat后删除。 | |
webapps | 存放web项目的目录,其中每个文件夹 都是一个项目;其中ROOT是一个特殊 的项目,在地址栏中没有给出项目目录 时,对应的就是ROOT项目。 | |
work | 运行时生成的文件,最终运行的文件都 在这里。 | 当客户端用户访问一个JSP文件时, Tomcat会通过JSP生成Java文件,然后再编 译Java文件生成class文件,生成的java和 class文件都会存放到这个目录下。 |
javaweb工程部署
Servlet简介
- Servlet:(Server Applet)是服务器端的程序(代码、功能实现),可交互式的处理客户 端发送到服务端的请求,并完成操作响应。
动态网页技术 - JavaWeb程序开发的基础,JavaEE规范(一套接口)的一个组成部分。
Servlet作用
- 接收客户端请求,完成操作。
- 动态生成网页(页面数据可变)。
- 将包含操作结果的动态网页响应给客户端。
IDEA创建Web项目
project--add framework support--web应用
web结构目录
src->java 源代码
lib->jar 包存放位置
web.xml web项目核心配置文件
IDEA开发Servlet
使用开发工具编写Servlet,仍要手工导入servlet-api.jar文件,并与项目关联
部署
将tomcat中servlet的jar包复制到WEB-INF下的lib目录
将jar包->add as Library 添加到类路径
编写Servlet
创建TestServlet,实现Servlet接口,覆盖5个方法
servlet类默认调用service方法
未来java代码写到service方法中,调用service层闭环形成
/**
* 1、写一个类实现Servlet接口(注意:添加servlet-api.jar 在WEB-INF中创建lib文件)
* 2、重写Servlet接口中的方法
* 3、配置Servlet的映射路径(web.xml 核心配置文件)
*/
public class TestServlet implements Servlet {
/*类不是由你调用,之前一直main方法调用,未来类创建全交给tomcat负责创建servlet执行里面的方法*/
//Servlet初始化时候执行
@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("servlet被初始化了");
}
//获取Servlet的配置信息
@Override
public ServletConfig getServletConfig() {
return null;
}
//当Servlet被访问的时候执行
@Override
public void service(ServletRequest servletRequest, ServletResponse
servletResponse) throws ServletException, IOException {
System.out.println("servlet被访问了");
}
//获取Servlet的名称
@Override
public String getServletInfo() {
return null;
}
//当Servlet被销毁的 时候调用
@Override
public void destroy() {
System.out.println("Servlet被销毁");
}
}
配置web.xml
让tomcat new对象调方法,配置虚拟路径
<?xml version="1.0" encoding="UTF-8"?>
<!--
html:超文本标记语言
xml:可扩展标记语言
xml的第一行是xml文件的标识
xml的约束:schema约束(标签顺序任意) DTD约束(标签顺序固定)
-->
<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">
<!--
核心思想:浏览器通过路径能访问到java中Servlet类
-->
<servlet>
<!-- Servlet名称,可以任意命名 -->
<servlet-name>test</servlet-name>
<!-- 要配置Servlet类的全限定名(包+类名)-->
<servlet-class>com.qf.servlet.TestServlet</servlet-class>
</servlet>
<!-- Servlet映射 -->
<servlet-mapping>
<!-- 要与上面的Servlet的名称要一致 -->
<servlet-name>test</servlet-name>
<!-- /不能省略(必须要有一个/) -->
<url-pattern>/test</url-pattern>
<!--访问/test就能访问TestServlet里的方法-->
</servlet-mapping>
</web-app>
IDEA集成部署Tomcat
绿锤子->添加Tomact
Tomcat Server->configure
部署 deployment->添加要部署的项目
HTTP协议
什么是HTTP
超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。
一个基于***请求与响应***模式的、无状态的、应用层的协议,运行于TCP协议基础之上。
不上长连接,否则淘宝那么多客户无法访问
特点
- 支持客户端(浏览器)/服务器模式。
- 简单快速:客户端只向服务器发送请求方法和路径,服务器即可响应数据,因而通信速度很快。请求方法常用的有GET、POST等。
- 灵活:HTTP允许传输任意类型的数据,传输的数据类型由Content-Type标识。
- 无连接:无连接指的是每次TCP连接只处理一个或多个请求,服务器处理完客户的请求后,即断开连接。采用这种方式可以节省传输时间。
- HTTP1.0版本是一个请求响应之后,直接就断开了。称为短连接。
- HTTP1.1版本不是响应后直接就断开了,而是等几秒钟,这几秒钟之内有新的请求,那么还是通过之前的连接通道来收发消息,如果过了这几秒钟用户没有发送新的请求,就会断开连接。称为长连接
- 无状态:HTTP协议是无状态协议。
- 无状态是指协议对于事务处理没有记忆能力。
HTTP协议通信流程
- 客户与服务器建立连接(三次握手)。
- 客户向服务器发送请求。
- 服务器接受请求并根据请求返回相应的文件作为应答。
- 客户与服务器关闭连接(四次挥手)。
请求报文和响应报文
HTTP请求报文
当浏览器向Web服务器发出请求时,它向服务器传递了一个数据块,也就是请求信息(请求报文),
HTTP请求信息由4部分组成:
1、请求行 请求方法/地址 URI协议/版本
2、请求头(Request Header)
3、空行
4、请求正文
HTTP响应报文
HTTP响应报文与HTTP请求报文相似,HTTP响应也由4个部分组成:
1、状态行
2、响应头(Response Header)
3、空行
4、响应正文
请求报文
响应报文
常见状态码
状态 代码 | 状态描述 | 说明 |
---|---|---|
200 | OK | 客户端请求成功 |
302 | Found | 临时重定向 |
403 | Forbidden | 服务器收到请求,但是拒绝提供服务。服务器通常会在响应正文中给 出不提供服务的原因 |
404 | Not Found | 请求的资源不存在,例如,输入了错误的URL。 |
500 | Internal Server Error | 服务器发生不可预期的错误,导致无法完成客户端的请求。 |
/**
*4、响应码
* 1XX :服务器接受到了请求,继续处理
* 2XX : 服务器请求成功 200
* 3XX : 请求重定向 302
* 4XX :客户端请求错误 400请求参数错误 404请求资源不存在 405请求方法不被允许
* 5XX :服务端错误 500服务端代码错误 502服务器宕机
*
*/
URL(统一资源定位符)
/**
*5、URL(统一资源定位符)
* http://localhost:8080/test?username=123&password=123
* http:// 请求协议
* localhost 请求地址
* 8080 请求端口号
* /test 请求的资源
* ?username=123&password=123 请求参数
*/
Servlet详解
Servlet核心接口和类
在Servlet体系结构中,除了实现Servlet接口,还可以通过继承GenericServlet 或 HttpServlet类,完成编写。
Servlet接口
在Servlet API中最重要的是Servlet接口,所有Servlet都会直接或间接的与该接口发生联系,或是直接实现该接口,或间接继承自实现了该接口的类。
该接口包括以下五个方法:
- init(ServletConfig config)
- ServletConfig getServletConfig()
- service(ServletRequest req,ServletResponse res)
- String getServletInfo()
- destroy( )
GenericServlet抽象类
GenericServlet 使编写 Servlet 变得更容易。它提供生命周期方法 init 和 destroy 的简单实现,要编 写一般的 Servlet,只需重写抽象 service 方法即可。
HttpServlet类
HttpServlet是继承GenericServlet的基础上进一步的扩展。
提供将要被子类化以创建适用于 Web 站点的 HTTP servlet 的抽象类。HttpServlet 的子类至少必须 重写一个方法,该方法通常是以下这些方法之一:
- doGet,如果 servlet 支持 HTTP GET 请求
- doPost,用于 HTTP POST 请求
- doPut,用于 HTTP PUT 请求
- doDelete,用于 HTTP DELETE 请求
Servlet两种创建方式
实现接口Servlet
/*
该方式比较麻烦,需要实现接口中所有方法。
*/
public class TestServlet implements Servlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse
servletResponse) throws ServletException, IOException {
System.out.println("Servlet被执行了");
}
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
继承HttpServlet(推荐)
/*
如果重写service方法,就不会再执行父类中的service方法,那么无法使用父类中对于请求的优化的
缓存操作
*/
@WebServlet("/Test")
public class TestServlet extends HttpServlet {
//不要重写service
// @Override
// protected void service(HttpServletRequest req, HttpServletResponse resp)
// throws ServletException, IOException {
// //super.service(req, resp); 一定要删掉
// System.out.println("servlet被执行了...");
// }
//处理get请求
//HttpServletRequest 处理请求, HttpServletResponse 处理响应
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//默认情况前端提交数据是以IS08859-1格式传输数据
//设置请求编码格式
req.setCharacterEncoding("utf-8");
//设置响应编码格式
//resp.setCharacterEncoding("utf-8");
//数据类型 MIME类型 text/html:文本网页
resp.setContentType("text/html;charset=utf-8");
//1.处理请求(接受请求参数)
//参数就是前端提交的数据key
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username);
System.out.println(password);
//2.调用service层方法
PrintWinter out=resp.getWriter();
//模拟service业务
if("admin".equals(username)&&"123".equals(password)){
//3.做出响应可以写标签(因为text/html)
out.write("success登陆成功");
}else{
out.write("fail");
}
}
//super.doGet(req,resp);
//删掉,否则会报错
//get/post都是一样的代码去处理
System.out.println("servlet被执行了...");
}
//处理post请求
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
<body>
<!--
action:Servlet的访问路径
http://localhost:8080/day36/login.html
http://localhost:8080/day36/login
-->
<form action="/login" method="post" enctype="application/x-www-form-urlencoded">
用户名<input type="text" name="username"><br>
密码<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
get和post区别
get请求
- get提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连
- get方式明文传递,数据量小,不安全
- 效率高,浏览器默认请求方式为GET请求
- 对应的Servlet的方法是doGet
post请求
- post方法是把提交的数据放在HTTP包的Body中
- 密文传递数据,数据量大,安全
- 效率相对没有GET高
- 对应的Servlet的方法是doPost
Servlet开发步骤
-
写一个类继承自HttpServlet
- 每次写servlet都要到xml配置一遍
- 注解式开发:@WebServlet(“/****”) —— 相当于配置了映射路径
-
重写doGet/Post方法
- 不重写service原因:当重写service类就会执行servlet中的service方法
- 重写了service,则不会调用父类的service方法,父类的优化就不执行
- doGet作用
- 处理请求
- 调用service层方法
- 做出响应
-
注册Servlet(配置映射路径)
常见错误
- HTTP Status 404 资源找不到 。
- 第一种情况:地址书写错误。
- 第二种情况:地址没有问题,把IDEA项目中out目录删除,然后重新运行。
- Serlvet地址配置重复。both mapped to the url-pattern [/hello] which is not permitted。
- Serlvet地址配置错误。比如没有写 / Invalid [helloservlet2] in servlet mapping。
Servlet应用
利用Servlet处理请求(收参)
调用Service业务方法
利用Servlet做出响应(通过响应流写出数据)
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username);
System.out.println(password);
request对象
在Servlet中用来处理客户端请求需要用doGet或doPost方法的request对象
request主要方法
方法名 | 说明 |
---|---|
String getParameter(String name) | 根据表单组件名称获取提交数据 |
void setCharacterEncoding(String charset) | 指定每个请求的编码 |
request应用
-
HTML页面
<!-- action:Servlet的访问路径 http://localhost:8080/day36/login.html http://localhost:8080/day36/login --> <form action="tz" method="post" enctype="application/x-www-form-urlencoded"> 用户名<input type="text" name="username"><br> 密码<input type="password" name="password"><br> <input type="submit" value="登录"> </form>
-
Servlet代码
//1、处理请求 String username = req.getParameter("username"); String password = req.getParameter("password"); System.out.println(username); System.out.println(password);
解决get请求乱码
req.setCharacterEncoding("utf-8");
response对象
response对象用于响应客户请求并向客户端输出信息。
response |
---|
response主要方法
方法名称 | 作用 |
---|---|
setHeader(name,value) | 设置响应信息头 |
setContentType(String) | 设置响应文件类型、响应式的编码格式 |
setCharacterEncoding(String) | 设置服务端响应内容编码格式 |
getWriter() | 获取字符输出流 |
response应用
//3、做出响应
PrintWriter out = resp.getWriter();
if(username.equals("admin") && password.equals("123")){
out.write("<h1>success,成功</h1>");
}else{
out.write("<h1>fail,失败</h1>");
}
页面跳转
转发
概念
转发的作用在服务器端,将请求发送给服务器上的其他资源,以共同完成一次请求的处理
在调用业务逻辑的Servlet中,编写以下代码
//登陆成功跳转(转发)
//获取请求转发器,参数是转发路径(不能写项目访问路径)
//其中/是路径分隔符
request.getRequestDispatcher("/目标URL-pattern").forward(request, response);
//登录失败跳转到失败页面(重定向)
//参数是重定向(必须写项目访问路径/b)
response.sendRedirect("/javaweb/fail.html")
使用forward跳转时,是在服务器内部跳转,地址栏不发生变化,属于同一次请求
数据传递
forward表示一次请求,是在服务器内部跳转,可以共享同一次request作用域中的数据
- request作用域:拥有存储数据的空间,作用范围是一次请求有效(一次请求可以经过多次转发)
- 可以将数据存入request后,在一次请求过程中的任何位置进行获取
- 可传递任何数据(基本数据类型、对象、数组、集合等)
- 存数据:request.setAttribute(key,value);
- 以键值对形式存储在request作用域中。key为String类型,value为Object类型
- 取数据:request.getAttribute(key);
- 通过String类型的key访问Object类型的value
转发特点
- 转发是服务器行为
- 转发是浏览器只做了一次访问请求
- 转发浏览器地址不变
- 转发两次跳转之间传输的信息不会丢失,所以可以通过request进行数据的传递、
- 转发只能将请求转发给同一个Web应用中的组件
重定向
概念
重定向作用在客户端,客户端将请求发送给服务器后,服务器响应给客户端一个新的请求地址,客户 端重新发送新请求。 在调用业务逻辑的Servlet中,编写以下代码
使用redirect跳转时,是在客户端跳转,地址栏发生变化,属于多次请求
response.sendRedirect("目标URI")
redirect |
---|
数据传递
sendRedirect跳转时,地址栏改变,代表客户端重新发送的请求。属于两次请求
- response没有作用域,两次request请求的数据无法共享
- 重定向不能直接传递数据
重定向特点
- 重定向是客户端行为。
- 重定向是浏览器做了至少两次的访问请求。
- 重定向浏览器地址改变。
- 重定向两次跳转之间传输的信息会丢失(request范围)。
- 重定向可以指向任何的资源,包括当前应用程序中的其他资源、同一个站点上的其他应用程序中 的资源、其他站点的资源。
转发、重定向总结
当两个Servlet需要传递数据时,选择forward转发。不建议使用sendRedirect进行传递
请求转发可以携带数据,重定向不能携带数据
代码实现
public class TiaoZhuanServlet extends HttpServlet {
/**
*
* 响应
* Servlet中的跳转:
* 请求转发
* 1、转发是同一次请求
* 2、转发地址栏不会发生改变
* 3、转发可以携带数据
* 请求重定向
* 1、重定向是两次请求
* 2、重定向地址栏会发生改变
* 3、重定向不能携带数据
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
//0、处理乱码
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
//1、处理请求
String username = req.getParameter("username");
String password = req.getParameter("password");
//2、调用业务层方法
if("admin".equals(username) && "123".equals(password)){
//3、做出响应
//重定向到首页
//http://localhost:8080/success.html
//获取项目的访问路径
String contextPath = req.getContextPath();
resp.sendRedirect(contextPath+"/success.html");
}else{
//3、做出响应
//转发到登录页面
//req.getRequestDispatcher("/login.html").forward(req,resp);
//携带数据(域对象(可以携带数据的对象) request、session、application )
req.setAttribute("msg","两瓶茅台");
req.getRequestDispatcher("/fw").forward(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
doGet(req, resp);
}
}
Servlet映射路径配置
使用web.xml配置Servlet
<!-- Servlet映射路径配置 -->
<servlet>
<servlet-name>my</servlet-name>
<servlet-class>com.qf.servlet.MyServlet</servlet-class>
<!-- Servlet配置参数 -->
<init-param>
<param-name>encoding</param-name>
<param-value>gbk</param-value>
</init-param>
<!-- 当前Tomcat容器启动的时候创建Servlet 参数为一个正整数,数值越小优先级越高 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>my</servlet-name>
<!--
映射路径(虚拟路径):
精准匹配: /my /product/add
后缀名匹配:*.do 任意的.do结尾的资源路径可以被匹配
通配符匹配:/* 任意的资源路径,可以被匹配(包括.jsp)
通配符匹配:/ 任意的资源路径,可以被匹配(不包括.jsp)
-->
<url-pattern>/product/add</url-pattern>
</servlet-mapping>
配置的常用属性
- servlet-name: Serlvet名字
- servlet-class: 配置Serlvet类(全限定名)
- url-pattern:配置映射url路径 一个servlet可以配置多个路径,一个路径只能映射一个servlet
- load-on-startup:配置Servlet的创建的时机, 如果是0或者正数 启动程序时创建,如果是负数, 则访问时创建。 数子越小优先级越高。
使用WebServlet注解配置Servlet
@WebServlet("/my2")
public class MyServlet2 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
System.out.println("MyServlet2被执行了");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
doGet(req,resp);
}
}
注解常用属性
- name: Serlvet名字 (可选)
- value: 配置url路径,可以配置多个
- urlPatterns:配置url路径 ,和value作用一样,不能同时使用
- loadOnStartup:配置Servlet的创建的时机, 如果是0或者正数 启动程序时创建,如果是负数, 则访问时创建。 数子越小优先级越高。
Servlet生命周期
(单例设计模式)
生命周期四个阶段
实例化
- 当用户第一次访问Servlet时,由容器调用Servlet的构造器创建具体的Servlet对象。也可以在容器启 动之后立刻创建实例。使用如下代码可以设置Servlet是否在服务器启动时就创建。
- 当当前Servlet第一次访问的时候,Tomacat会创建Servlet对象
- 注意:只执行一次
初始化
- 在初始化阶段,init()方法会被调用。这个方法在javax.servlet.Servlet接口中定义。其中,方法以一个 ServletConfig类型的对象作为参数。
- 当当前Servlet被第一次访问的时候,Tomcat会初始化当前Servlet对象,调用init方法
- 注意:init方法只被执行一次
服务
- 当客户端有一个请求时,容器就会将请求ServletRequest与响应ServletResponse对象转给Servlet, 以参数的形式传给service方法。 、
- 当当前Servlet被访问的时候,会调用service(会根据请求方式去调用doget或者是dopost)
- 此方法会执行多次
销毁
- 当Servlet容器停止或者重新启动都会引起销毁Servlet对象并调用destroy方法。
- 当Tomcat容器关闭的时候会销毁当前Servlet
- destroy方法执行一次
@WebServlet(value = "/life",loadOnStartup = 10)
public class LifeServlet extends HttpServlet /*implements SingleThreadModel*/{
public LifeServlet(){
System.out.println("Servlet被创建");
}
@Override
public void init(ServletConfig config) throws ServletException {
System.out.println("Servlet被初始化");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
System.out.println(Thread.currentThread().getName()+"Servlet被执行");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
doGet(req, resp);
}
@Override
public void destroy() {
System.out.println("Servlet被销毁");
}
}
Servlet特性
Serlvet 特征
Servlet是单例多线程的
- 单例: 生命周期
- 多线程:多线程的效率更高
线程安全问题
Servlet在访问之后,会执行实例化操作,创建一个Servlet对象。而我们Tomcat容器可以同时多个线 程并发访问同一个Servlet,如果在方法中对成员变量做修改操作,就会有线程安全的问题。
如何保证线程安全
- synchronized
- 将存在线程安全问题的代码放到同步代码块中
- 实现SingleThreadModel接口
- 实现SingleThreadModel接口后,每个线程都会创建servlet实例,这样每个客户端 请求就不存在共享资源的问题,但是servlet响应客户端请求的效率太低,所以已经淘汰。
- 尽可能使用局部变量,不要使用成员变量
- 多线程栈中独立,堆中共享
代码实现
@WebServlet(value = "/life",loadOnStartup = 10)
public class LifeServlet extends HttpServlet /*implements SingleThreadModel*/{
String msg;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
System.out.println(Thread.currentThread().getName()+"Servlet被执行");
// synchronized (this){
// msg = req.getParameter("msg");
// try {
// Thread.sleep(5000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// resp.getWriter().write(msg);
// }
String msg = req.getParameter("msg");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
resp.getWriter().write(msg);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
doGet(req, resp);
}
}
状态管理
现有问题
- HTTP协议是无状态的,不能保存每次提交的信息
- 如果用户发来一个新的请求,服务器无法知道它是否与上次的请求有联系。
- 对于那些需要多次提交数据才能完成的Web操作,比如登录来说,就成问题了。
概念
将浏览器与web服务器之间多次交互当作一个整体来处理,并且将多次交互所涉及的数据(即状态) 保存下来。
状态管理分类
- 客户端状态管理技术:将状态保存在客户端。代表性的是Cookie技术。
- 服务器状态管理技术:将状态保存在服务器端。代表性的是session技术(服务器传递sessionID 时需要使用Cookie的方式)
Cookie的使用
什么是Cookie
- Cookie是在浏览器访问Web服务器的某个资源时,由Web服务器在HTTP响应消息头中附带传送 给浏览器的一小段数据。
- 一旦Web浏览器保存了某个Cookie,那么它在以后每次访问该Web服务器时,都应在HTTP请求 头中将这个Cookie回传给Web服务器。
- 一个Cookie主要由标识该信息的名称(name)和值(value)组成。
Cookie原理 |
---|
创建Cookie
//1、创建Cookie对象
Cookie cookie = new Cookie("msg","hello:cookie");
//2、设置Cookie
//设置Cookie保存路径,注意:如果不设置Cookie保存路径,Cookie对象获取的时候必须要与保存Cookie的
路径要一致
cookie.setPath("/");
//设置Cookie的有效时间(单位:秒)
cookie.setMaxAge(60*60*24);
//3、通过resp对象添加Cookie响应给客户端
resp.addCookie(cookie);
获取cookie
//获取所有的Cookie
Cookie[] cookies = req.getCookies();
for (Cookie cookie : cookies) {
//获取指定的Cookie对象
if(cookie.getName().equals("msg")){
System.out.println(cookie.getValue());
}
}
删除Cookie
//获取所有的Cookie
Cookie[] cookies = req.getCookies();
for (Cookie cookie : cookies) {
//获取msg这个Cookie
if("msg".equals(cookie.getName())){
cookie.setValue("");
//设置Cookie的保存位置 注意:要与删除的session位置一致
cookie.setPath("/");
//设置Cookie立即失效
cookie.setMaxAge(0);
//将Cookie对象响应给客户端浏览器 不要忘记了
resp.addCookie(cookie);
}
}
Cookie编码与解码
Cookie默认不支持中文,只能包含ASCII字符,所以Cookie需要对Unicode字符进行编码,否则会出 现乱码。
- 编码可以使用java.net.URLEncoder类的encode(String str,String encoding)方法
- 解码使用java.net.URLDecoder类的decode(String str,String encoding)方法
//存储中文的Cookie信息需要编码
Cookie cookie = new Cookie(URLEncoder.encode("名字","utf-8"),
URLEncoder.encode("尼古拉斯","utf-8"));
//获取中文Cookie信息需要解码
System.out.println(URLDecoder.decode(cookie.getName(),"utf-8"));
System.out.println(URLDecoder.decode(cookie.getValue(),"utf-8"));
Cookie优点和缺点
优点
- 可配置到期规则。
- 简单性:Cookie 是一种基于文本的轻量结构,包含简单的键值对。
- 数据持久性:Cookie默认在过期之前是可以一直存在客户端浏览器上的。
缺点
- 大小受到限制:大多数浏览器对 Cookie 的大小有 4K、8K字节的限制。
- 用户配置为禁用:有些用户禁用了浏览器或客户端设备接收 Cookie 的能力,因此限制了这一功 能。
- 潜在的安全风险:Cookie 可能会被篡改。会对安全性造成潜在风险或者导致依赖于Cookie 的应 用程序失败。
Session
Session概述
- Session用于记录用户的状态。Session指的是在一段时间内,单个客户端与Web服务器的一连 串相关的交互过程。
- 在一个Session中,客户可能会多次请求访问同一个资源,也有可能请求访问各种不同的服务器 资源。
Session原理
- 服务器会为每一次会话分配一个Session对象
- 同一个浏览器发起的多次请求,同属于一次会话(Session)
- 首次使用到Session时,服务器会自动创建Session,并创建Cookie存储SessionId发送回客户端
注意:session是由服务端创建的。
Session原理 |
---|
Session使用
Session作用域:拥有存储数据的空间,作用范围是一次会话有效
- 一次会话是使用同一浏览器发送的多次请求。一旦浏览器关闭,则结束会话
- 可以将数据存入Session中,在一次会话的任意位置进行获取
- 可传递任何数据(基本数据类型、对象、集合、数组)
session储存数据
//1、获取Session对象
HttpSession session = req.getSession();
//设置session的有效时间(单位:秒)
//session.setMaxInactiveInterval(50);
//2、通过session保存数据(状态)
session.setAttribute("msg","hello,session");
seesion获取数据
//1、获取Session对象
HttpSession session = req.getSession();
//2、通过session获取数据(状态)
System.out.println(session.getAttribute("msg"));
清除session
//1、获取Session对象
HttpSession session = req.getSession();
//2、通过session删除数据(状态)
//session.removeAttribute("msg");
//使session失效
session.invalidate();
Session的生命周期
-
开始:第一次使用到Session的请求产生,则创建Session
-
结束:
- 浏览器关闭,则失效
- Session超时,则失效
- session.setMaxInactiveInterval(seconds);//设置最大有效时间(单位:秒)
- 手工销毁,则失效
- session.invalidate();//登录退出、注销
-
设置全局Session的有效时间
-
<!-- 配置全局session有效时间 单位:分 --> <session-config> <session-timeout>10</session-timeout> </session-config>
-
ServletContext对象
ServletContext概述
- 全局对象,也拥有作用域,对应一个Tomcat中的Web应用
- 当Web服务器启动时,会为每一个Web应用程序创建一块共享的存储区域 (ServletContext)。
- ServletContext在Web服务器启动时创建,服务器关闭时销毁。
获取ServletContext对象
- GenericServlet提供了getServletContext()方法。(推荐) this.getServletContext();
- HttpServletRequest提供了getServletContext()方法。(推荐)
- HttpSession提供了getServletContext()方法。
ServletContext作用
获取项目真实路径
//获取当前项目在服务器发布的真实路径
String realpath=servletContext.getRealPath("/");
获取项目上下文路径
System.out.println(servletContext.getContextPath());//上下文路径(应用程序名称)
System.out.println(request.getContextPath());//上下文路径(应用程序名称)
全局容器
- ServletContext拥有作用域,可以存储数据到全局容器中
- 存储数据:servletContext.setAttribute(“name”,value);
- 获取数据:servletContext.getAttribute(“name”);
- 移除数据:servletContext.removeAttribute(“name”);
ServletContext特点
- 唯一性: 一个应用对应一个ServletContext。
- 生命周期: 只要容器不关闭或者应用不卸载,ServletContext就一直存在。
Servlet中的作用域
作用域总结
- HttpServletRequest:一次请求,请求响应之前有效
- HttpSession:一次会话开始,浏览器不关闭或不超时之前有效
- ServletContext:服务器启动开始,服务器停止之前有效
过滤器
现有问题
在以往的Servlet中,有没有冗余的代码,多个Servlet都要进行编写。
概念
过滤器(Filter)是处于客户端与服务器目标资源之间的一道过滤技术。
原理
过滤器工作流程 |
---|
过滤器作用
- 执行地位在Servlet之前,客户端发送请求时,会先经过Filter,再到达目标Servlet中;响应时, 会根据执行流程再次反向执行Filter
- 可以解决多个Servlet共性代码的冗余问题(例如:乱码处理、登录验证)
过滤器实现
- 编写Java类实现Filter接口
- 在doFilter方法中编写拦截逻辑
- 设置拦截路径
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilter初始化了");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse
servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("MyFilter执行过滤了");
//放行请求的资源
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
System.out.println("MyFilter销毁了");
}
}
过滤器配置
-
注解配置
- 在自定义的Filter类上使用注解@WebFilter(value=“/过滤目标资源”)
-
xml配置
-
<!--过滤器的xml配置 --> <filter> <filter-name>myFilter</filter-name> <filter-class>com.qf.filter.MyFilter</filter-class> </filter> <filter-mapping> <filter-name>myFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
-
-
过滤器路径
-
<!-- 过滤器的过滤路径通常有三种形式: 精确过滤匹配 ,比如/index.jsp /myservlet1 后缀过滤匹配,比如*.jsp、*.html、*.jpg 通配符过滤匹配/*,表示拦截所有。 注意过滤器不能使用/匹配。 -->
-
-
注解配置
-
@WebFilter("/*")
-
过滤器总结
在一个Web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。 优先级:
- 如果为注解的话,是按照类全名称的字符串顺序决定作用顺序
- 如果web.xml,按照 filter-mapping注册顺序,从上往下
- web.xml配置高于注解方式
- 如果注解和web.xml同时配置,会创建多个过滤器对象,造成过滤多次。
过滤器应用
编码过滤器
@WebFilter("/*")
public class EncodingFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse
servletResponse, FilterChain filterChain) throws IOException, ServletException {
//处理请求与响应乱码
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=utf-8");
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
}
登录验证过滤器
@WebFilter("*.do")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse
servletResponse, FilterChain filterChain) throws IOException, ServletException {
/**
* 1、从session中获取User对象
* 2、判断User对象是否为null
* 如果为null则跳转到登录页面
* 否则放行
*/
//将servletRequest转成HttpServletRequest对象
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//登录验证
HttpSession session = request.getSession();
Object user = session.getAttribute("userLogin");
if(user == null){
response.sendRedirect(request.getContextPath()+"/login.html");
}else{
filterChain.doFilter(request,response);
}
}
}
自动登录过滤器
@WebFilter("/login.html")
public class AutoLoginFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse
servletResponse, FilterChain filterChain) throws IOException, ServletException {
//将servletRequest转成HttpServletRequest对象
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String userinfo = CookieUtils.getCookie("userinfo", request);
if(userinfo != null){
//表示要自动登录,在session存储登录状态,跳转到首页
UserService userService = new UserServiceImpl();
User user = userService.login(userinfo.split(":")[0],
userinfo.split(":")[1]);
request.getSession().setAttribute("userLogin",user);
response.sendRedirect(request.getContextPath()+"/empAll.do");
}else{
filterChain.doFilter(request,response);
}
}
}
监听器
JavaServlet规范包括三个技术点:servlet ;listener ;filter; 监听器就是监听某个对象的的状态变化的组件。
监听器的相关概念事件源:
- 被监听的对象(三个域对象 request,session,servletContext)
- 监听器:监听事件源对象, 事件源对象的状态的变化都会触发监听器
- 注册监听器:将监听器与事件源进行绑定
- 响应行为:监听器监听到事件源的状态变化时,所涉及的功能代码(程序员编写代码)
监听器的分类
ServletContext域 | HttpSession域 | ServletContext域 | |
---|---|---|---|
域 对 象 创 建 和 销 毁 | ServletContextListener | HttpSessionListener | ServletRequestListener |
域 对 象 属 性 的 变 化 | ServletContextAttributeListener | HttpSessionAttributeListener | ServletRequestAttributeListener |
ServletContextListener监听器
- 编写一个监听器类去实现监听器接口
- 覆盖监听器的方法
- 需要在web.xml中进行配置(注册)或者使用注解
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext被创建的时候执行。。。。");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContext被销毁的时候执行。。。。");
}
}
<!-- 注册监听器 -->
<listener>
<listener-class>com.qf.listener.MyServletContextListener</listener-class>
</listener>
ServletContextAttributeListener监听器
@WebListener
public class MyServletContextAttributeListener implements
ServletContextAttributeListener {
@Override
public void attributeAdded(ServletContextAttributeEvent scae) {
System.out.println("有人向ServletContext域中添加了属性"+scae.getName()+"---
>"+scae.getValue());
}
@Override
public void attributeRemoved(ServletContextAttributeEvent scae) {
System.out.println("有人向ServletContext域中移除了属性"+scae.getName()+"---
>"+scae.getValue());
}
@Override
public void attributeReplaced(ServletContextAttributeEvent scae) {
System.out.println("有人向ServletContext域中修改了属性"+scae.getName()+"---
>"+scae.getValue());
}
}