Servlet总结

软件架构

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.xmlserver.xml:配置整个服务器信息。例如 修改端口号,编码格式等。 web.xml:项目部署描述符文件,这个文 件中注册了很多MIME类型,即文档类型。
libTomcat的类库,里面存放Tomcat运行 所需要的jar文件。
logs存放日志文件,记录了Tomcat启动和关 闭的信息,如果启动Tomcat时有错误, 异常也会记录在日志文件中。
tempTomcat的临时文件,这个目录下的东西 在停止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、响应正文

请求报文
在这里插入图片描述
响应报文
在这里插入图片描述

常见状态码

状态 代码状态描述说明
200OK客户端请求成功
302Found临时重定向
403Forbidden服务器收到请求,但是拒绝提供服务。服务器通常会在响应正文中给 出不提供服务的原因
404Not Found请求的资源不存在,例如,输入了错误的URL。
500Internal 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域
域 对 象 创 建 和 销 毁ServletContextListenerHttpSessionListenerServletRequestListener
域 对 象 属 性 的 变 化ServletContextAttributeListenerHttpSessionAttributeListenerServletRequestAttributeListener

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());
}
}
  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是二次元穿越来的

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值