Servlet
1.Tomcat服务器
1.1 介绍
软件分为B/S架构与C/S架构,而我们学习的是B/S架构
C/S(客户端/服务端)架构如:QQ、微信、抖音等
特点:
1、使用前必须安装。
2、更新的时候,服务器和客户端必须同时更新。
3、不能跨平台使用
4、客户端和服务器通信采用的是自有协议,相对来说比较安全。B/S(浏览器/服务端)本质上也是C/S,只不过B/S架构的软件,使用浏览器作为客户端。B/S架构软件通过使用浏览器访问网页的形式,来使用软件。比如:淘宝、微博、百度等
特点:
1、软件不需要安装,直接访问指定的网址即可。
2、软件更新时客户端不需要更新。
3、软件可以跨平台,只要系统中有浏览器就可以使用。常见的web应用服务器
Tomcat:Apache开源组织下的,免费
webLogic:是Oracle,收费
webSphere:是IBM的,收费
Nginx:后面要学习的,免费的,一般用于负载均衡
Apache:Apache开源组织下的,免费
web资源
在web应用服务器上可以提供给外界访问的资源就是web资源,比如:html,css,图片,mp3,......
动态资源:web页面中供人浏览的数据,由程序产生的,比如:jsp,php,Servlet
静态资源:web页面中供人浏览的数据,不变的。比如:html,css,图片,mp3,......

1.2 下载与安装Tomcat
Tomcat下载页面地址:
Tomcat与JDK版本对应关系

下载页的说明

1.3 tomcat目录结构说明

bin 存放二进制文件,脚本文件的目录,运行tomcat的脚本也在里面
startup.bat 启动tomcat的批处理文件
shutdown.bat 停止tomcat的批处理文件
conf 配置文件的目录
logging.properties 日志的配置文件
server.xml 核心配置文件
tomcat-users.xml 用户权限配置文件
web.xml 全局的项目默认配置文件,局部只对单个项目有效,全局对所有项目有效
lib tomcat依赖的jar以及tomcat提供的API存放的目录
logs 存放日志的目录
temp 临时文件存放的目录
webapps web应用发布的目录
work tomcat处理jsp的工作目录
1.4 启动tomcat
双击D:\apache-tomcat-8.5.82\bin\startup.bat,就会出现一个黑窗口,如果黑窗口出现闪退情况,证明启动失败。
启动成功后,你可以在浏览器上访问http://localhost:8080/,回出现下图效果

启动出现闪退的常见原因:
1.没有正确的配置JDK的环境变量,如果你没有配置JAVA_HOME这个环境变量,那么就会启动闪退
2.端口冲突,tomcat默认使用的是8080端口,如果8080端口被其他的程序所占用,那么tomcat会启动失败
1.5 eclipse关联tomcat








2.HTTP协议
超文本传输协议(HTTP),它是一个简单的请求-响应协议,它通常运行在TCP之上,它是TCP/IP的子协议。
HTTP协议由HTTP请求和HTTP响应组成。
HTTP协议的特点:
1.基于请求与响应模型的协议,请求和响应是成对出现的。
2.此协议的默认端口为80。
HTTP请求包括:请求行,请求头,请求体
HTTP响应包括:响应行,响应头,响应体
HTTP协议的版本
HTTP/1.0 发送请求的时候,创建一次连接,获得一个web资源,短连接
HTTP/1.1 发送请求的时候,创建一次连接,获得多个web资源,长连接
当你通过域名访问网站的时候,其实就是你利用浏览器向网站所在服务器发送了一次请求


请求头的参数说明:
请求头是客户端发送给服务端的一些信息,这些信息是以key-value形式成对出现的
Accept:浏览器可以支持的MIME类型,文件类型的一种描述方式
Accept-Encoding:浏览器支持的数据压缩格式,这里是gzip
Accept-Language:浏览器支持的语言,zh-CN代表中文
Connection:连接状态,上图的keep-alive代表是连接中的,如果是close就是关闭状态
Content-Length:请求体的长度
Content-Type:请求数据的类型
Host:请求的服务端的主机名和端口
Origin:域名地址
Referer:当前请求来自何处,可以利用它做防盗链
Use-Agent:发送请求的浏览器得相关信息
当网站所在服务器接收到了客户端(浏览器)所发送的请求后,就会响应数据给客户端(浏览器)


3.HTTP常见状态码
状态码在请求行或者响应行中看,比如:HTTP/1.1 200 OK,这里的200就是状态机
常见的状态码:
200 请求成功
302 请求重定向
304 请求资源没有改变,访问本地缓存
404 请求的资源找不到或者压根就不存在
500 服务器内部错误,一般是代码出问题了
更详细的状态码,请参考:https://www.runoob.com/http/http-status-codes.html

4.创建web工程




5.Servlet介绍
Servlet是运行在服务端的Java小程序,是Sun公司提供的一套规范(接口),用于处理客户端请求以及响应的动态资源
Servlet规范主要包含了三大技术点:
Servlet技术
Filter过滤器技术
Listener监听器技术
一般我们创建一个类然后继承HttpServlet,并覆盖doGet和doPost方法。
你在eclipse中创建了一个名叫Demo的web工程,然后你Run on Server后,项目就会被发布到Tomcat安装目录下的webapps目录里面。webapps目录里面的工程的结构:
Demo文件夹
META-INF文件夹
WEB-INF文件夹
classes文件夹(存放着你写的Java代码编译出来的字节码文件)
、 lib文件夹(存放第三方依赖Jar包的,放在这里的Jar会自动导入,不用build path了)
web.xml文件(配置Servlet的配置文件)
一些静态资源......
注意:WEB-INF目录是一个受保护的目录。不能被外界直接访问
6.创建Servlet的方式



7.设置Servlet的模板


模板代码
package ${enclosing_package};
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ${primary_type_name} extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("DarkSnow...");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
你在新建完Servlet之后,清除掉原来的内容,然后输入servlet,再按alt + /即可生成代码
8.Servlet的生命周期(重要)
Servlet接口中的方法:
1.public void init(ServletConfig config) 默认只执行一次
(1)什么时候执行?
Servlet对象创建的时候执行
(2)Servlet对象什么时候创建?
默认第一次访问Servlet时创建
(3)ServletConfig代表的是该Servlet对象的配置信息(web.xml的信息)
2.public void service(ServletRequest req, ServletResponse res)
(1)什么时候执行?
每次请求的时候都会执行(每次访问的时候)
(2)ServletRequest
代表请求对象,内部封装了HTTP请求的数据信息
(3)ServletResponse
代表响应对象,内部封装了HTTP响应的数据信息
3.public void destroy()
(1)什么时候执行?
Servlet销毁的时候执行
(2)Servlet什么时候销毁?
服务器停止时销毁


9.ServletConfig对象
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>Demo</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<servlet>
<description></description>
<display-name>MyServletSoft</display-name>
<servlet-name>MyServletSoft</servlet-name>
<servlet-class>com.darksnow.MyServletSoft</servlet-class>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://test</param-value>
</init-param>
<init-param>
<param-name>name</param-name>
<param-value>darksnow</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>MyServletSoft</servlet-name>
<url-pattern>/darksnow</url-pattern>
</servlet-mapping>
</web-app>
我们在public void init(ServletConfig config)中能够看到有一个ServletConfig对象,此对象是Tomcat帮你创建的,所以你可以直接用。
package com.darksnow;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServletSoft extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
@Override
public void destroy() {
}
@Override
public void init(ServletConfig config) throws ServletException {
//ServletConfig对象是Servlet配置对象
//获取了web.xml中的<servlet-name>MyServletSoft</servlet-name>
String servletName = config.getServletName();
System.out.println(servletName);
//根据Servlet的初始化参数name(<param-name>url</param-name>)来获取对应value
String initParameter = config.getInitParameter("url");
System.out.println(initParameter);
//获取Servlet的所有初始化参数
Enumeration<String> initParameterNames = config.getInitParameterNames();
while(initParameterNames.hasMoreElements()) {
String key = initParameterNames.nextElement();
String value = config.getInitParameter(key);
System.out.println(key + "---" + value);
}
//通过ServletConfig可以获得ServletContext对象,也叫Servlet上下文对象
ServletContext servletContext = config.getServletContext();
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public String getServletInfo() {
return null;
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("DarkSnow...");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
10.web.xml的配置
<!--
欢迎页面的设置
假设我访问http://localhost:8080/Demo/
如果Demo项目里面有下面所示名称的文件,那么就可以有页面显示
否则报404
-->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
Servlet默认第一次访问时创建,但是加上下面这个配置,Servlet对象就会在Tomcat服务器启动时就创建。
数字代表优先级,数字越小优先级越高!
注意:该配置项写在<init-param>配置后面,如果没有配置<init-param>,则写在<servlet-class>配置后面
<load-on-startup>2</load-on-startup>
11.ServletContext
ServletContext也叫做上下文对象。
如何获取ServletContext对象?
1.通过ServletConfig对象获取(具体参考ServletConfig的笔记)
2.通过this.getServletContext();的方式获取,
主要是因为你写的Servlet会从GenericServlet将getServletContext方法给继承过来。
本质上还是通过ServletConfig对象调用的。
注意:如果你是通过第二种方式获取ServletContext对象,要不你就不重写init方法,要不你就重写不带参数的init方法,如果你要从写init(ServletConfig config)方法,那么在你的方法体中的第一行一定要写上super.init(config); 否则会报空指针异常
ServletContext的作用:
(1)可以获取web应用的全局初始化参数,在web.xml中配置(此配置可以写在web-app标签内部的任意子位置)
<context-param>
<param-name>driver</param-name>
<param-value>com.mysql.jdbc.Driver</param-value>
</context-param>
// 获取ServletContext上下文对象
ServletContext servletContext = this.getServletContext();
// 获取全局的初始化参数
String value = servletContext.getInitParameter("driver");
System.out.println(value);
context-param与init-param的区别:
context-param中设置的初始化值可以被所有的Servlet获取
init-param中设置的初始化值只能被单个Servlet获取
(2)可以获取web应用中任何资源的绝对路径
getRealPath()里面传入的参数为你要获取的资源的相对路径
相对于该web应用

(3)ServletContext的域对象
存储数据的区域对象
ServletContext域对象的作用范围:
作用于整个Web应用,也就意味着这个web应用下任何的Servlet都可以向ServletContext域中存取数据,以达到一个数据共享的效果。
相关操作:
package com.darksnow;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletOne extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
//向ServletContext域中存储key-value(键值对)形式的数据
servletContext.setAttribute("name", "Mark");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
package com.darksnow;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletTwo extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
//通过key来获取ServletContext域对象中存储的值
String value = (String) servletContext.getAttribute("name");
response.getWriter().write(value);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
package com.darksnow;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletThree extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
//通过key来移除ServletContext域对象中存储的值
servletContext.removeAttribute("name");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
12.response响应对象
12.1 设置响应的状态码
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置状态码
response.setStatus(501);
response.getWriter().write("DarkSnow...");
}

(2)设置响应头
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//添加了一个响应头
//response.addIntHeader("count", 1314520);
//response.addDateHeader("MyDate", new Date().getTime());
//修改响应头
//response.setIntHeader("", 0);
//response.setDateHeader("", 0L);
//上面的只是给你用来体验的,下面才是真正你要用的
response.addHeader("MyServer", "Tomcat");
response.setHeader("Content-Length", "19");
//总结:带add是添加,带set的是修改
}

定时页面跳转
package com.darksnow.api;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class RefreshServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置刷新时间的头 里面的5代表5秒
response.setHeader("refresh", "5;url=http://www.baidu.com");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
window.onload = function(){
var time = 4;
var secondElement = document.getElementById("second");
var timer = setInterval(function(){
secondElement.innerHTML = time;
--time;
//如果时间为0了,定时器任务就应该停止
if(time == 0){
clearInterval(timer);
location.href = "http://www.baidu.com";
}
},1000);
}
</script>
</head>
<body>
恭喜您,注册成功,<span style="color:red" id="second">5</span>秒后跳转,如果不跳转点击<a href="http://www.baidu.com">这里</a>!
</body>
</html>
12.2 可以做重定向
//加上访问MyServletOne重定向到MyServletTwo
public class MyServletOne extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("one...");
//1.设置状态码为302
//response.setStatus(302);
//2.设置响应头Location(此响应头的值的格式:/项目名/资源名)
//response.setHeader("Location", "/response_demo/two");
//HttpServletResponse对象中内部封装了一个用于重定向的方法 项目名+资源名
response.sendRedirect("/response_demo/two");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
public class MyServletTwo extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().write("two...");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
重定向的特点:
(1) 请求了两次
(2) 地址栏发生了变化

12.3 页面内容打印
package com.darksnow.api;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TextServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 设置response缓冲区的编码 写在第一行
* 但是我们发现设置了也还是会乱码,因为我们的系统是中文环境的,所以浏览器默认用的是GB2312编码
* 所以我们不用此方法
*/
//response.setCharacterEncoding("UTF-8");
//我们可以通过Content-Type响应头来告诉客户端我们到底使用什么编码
//response.setHeader("Content-Type", "text/html;charset=UTF-8");
//最终解决方案:Servlet给你提供了相关API(原理和设置响应头一样)
response.setContentType("text/html;charset=UTF-8");
//向页面写一个内容
//这里本质上用的是I/O流
response.getWriter().write("<h1>DarkSnow...</h1>"); //显示正常
/*
* 中文乱码原因:
* 内容其实在response缓冲区内,但是这个缓冲区的默认编码为ISO-8859-1
* 然而这编码表中没有中文,所以就乱码了。
*/
response.getWriter().write("真热啊..."); //乱码
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
12.4 getOutputStream方法案例
package com.darksnow.api;
import java.io.FileInputStream;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*
* 写一张图片
*/
public class ImgServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//这是一个字节流,该字节流可以向response缓冲区写入字节,再由tomcat引擎将字节的内容与其他信息封装成HTTP响应返回给浏览器
ServletOutputStream out = response.getOutputStream();
//首先得获取服务器上的图片路径 ---> 绝对路径
String realPath = this.getServletContext().getRealPath("22.png");
FileInputStream in = new FileInputStream(realPath);
int len = 0;
byte[] buffer = new byte[1024];
while((len = in.read(buffer))>0) {
out.write(buffer,0,len);
}
in.close();
//查看response缓存区的大小
System.out.println(response.getBufferSize()); //8192Byte 1KB=1024Byte
//设置response缓冲区的大小
//response.setBufferSize(10240);
/*
* 关于getOutputStream的相关注意事项
* 1.response获取的流不用手动关闭,tomcat会去关闭
* 2.getOutputStream方法与getWriter方法不能同时调用
* 3.response缓冲区默认大小为8KB,它存在自动扩容机制,无需你关心
*/
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
12.5 文件下载案例
前端
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>欢迎来到资源下载小站</h1>
<a href="/response_demo/download/apache-tomcat-8.5.82.zip">下载Tomcat源码</a>
<br>
<a href="/response_demo/download/girl.png">下载美女图片</a>
<br>
<a href="/response_demo/download/1.txt">下载文档</a>
<br>
<a href="/response_demo/download?filename=girl.png">真正的下载美女图片一</a>
<br>
<a href="/response_demo/download?filename=美女.jpeg">真正的下载美女图片二</a>
<br>
<a href="/response_demo/download?filename=1.txt">真正的下载文档</a>
</body>
</html>
后端
package com.darksnow.download;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DownloadServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取要下载的文件的名称
String filename = request.getParameter("filename");
//要下载的文件的类型 客户端通过文件的MIME类型去区分文件类型(Tomcat安装目录的conf/web.xml里面去看)
response.setContentType(this.getServletContext().getMimeType(filename));
//获取要下载的文件的绝对路径
String path = this.getServletContext().getRealPath("download/" + filename);
//解决文件名中文乱码问题,不同浏览器解决方式还不一样
//怎么去根据不同的浏览器设置不同的解决方案呢?怎么去区分浏览器,根据浏览器的内核来区分
//获取请求头中的User-Agent
String agent = request.getHeader("User-Agent");
//根据不同的浏览器进行不同的编码
String filenameEncoder = "";
if(agent.contains("AppleWebKit")){
//适用于谷歌浏览器和微软的Edge浏览器
filenameEncoder = URLEncoder.encode(filename,"UTF-8");
}else {
//适用于火狐浏览器
filenameEncoder = new String(filename.getBytes("GBK"),"ISO-8858-1");
}
//你要告诉客户端,文件不是直接取解析,而是以附件的形式打开(下载)
response.setHeader("Content-Disposition", "attachment;filename=" + filenameEncoder);
//获得该文件的输入流
InputStream in = new FileInputStream(path);
//获得文件输出流 --- 通过response获得输出流
ServletOutputStream out = response.getOutputStream();
//边读边写
int len = 0;
byte[] buffer = new byte[1024];
while((len = in.read(buffer)) > 0) {
out.write(buffer,0,len);
}
//关闭资源
in.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
12.6 图片验证码案例
servlet
package com.darksnow.check;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 验证码生成程序
*/
public class CheckImgServlet extends HttpServlet {
// 集合中保存所有成语
private List<String> words = new ArrayList<String>();
@Override
public void init() throws ServletException {
// 初始化阶段,读取new_words.txt
// web工程中读取 文件,必须使用绝对磁盘路径
// new_words.txt存放在webapp目录下,此文件的内容为一行一个四字成语
String path = getServletContext().getRealPath("new_words.txt");
try {
BufferedReader reader = new BufferedReader(new FileReader(path));
String line;
while ((line = reader.readLine()) != null) {
words.add(line);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 禁止缓存
// response.setHeader("Cache-Control", "no-cache");
// response.setHeader("Pragma", "no-cache");
// response.setDateHeader("Expires", -1);
int width = 120;
int height = 30;
// 步骤一 绘制一张内存中图片
BufferedImage bufferedImage = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
// 步骤二 图片绘制背景颜色 ---通过绘图对象
Graphics graphics = bufferedImage.getGraphics();// 得到画图对象 --- 画笔
// 绘制任何图形之前 都必须指定一个颜色
graphics.setColor(getRandColor(200, 250));
graphics.fillRect(0, 0, width, height);
// 步骤三 绘制边框
graphics.setColor(Color.WHITE);
graphics.drawRect(0, 0, width - 1, height - 1);
// 步骤四 四个随机数字
Graphics2D graphics2d = (Graphics2D) graphics;
// 设置输出字体
graphics2d.setFont(new Font("宋体", Font.BOLD, 18));
Random random = new Random();// 生成随机数
int index = random.nextInt(words.size());
String word = words.get(index);// 获得成语
System.out.println(word);
// 定义x坐标
int x = 10;
for (int i = 0; i < word.length(); i++) {
// 随机颜色
graphics2d.setColor(new Color(20 + random.nextInt(110), 20 + random
.nextInt(110), 20 + random.nextInt(110)));
// 旋转 -30 --- 30度
int jiaodu = random.nextInt(60) - 30;
// 换算弧度
double theta = jiaodu * Math.PI / 180;
// 获得字母数字
char c = word.charAt(i);
// 将c 输出到图片
graphics2d.rotate(theta, x, 20);
graphics2d.drawString(String.valueOf(c), x, 20);
graphics2d.rotate(-theta, x, 20);
x += 30;
}
// 将验证码内容保存session
request.getSession().setAttribute("checkcode_session", word);
// 步骤五 绘制干扰线
graphics.setColor(getRandColor(160, 200));
int x1;
int x2;
int y1;
int y2;
for (int i = 0; i < 30; i++) {
x1 = random.nextInt(width);
x2 = random.nextInt(12);
y1 = random.nextInt(height);
y2 = random.nextInt(12);
graphics.drawLine(x1, y1, x1 + x2, x2 + y2);
}
// 将上面图片输出到浏览器 ImageIO
graphics.dispose();// 释放资源
//将图片写到response.getOutputStream()中
ImageIO.write(bufferedImage, "jpg", response.getOutputStream());
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
/**
* 取其某一范围的color
*
* @param fc
* int 范围参数1
* @param bc
* int 范围参数2
* @return Color
*/
private Color getRandColor(int fc, int bc) {
// 取其随机颜色
Random random = new Random();
if (fc > 255) {
fc = 255;
}
if (bc > 255) {
bc = 255;
}
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
}
前端页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
function checkImg(obj){
obj.src = "/response_demo/checkImg?time=" + new Date().getTime();
console.log(obj);
}
</script>
</head>
<body>
<form action="#" method="post">
账号:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
验证:<input type="text" name="check">
<!-- 这里的this(当前对象)其实就是img这个文档对象 -->
<img onclick="checkImg(this)" src="/response_demo/checkImg"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
12.7 登陆案例
需求
做一个登录功能,输入账号密码登录,如果登录成功(验证数据库),那么就在页面显示"恭喜xxx登陆成功,这是您第x次成功登录!",如果登录失败,那么就在页面上显示"抱歉您的账号或密码错误"
前端页面login.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/response_demo/login" method="post">
账号:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
C3P0工具类
package com.darksnow.utils;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
/**
* C3P0的相关工具类
*
* 你可以把数据源看成是连接池
*/
public class C3P0Util {
//私有构造函数,不让其创建对象
private C3P0Util() {
}
/*
* 这里ComboPooledDataSource的构造函数传入了一个"darksnow"的字符串参数,那么就会去使用c3p0-config.xml配置文件中的
* <named-config name="darksnow">下面的配置
*
* 如果ComboPooledDataSource用的是无参构造,那么就会去使用c3p0-config.xml配置文件中的
* <default-config>下面的配置
*/
// private static ComboPooledDataSource dataSource = new ComboPooledDataSource("darksnow");
private static ComboPooledDataSource dataSource = new ComboPooledDataSource(); //设置数据源
/**
* javax.sql.DataSource 是Java提供的原生接口,各大厂商实现连接池需要实现这个接口
* 获取数据源的方法
*/
public static DataSource getDataSource() {
return dataSource;
}
/**
* 获取连接的方法
*/
public static Connection getConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
User实体类
package com.darksnow.bean;
/**
* 这就是JavaBean 对象实体
* 此对象的成员变量名和数据库的字段名是一一对应的,数据类型也是对应的
*
* 一个实体对象基本上对应一条数据
*/
public class User {
private int id;
private String username;
private String password;
private String name;
public User() {
}
public User(int id, String username, String password, String name) {
this.id = id;
this.username = username;
this.password = password;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password=" + password + ", name=" + name + "]";
}
}
具体处理业务的Servlet
package com.darksnow.login;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.Properties;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import com.darksnow.bean.User;
import com.darksnow.utils.C3P0Util;
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
@Override
public void init() throws ServletException {
try {
//加载count.properties文件得到一个字节输入流
InputStream in = LoginServlet.class.getClassLoader().getResourceAsStream("count.properties");
//创建Properties对象
Properties properties = new Properties();
//通过字节输入流将count.properties文件的内容加载到Properties对象中
properties.load(in);
//通过key从Properties对象中获取对应的值
int count = Integer.parseInt(properties.getProperty("count"));
//创建ServletContext上下文对象,并且向域中存入登陆次数
this.getServletContext().setAttribute("count", count);
} catch (IOException e) {
e.printStackTrace();
}
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//处理response缓冲区中文乱码问题
response.setContentType("text/html;charset=UTF-8");
//登录页面输入完账号密码,然后点击登录以后,就将账号密码数据提交给了LoginServlet来处理
//所以我们首先需要获取,前端页面提交过来的数据(账号密码)
String username = request.getParameter("username");
String password = request.getParameter("password");
//去数据库中校验账号密码是否正确
QueryRunner queryRunner = new QueryRunner(C3P0Util.getDataSource());
String sql = "select * from t_user where username=? and password=?";
try {
User user = queryRunner.query(sql, new BeanHandler<User>(User.class),username,password);
//根据SQL的返回结果来显示不同的提示信息
if(user != null) {
//User对象不为空意味着账号密码正确
//创建ServletContext上下文对象
ServletContext servletContext = this.getServletContext();
//获取ServletContext域中已经存在的次数
int count = (int) servletContext.getAttribute("count");
//登陆成功,登陆此数加一
count++;
response.getWriter().write("<h1>恭喜" + user.getName() + "登陆成功,这是您第" + count + "次成功登录!</h1>" );
//将更新后的次数写回ServletContext域中
servletContext.setAttribute("count", count);
}else {
response.getWriter().write("<h1 style='color:red'>抱歉~您的账号或密码错误~</h1>");
}
}catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void destroy() {
try {
//tomcat服务器停止会执行此方法,所以在这里将ServletContext域中存储的值写入到count.properties中
//通过key从ServletContext域中获取对应的值
String count = String.valueOf(this.getServletContext().getAttribute("count"));
//创建Properties对象
Properties properties = new Properties();
//给Properties对象设置key,value
properties.setProperty("count", count);
//将Properties对象的内容写入到count.properties文件中
properties.store(new FileOutputStream(LoginServlet.class.getClassLoader().getResource("count.properties").getPath()), count);
} catch (IOException e) {
e.printStackTrace();
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
c3p0-config.xml配置文件,放在src/main/java下
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///darksnow</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
<named-config name="darksnow">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///darksnow</property>
<property name="user">root</property>
<property name="password">root</property>
</named-config>
</c3p0-config>
count.properties配置文件,放在src/main/java下
count=0
数据库截图

13.request请求对象
13.1 请求与响应图解

13.2 获取请求行的内容
form.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/request_demo/line" method="post">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<button>登陆</button>
</form>
</body>
</html>
servlet代码
package com.darksnow.api;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LineServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//request对象获取请求的方式
String method = request.getMethod();
System.out.println("method:" + method);
//request对象获取请求的资源相关的内容
String requestURI = request.getRequestURI();
StringBuffer requestURL = request.getRequestURL();
System.out.println("uri:" + requestURI); // /request_demo/line
System.out.println("url:" + requestURL); // http://localhost:8080/request_demo/line
//request对象获取web应用的名称,也就是你的工程名(格式:/web应用名) 在JavaWeb中常用
String contextPath = request.getContextPath();
System.out.println(contextPath);
//request对象获取地址后的参数的字符串,get请求方式就可以获取到,post会获取到null
String queryString = request.getQueryString();
System.out.println(queryString);
//request对象获取客户端的信息,获取访问者的IP地址,但是此API与请求行无关。
String ip = request.getRemoteAddr();
System.out.println(ip);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
13.3 获取请求头的内容
referer_test.html前端
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="/request_demo/header">访问HeaderServlet资源</a>
</body>
</html>
servlet代码
package com.darksnow.api;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HeaderServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//request获取指定的请求头
String header = request.getHeader("User-Agent");
System.out.println(header);
System.out.println("----------------------");
//获得所有的请求头的名称
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()){
String headerName = headerNames.nextElement();
String headerNameValues = request.getHeader(headerName);
System.out.println(headerName + ": " +headerNameValues);
}
System.out.println("----------------------");
/*
* 请求头referer的作用:能够让你知道此次访问的来源
* 点击referer_test.html前端页面中的a标签可以看到请求头中包含
* Referer: http://localhost:8080/request_demo/referer_test.html
*
* 这个头可以做防盗链
*/
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
13.4 防盗链实现
news.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>DarkSnow热搜新闻网~</title>
</head>
<body>
<a href="/request_demo/referer">独家新闻:震惊,某一明显竟然在酒店做出这样的事儿!</a>
</body>
</html>
servlet代码
package com.darksnow.exp;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class RefererServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
//对该新闻的来源进行判断
String header = request.getHeader("Referer");
if(header != null && header.contains("http://192.168.0.62:8080")) {
response.getWriter().write("看到独家新闻是不是点进来了?其实明星也没干啥,老营销号了~");
}else {
response.getWriter().write("哼~ 盗链狗,可耻!!!");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
13.5 获取请求参数
前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/request_demo/content" method="post">
账号:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
爱好:<input type="checkbox" name="hobby" value="zq">足球
<input type="checkbox" name="hobby" value="lq">篮球
<input type="checkbox" name="hobby" value="ppq">乒乓球
<br><button>登陆</button>
</form>
</body>
</html>
servlet代码
package com.darksnow.api;
import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ContentServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取单个表单值
String username = request.getParameter("username");
System.out.println("账号:" + username);
System.out.println("-----------------------");
//获取所有的请求参数的名称
Enumeration<String> parameterNames = request.getParameterNames();
while(parameterNames.hasMoreElements()) {
System.out.println(parameterNames.nextElement());
}
System.out.println("-----------------------");
//获取所有请求参数的名称与其对应的值
Map<String, String[]> parameterMap = request.getParameterMap();
Set<Entry<String, String[]>> entrySet = parameterMap.entrySet();
for(Entry<String, String[]> entry : entrySet) {
String key = entry.getKey(); //请求参数的名称
String[] value = entry.getValue(); //请求参数对应的值
System.out.println(key + "---" + Arrays.toString(value));
}
System.out.println("-----------------------");
//以上操作对post请求和get请求都有效
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
13.6 请求转发与域对象
图解

ServletOne代码
package com.darksnow.exp;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletOne extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 前面我们学习了ServletContext域对象,现在我告诉你,request对象其实也是一个域对象
* 所以它具备了同样的方法:
* 向域以key-value的形式存储数据
* request.setAttribute(String name,Object obj);
* 通过key从域中获取相应的value
* request.getAttribute(String name);
* 通过key将域中相应的key-value数据给移除调
* request.removeAttribute(String name);
*
* request域的作用范围只在一次请求中有效
*
* 因此,一般request域和请求转发搭配使用,先设置域,再转发。
* (本质上我们这么玩儿就是为了将一个Servlet中的数据带到另外一个Servlet中,且保持一次请求)
*/
request.setAttribute("name", "darksnow");
//转发在内部发生的,不需要写项目名,直接写资源就行了。 设置转发到哪里去!
RequestDispatcher requestDispatcher = request.getRequestDispatcher("/two");
//调用转发方法
requestDispatcher.forward(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
ServletTwo代码
package com.darksnow.exp;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletTwo extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
String value = (String) request.getAttribute("name");
response.getWriter().write("ServletTwo执行了..." + value);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
总结
ServletContext域与Request域比较:
ServletContext
创建:在服务器启动时创建
销毁:在服务器关闭时销毁
域的作用范围:整个Web应用
Request
创建:请求时创建
销毁:响应结束时销毁
域的作用范围:作用于一次请求
请求转发与重定向的区别:
转发是一次请求,重定向是两次请求
转发地址栏的地址不改变,重定向地址栏的地址改变
转发只能访问内部资源,重定向可以访问外部资源(别的网站)
转发的性能要高于重定向
13.7 request中文乱码
图解

前端页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/request_demo/encode" method="post">
账号:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<button>登陆</button>
</form>
</body>
</html>
Servlet代码
package com.darksnow.api;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class EncodeServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//解决request中文乱码问题 它只适用于post请求
//request.setCharacterEncoding("UTF-8");
String username = request.getParameter("username");
//解决request中文乱码问题 它只适用于post请求也适用于get请求
username = new String(username.getBytes("ISO-8859-1"),"UTF-8");
String password = request.getParameter("password");
System.out.println(username + "---" + password);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
14.Cookie
14.1 会话技术
比如网站的购物系统,用户将购买的商品信息存储到哪里?因为http协议是无状态的,也就是说每个客户访问服务端资源时,服务器并不知道客户端是谁?所以需要会话技术来识别客户端的状态,会话技术也是帮助服务器记住客户状态的(区分客户端)
从打开一个浏览器访问某个站点,到关闭这个浏览器得整个过程,称为一次会话。会话技术就是记录这次会话中客户端的状态与数据的。会话技术分为Cookie和Session
Cookie:将数据存储在客户端本地,减少了服务器的存储的压力,安全性不好,客户端可以清除Cookie
Session:将数据存储在服务端,安全性相对好,但是会增加服务器的压力
14.2 cookie技术购物图解

14.3 cookie的API
package com.darksnow;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SendCookieServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//创建Cookie对象
//cookie本身是不能够使用中文,但是有一些小偏方可以实现,不过不推荐!
Cookie cookie = new Cookie("name", "zhangsan");
/*
* 设置Cookie在客户端的持久化(一般来说持久化就是存到硬盘上)时间,单位秒。
* 如果不设置持久化时间,Cookie会存储在浏览器的内存中,浏览器关闭,Cookie信息销毁。
* 如果设置了持久化时间,Cookie信息会被持久化到浏览器的硬盘文件里。
* 下面是Cookie信息在浏览器得磁盘文件中存储的时间是10分钟,过期,浏览器自动删除该Cookie。
*/
cookie.setMaxAge(10 * 60);
//将cookie中存储的信息发送到客户端
response.addCookie(cookie);
/*
在webapp下建立darksnow.html,内容随意
先访问SendCookieServlet你能够在响应头中看到set-cookie:name=zhangsan
然后你再去访问darksnow.html你能够在请求头中看到Cookie:name=zhangsan
将浏览器关闭,再去访问darksnow.html你会发现Cookie没了,
是因为Cookie默认是会话级别的(一次会话)
但是我们通过cookie.setMaxAge(int expiry)方法解决这个问题。
*/
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
cookie设置携带路径
package com.darksnow;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CookieServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie cookie = new Cookie("name","DarkSnow");
cookie.setMaxAge(10 * 60);
//设置cookie的携带路径 /cookie_demo/index.html
//cookie.setPath(request.getContextPath() + "/index.html"); //代表只有访问cookie_demo下的index.html这个资源才携带cookie
//cookie.setPath("/cookie_demo"); //代表cookie_demo下的所有资源都携带cookie
//cookie.setPath("/"); //代表该tomcat服务器下所有的资源都携带cookie
//如果我不设置cookie携带路径,那默认会是什么情况?
//如果不设置携带路径,那么该cookie信息会在产生该cookie的web资源所在的路径都携带cookie信息。
response.addCookie(cookie);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
销毁cookie(4步)
package com.darksnow;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CleanCookieServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.把要删除的cookie清空
Cookie cookie = new Cookie("name","");
//2.设置一样的携带路径
cookie.setPath("/cookie_demo");
//3.设置持久化时间为0
cookie.setMaxAge(0);
//4.发送cookie
response.addCookie(cookie);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
获取cookie的值
package com.darksnow;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class GetCookieServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取客户端携带的所有的cookie
Cookie[] cookies = request.getCookies();
if(cookies != null) {
for(Cookie cookie : cookies) {
//获取cookie的名称
String cookieName = cookie.getName();
if("name".equals(cookieName)) {
//获取cookie值
String cookieValue = cookie.getValue();
response.getWriter().write(cookieValue);
}
}
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
14.4 案例
现有一个资源,当用户第一次访问,那么提示"欢迎您第一次访问~",如果不是第一次访问那么就显示上一次的访问时间。

package com.darksnow;
import java.io.IOException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyPageServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
Cookie cookie = new Cookie("lastAccessTime",URLEncoder.encode(currentTime,"UTF-8"));
cookie.setMaxAge(365 * 24 * 60 * 60);
response.addCookie(cookie);
Cookie[] cookies = request.getCookies();
String lastAccessTime = null;
if(cookies != null) {
for(Cookie ck : cookies) {
if("lastAccessTime".equals(ck.getName())) {
lastAccessTime = ck.getValue();
}
}
}
if(lastAccessTime == null) {
response.getWriter().write("<h1>欢迎您第一次访问~</h1>");
}else {
response.getWriter().write("<h1>您上一次访问的时间为:" + URLDecoder.decode(lastAccessTime, "UTF-8") + "</h1>");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
15.Session

Session技术是将数据存储在服务端的技术,会为每个客户端都创建一块内存空间存储客户端数据,但是客户端需要每次都携带一个编号去服务器中找寻属于自己的内存空间。Session的实现是基于Cookie的,Session需要借助于Cookie存储客户端的唯一编号JSESSIONID
在Session我们解决以下三个问题:
1.怎么获得属于本客户端的Session对象?
2.怎么向Session中存取数据(Session也是一个域对象)?
3.Session对象的生命周期?
怎么获得属于本客户端的Session对象?
package com.darksnow;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class SessionOneServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 创建属于该客户端(会话)的私有的session区域
* 此方法内部会判断客户端是否在自己的服务端已经存在session
* 如果该客户端在服务器不存在session,那么就会创建一个新的session
* 如果该客户端在服务器已经存在session,那么就会获取那个已经存在的session,而不会创建新的
* 本质就是根据JSESSIONID判断客户端是否在服务器上已经存在session
*/
HttpSession session = request.getSession();
String id = session.getId(); //该Session对象的编号id
response.getWriter().write(id);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}

怎么向Session中存取数据(Session也是一个域对象)?
Session是一个域对象,所以Session具备以下方法:
session.getAttribute(String name,Object obj);
session.getAttribute(String name);
session.removeAttribute(String name);
-----------------------------------------------------
public class SessionOneServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
session.setAttribute("name", "DarkSnow");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
public class SessionTwoServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
String value = (String) session.getAttribute("name");
response.getWriter().write(value);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
Session对象的生命周期?
session生命周期
(1)什么时候创建?
第一次执行request.getSession()方法的时候创建。
(2)什么时候销毁?
1.服务器关闭
2.session过期(默认30分钟)
这30分钟是从什么时候开始算起?从不操作资源时开始算起!
在web.xml中可以配置session的过期时间,单位:分钟
<session-config>
<session-timeout>30</session-timeout>
</session-config>
3.手动销毁session
session.invalidate();
session持久化
操作:
当你访问sessionOne向Session存入key为name,value为darksnow的数据,再访问sessionTwo,能够正常的获取,但是我把浏览器关闭后,重新打开再访问sessionTwo的时候,发现去获取key为name对应的值却为null
解决方案:
public class SessionOneServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
session.setAttribute("name", "DarkSnow");
/*
* 思路:因为session是基于cookie的,所以我们给cookie设置持久化时间就行了!
*/
Cookie cookie = new Cookie("JSESSIONID",session.getId());
cookie.setMaxAge(60 * 10); //十分钟
cookie.setPath(request.getContextPath()); //获取当前的项目名
response.addCookie(cookie);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
public class SessionTwoServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
String value = (String) session.getAttribute("name");
response.getWriter().write(value);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
16.JSP

16.1 修改jsp页面的默认编码


16.2 JSP的脚本与注释
JSP脚本的格式:
1.<% java代码 %>
此代码块中的代码会被翻译到service方法中
2.<%= java变量或者表达式 %>
此代码块中的代码会被翻译到service方法内部的out.print()里面,这个out其实是JspWriter对象
3.<%!java代码 %>
此代码块中的代码会被翻译到servlet的成员中
JSP支持的注释:
1.html的注释
<!-- html的注释 -->
注释的可见范围:
(1)在源代码中可见
(2)浏览器访问的jsp页面中可见
(3)翻译后的Servlet中可见
2.java的注释
//单行注释
/* 多行注释 */
注释的可见范围:
(1)在源代码中可见
(2)翻译后的Servlet中可见
3.jsp的注释
<%-- jsp的注释 --%>
注释的可见范围:
(1)在源代码中可见
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 这是html的注释 -->
<%
//这是单行注释
int i=10;
/*
这是多行注释
*/
System.out.println(i);
%>
<%-- 这是jsp的注释 --%>
<%=i %>
<%=1+1 %>
<%! String str = "DarkSnow";%>
<%=str %>
</body>
</html>
JSP的本质其实是Servlet,JSP被访问的时候,Tomcat会将其翻译成Servlet,由JspServlet负责这个事儿。
在tomcat安装目录下的work目录中,里面存放着jsp被翻译后的Servlet。
上述代码翻译后的代码
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class test_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
String str = "DarkSnow";
private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();
private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;
private static final java.util.Set<java.lang.String> _jspx_imports_packages;
private static final java.util.Set<java.lang.String> _jspx_imports_classes;
static {
_jspx_imports_packages = new java.util.HashSet<>();
_jspx_imports_packages.add("javax.servlet");
_jspx_imports_packages.add("javax.servlet.http");
_jspx_imports_packages.add("javax.servlet.jsp");
_jspx_imports_classes = null;
}
private volatile javax.el.ExpressionFactory _el_expressionfactory;
private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;
public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
}
public java.util.Set<java.lang.String> getPackageImports() {
return _jspx_imports_packages;
}
public java.util.Set<java.lang.String> getClassImports() {
return _jspx_imports_classes;
}
public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
if (_el_expressionfactory == null) {
synchronized (this) {
if (_el_expressionfactory == null) {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
}
}
}
return _el_expressionfactory;
}
public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
if (_jsp_instancemanager == null) {
synchronized (this) {
if (_jsp_instancemanager == null) {
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
}
}
return _jsp_instancemanager;
}
public void _jspInit() {
}
public void _jspDestroy() {
}
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
final java.lang.String _jspx_method = request.getMethod();
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
return;
}
final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;
try {
response.setContentType("text/html; charset=UTF-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write("\r\n");
out.write("<!DOCTYPE html>\r\n");
out.write("<html>\r\n");
out.write("<head>\r\n");
out.write("<meta charset=\"UTF-8\">\r\n");
out.write("<title>Insert title here</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write(" <!-- 这是html的注释 -->\r\n");
out.write(" ");
//这是单行注释
int i=10;
/*
这是多行注释
*/
System.out.println(i);
out.write("\r\n");
out.write(" \r\n");
out.write(" ");
out.write('\r');
out.write('\n');
out.write(' ');
out.print(i );
out.write('\r');
out.write('\n');
out.write(' ');
out.print(1+1 );
out.write(" \r\n");
out.write(" \r\n");
out.write(" ");
out.write('\r');
out.write('\n');
out.write(' ');
out.print(str );
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {
if (response.isCommitted()) {
out.flush();
} else {
out.clearBuffer();
}
} catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}
16.3 jsp的指令
jsp包含三大指令
1.page指令(一般不需要动它)
格式:<%@ page 属性名1="属性值" 属性名2="属性值" ...%>
常见的属性:
(1)language:代表jsp脚本可以嵌入的语言种类
(2)contentType:jsp被翻译成Servlet后,在service方法内会出现response.setContentType()
(3)pageEncoding:代表当前jsp文件自身的编码
(4)session:jsp在翻译的时候是否自动创建session对象,默认为true,代表默认自动创建session
(5)import:用来导包的
(6)extends:继承
(7)errorPage:当前页面报错可以跳转到指定的页面
(8)isErrorPage:表示当前页面是一个处理错误的页面,当isErrorPage为true时能够通过exception
这个变量来获取一个异常对象
在web.xml中可以根据状态码来配置错误页面
<error-page>
<error-code>404</error-code>
<location>/404.jsp</location>
</error-page>
(9)buffer:配置out缓冲区的大小,默认8kb
2.include指令
可以将一个jsp页面包含到另外一个jsp页面中
格式:<%@ include file="被包含的文件的相对路径" %>
3.taglib指令
在jsp页面中引入标签库(jstl标签库)
格式:<%@ taglib uri="标签库地址" prefix="前缀" %>
16.4 JSP的九大隐式对象
JSP被翻译成Servlet之后,在service中有9个对象已经定义并初始化完毕了,所以可以直接在jsp也面上使用
1.pageContext(PageContext对象)
2.session(HttpSession对象)
3.application(ServletContext对象)
4.config(ServletConfig对象)
5.out(JspWriter对象)
6.page(当前Servlet对象)
7.request(HttpServletRequest对象)
8.response(HttpServletResponse对象)
9.exception(Throwable对象)
其他的都学了,我们主要讲out和pageContext
16.4.1 out(JspWriter)对象

16.4.2 pageContext对象
pageContext对象(JSP页面的上下文对象)
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
//pageContext是一个域对象,所以可以存取数据
pageContext.setAttribute("name", "darksnow");
//原来往request域存储数据
request.setAttribute("name", "zhangsan");
//pageContext还能向其他三大域存取数据
pageContext.setAttribute("name", "lisi", PageContext.REQUEST_SCOPE);
pageContext.setAttribute("name", "wangwu", PageContext.SESSION_SCOPE);
pageContext.setAttribute("name", "maliu", PageContext.APPLICATION_SCOPE);
%>
<%=request.getAttribute("name") %>
<%=pageContext.getAttribute("name", PageContext.REQUEST_SCOPE) %>
<%=
//依次从pageContext域,request域,session域,application域中获取指定属性的值
//如果在找到过程中找到了,那么就不继续往下找了!
pageContext.findAttribute("name")
%>
<!--
综上述总结:
1.pageContext本身是一个域,所以可以存取数据,但它也能从其它的域中存取数据以及移除数据
2.能够通过findAttribute方法在四大域中找寻指定属性的值
-->
</body>
</html>
四大域的作用范围
pageContext域:当前jsp页面
request域:一次请求中
session域:默认是一次会话
application域:整个web工程
pageContext获取其他隐式对象
<%
//pageContext获取其他八大隐式对象
HttpSession s = pageContext.getSession();
ServletRequest req = pageContext.getRequest();
ServletResponse reqo = pageContext.getResponse();
pageContext.getServletContext();
pageContext.getServletConfig();
pageContext.getOut();
pageContext.getException();
pageContext.getPage();
%>
16.5 JSP的标签
16.5.1 页面包含
格式:<jsp:include page="被包含的页面"></jsp:include>


16.5.2 请求转发
格式:<jsp:forward page="要转发的资源,不用带项目名"></jsp:forward>
17.EL表达式 & JSTL
17.1 EL表达式
EL表达式可以嵌入在jsp页面中,使用它是为了减少jsp脚本的编写,从而提升开发效率。
EL从域中取数据,格式:${EL表达式}
例子:
(1)EL从pageContext域中获取数据:${pageScope.key}
(2)EL从request域中获取数据:${requestScope.key}
(3)EL从session域中获取数据:${sessionScope.key}
(4)EL从application域中获取数据:${applicationScope.key}
(5)EL从四大域中获取数据:${key}
依次从pageContext域,request域,session域,application域中获取指定属性的值,
如果在找到过程中找到了,那么就不继续往下找了!
<%@page import="java.util.*"%>
<%@page import="com.darksnow.bean.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 模拟域中的数据 -->
<%
pageContext.setAttribute("company", "中国好公司");
request.setAttribute("company", "点子牛公司");
//存储一个对象
User user = new User();
user.setId(1);
user.setName("zhangsan");
user.setAge(18);
session.setAttribute("user", user);
//存储一个集合
List<User> list = new ArrayList<>();
User user1 = new User();
user1.setId(2);
user1.setName("lisi");
user1.setAge(28);
User user2 = new User();
user2.setId(3);
user2.setName("wangwu");
user2.setAge(38);
list.add(user1);
list.add(user2);
application.setAttribute("list", list);
%>
<!-- 用原来的方式取出域中的值 -->
<%=request.getAttribute("company") %>
<%
User sessionUser = (User)session.getAttribute("user");
out.write(sessionUser.getName());
%>
<hr/>
<!-- 用EL表达式取出域中的值 -->
${requestScope.company}
${sessionScope.user.name}
${applicationScope.list[0].name }
<hr/>
<!-- EL表达式可以执行表达式的运算 -->
${1+1}
${1==1?true:false}
<!-- empty是用来判断某个对象是否为null,如果为null返回true -->
${empty list}
<hr/>
<!-- 原来获取项目名 -->
<%=request.getContextPath() %>
<!-- 以后在jsp页面中获取项目名,请用以下方式 重要 -->
${pageContext.request.contextPath}
<hr/>
<!--
EL从四大域中获取数据:${key}
依次从pageContext域,request域,session域,application域中获取指定属性的值,
如果在找到过程中找到了,那么就不继续往下找了!
项目中全部采取这种方式来获取域中的值!
前提:你往四大域中存数据,要保证key唯一!
-->
${list[1].name}
</body>
</html>

17.2 JSTL
JSTL叫做JSP标准标签库,开发人员可以利用这些标签取代JSP页面上的一些Java代码,从而提高程序的可读性,降低程序的维护难度。
想要使用JSTL需要导入两个jar包
1.jstl.jar
2.standrad.jar
JSTL多于EL表达式一起使用
<%@page import="com.darksnow.bean.User"%>
<%@page import="java.util.*"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- 在uri的双引号内部,按alt + / 能够出现提示 -->
<!-- 下面这句代码没有什么技巧,就当做写死的就行 -->
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- if标签 -->
<% request.setAttribute("count", 10); %>
<!-- test代表返回boolean值的条件表达式 -->
<c:if test="${count > 1}">
满足条件<br>
</c:if>
<!--
模拟普通for循环
for(int x=0;x<=8;x++){
System.out.println(x);
}
属性:
begin:代表从什么开始
end:代表到什么结束(等价于<=)
var:自定义的变量名
-->
<c:forEach begin="0" end="8" var="x">
${x}<br>
</c:forEach>
<!--
模拟增强for循环
for(User user : list){
System.out.println(user.getName());
}
item:代表你要遍历的数组或集合
var:代表自定义变量,你遍历出来的数据存到哪个变量中
-->
<%
List<User> list = new ArrayList<>();
User u1 = new User();
u1.setId(1);
u1.setName("zhangsan");
u1.setAge(18);
User u2 = new User();
u2.setId(2);
u2.setName("lisi");
u2.setAge(28);
list.add(u1);
list.add(u2);
request.setAttribute("list", list);
%>
<!--
varStatus功能:
下面代码的status是一个自定义变量,这个变量存着LoopTagSupport$1Status对象
此对象有一下属性:
current:当前这次循环的(集合/数组)中的项
index:当前这次迭代从0开始的迭代索引
count:当前这次迭代从1开始的迭代次数
-->
<c:forEach items="${list}" var="user" varStatus="status">
${user.name} --- ${user.age} / ${status.current.name} --- ${status.current.age} / ${status.index} / ${status.count}<br>
</c:forEach>
<!-- 遍历map集合 -->
<%
Map<Integer,String> mapOne = new HashMap<>();
mapOne.put(1, "小花");
mapOne.put(2, "小汪");
mapOne.put(3, "小胖");
session.setAttribute("mapOne", mapOne);
%>
<c:forEach items="${mapOne}" var="data">
${data.key} --- ${data.value}<br>
</c:forEach>
<%
Map<Integer,User> mapTwo = new HashMap<>();
User uu1 = new User();
uu1.setId(1);
uu1.setName("zhangsan");
uu1.setAge(18);
User uu2 = new User();
uu2.setId(2);
uu2.setName("lisi");
uu2.setAge(28);
mapTwo.put(11, uu1);
mapTwo.put(22, uu2);
session.setAttribute("mapTwo", mapTwo);
%>
<c:forEach items="${mapTwo}" var="data">
${data.key} --- ${data.value.name} --- ${data.value.age}<br>
</c:forEach>
</body>
</html>
18.JavaEE开发模式
(1)什么是开发模式?
就是程序员们在长期的开发过程中总结出来的套路,你开发的时候跟着套路走就完事儿了!
(2)Model One
技术栈:JSP + JavaBean
弊端:所有的东西都杂糅在一起,整体看起来非常的凌乱!
(3)Model Two
技术栈:JSP + Servlet + JavaBean
这种模式,相比较Model One来说,会更加的有条理,因为各个技术都只复制自己擅长的部分!
Servlet处理业务,jsp用于展示页面,JavaBean用于封装数据
Model Two也就是MVC(Web开发的设计模式)
1.M(Model模型) ---> JavaBean
2.V(View视图) ---> JSP
3.C(Controller控制器) ---> Servlet
(4)三层架构
使用Model Two的时候,开发分为三层
web层 ---> 与客户端交互
service层 ---> 处理业务
dao层 ---> 与数据库交互
注意:三层使用三个不同的包来体现的
例子:
com.darksnow.web
com.darksnow.service
com.darksnow.dao
com.darksnow.bean(JavaBean,但是你在有些公司会看到这个包可能还会是domain,pojo,entity)
com.darksnow.utils(存放工具类的包)
一般异常在dao的异常一般抛到service处理!!!

利用三层架构实现登录的案例,请查看Login_Demo工程!!!
19.事务
19.1 概述
一件事儿是由n个小事件组成的,要不这n个小事件都成功,要不都失败,这就是将这n个小事件放到了一个事务中。
MySQL支持多种执行引擎,我们重点关注下面两个:
MyISAM:不支持事务,适合查询以及插入为主的项目
InnoDB:支持事务,适合频繁修改以及涉及到安全性较高的项目
mysql的事务:
默认(自动事务):
一条sql语句其实就是一个事务,而且这个事务是自动提交的,不需要你去关心!
手动事务的SQL语句:
1.开启事务:start transaction
2.提交事务:commit(代表从开启事务到事务提交,中间所有的sql都认为是有效的,
会真正的更新到数据库,没有提交以前数据库没真正的发生改变)。
3.事务回滚:rollback(代表从开启事务到事务回滚,之间所有的sql语句都认为是无效的,不会真正更新到数据库)

19.2 JDBC事务操作
package com.darksnow.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 通过JDBC进行手动事务操作API:
* 我们操作事务的API需要通过Connection对象去调用
* conn.setAutoCommit(false); 开启手动事务,取消自动提交
* conn.rollback(); 事务回滚
* conn.commit(); 事务提交
*
* 注意:
* 控制事务的Connection必须是同一个
* 执行sql的Connection与开启事务的Connection必须是同一个才能控制住事务
*/
public class JDBCTransaction {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///darksnow","root","root");
//开启手动事务,不自动提交,而是手动提交
conn.setAutoCommit(false);
stmt = conn.createStatement();
stmt.executeUpdate("update account set money=500 where name = 'zhangsan'");
int i = 1/0;
} catch (Exception e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
try {
conn.commit();
} catch (SQLException e1) {
e1.printStackTrace();
}
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
19.3 DBUtils事务操作
package com.darksnow.test;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import com.darksnow.utils.C3P0Util;
public class DBUtilsTransaction {
public static void main(String[] args) {
Connection conn = null;
try {
QueryRunner queryRunner = new QueryRunner();
conn = C3P0Util.getConnection();
//开启手动事务
conn.setAutoCommit(false);
queryRunner.update(conn,"update account set money=500 where name='zhangsan'");
int i = 1/0;
} catch (Exception e) {
try {
//事务回滚
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
try {
//提交事务
conn.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
19.4 转账案例

此案例具体参考transfer_demo这个工程!
19.5 ThreadLocal
参考:https://www.cnblogs.com/henuliulei/p/16397795.html
使用ThreadLocal解决转账事务问题,请参考transcation_ThreadLocal工程
20.Ajax
1.概述
什么是同步?什么异步?
同步:客户端发送请求到服务端,当前服务端响应之前,客户端处于等待状态,不能做其他事儿。
异步:客户端发送请求到服务端,无论服务端是否响应,客户端随时可以做其他的事儿。
Ajax的运行原理
客户端发送请求,请求会发到浏览器内核中的Ajax引擎上,Ajax引擎会提交请求到服务端,在此期间,客户端可以进行任意操作,而且这个任意操作会被响应

2.js原生Ajax(了解)
test.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
function fun1(){
//创建Ajax引擎对象
var xmlhttp = new XMLHttpRequest();
//绑定监听事件(监听服务器的响应任务)
xmlhttp.onreadystatechange = function(){
/*
readyState属性
存有XMLHttpRequest的状态,从0到4。
0:请求未初始化
1:服务器连接已经建立
2:请求已经接收
3:请求处理中
4:请求已完成,并且响应已就绪
status:HTTP状态码
*/
if(xmlhttp.readyState == 4 && xmlhttp.status == 200){
//接收响应数据
document.getElementById('span1').innerHTML = xmlhttp.responseText;
}
}
//设置发送Ajax请求前的参数(请求提交方式,请求提交到哪里,是否异步)
xmlhttp.open("GET","/Ajax_Demo/ajax?name=zhangsan",true);
//发送请求
xmlhttp.send();
}
function fun2(){
//创建Ajax引擎对象
var xmlhttp = new XMLHttpRequest();
//绑定监听事件(监听服务器的响应任务)
xmlhttp.onreadystatechange = function(){
/*
readyState属性
存有XMLHttpRequest的状态,从0到4。
0:请求未初始化
1:服务器连接已经建立
2:请求已经接收
3:请求处理中
4:请求已完成,并且响应已就绪
status:HTTP状态码
*/
if(xmlhttp.readyState == 4 && xmlhttp.status == 200){
//接收响应数据
document.getElementById('span2').innerHTML = xmlhttp.responseText;
}
}
//设置发送Ajax请求前的参数(请求提交方式,请求提交到哪里,是否异步)
xmlhttp.open("POST","/Ajax_Demo/ajax",false);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
//发送请求
xmlhttp.send("name=lisi");
}
</script>
</head>
<body>
<input type="button" value="异步操作" onclick="fun1()"><br>
<span id="span1" style="color:red"></span><br>
<input type="button" value="同步操作" onclick="fun2()"><br>
<span id="span2" style="color:red"></span><br>
<input type="button" value="测试" onclick="alert('test...')">
</body>
</html>
AjaxServlet
package com.darksnow.ajax;
import java.io.IOException;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class AjaxServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//UUID.randomUUID().toString() 获取随机字符串
response.getWriter().write(UUID.randomUUID().toString());
System.out.println(request.getParameter("name"));
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
3.JSON(重点)
json是一种数据交互格式,可以配合Ajax进行前后台的数据交互,以及移动端与服务端的数据交互
json的格式:
(1)对象格式:
{"key1":obj1,"key2":obj,...}
(2)数组/集合格式:
[obj1,obj2,obj3,...]
例子:
(1)<!-- 现在有一个Account对象,用json数据格式表示 -->
{"id":1,"name":"张三","money":10000}
(2)<!-- List<Account>,用json数据格式表示 -->
[
{"id":1,"name":"张三","money":10000},
{"id":2,"name":"李四","money":20000}
]
注意:json是JavaScript的原生内容,所以JavaScript可以直接去获取json数据
<script type="text/javascript">
/*
{key:value,key:value}
class Person{
String firstname = "张";
String lastname = "三丰";
Integer age = 100;
}
Person p = new Person();
System.out.println(p.firstname);
*/
var person = {"firstname":"张","lastname":"三丰","age":100}
//取出lastname
alert(person.lastname)
//取出age
alert(person.age)
</script>
<script type="text/javascript">
/*
[{key:value,key:value},{key:value,key:value}]
*/
var person = [
{"firstname":"张","lastname":"三丰","age":100},
{"firstname":"辛","lastname":"吉飞","age":200}
]
//取出 辛
alert(person[1].firstname)
//取出 100
alert(person[0].age)
</script>
<script type="text/javascript">
/*
{
"param":[{key:value,key:value},{key:value,key:value}]
}
class json{
List<Person> param
}
*/
var json = {
"persons":[
{"name":"小双","age":28,"addr":"扬州"},
{"name":"可爱多","age":18,"addr":"紫禁城"},
{"name":"小吴","age":10,"addr":"东北"}
]
}
//取出 可爱多
alert(json.persons[1].name)
//取出 东北
alert(json.persons[2].addr)
</script>
<script type="text/javascript">
/*
{
"param1":[{key:value,key:value},{key:value,key:value}],
"param2":[{key:value,key:value},{key:value,key:value}],
"param3":[{key:value,key:value},{key:value,key:value}]
}
*/
var json = {
"persons1":[
{"name":"小双","age":28,"addr":"扬州"},
{"name":"可爱多","age":18,"addr":"紫禁城"},
{"name":"小吴","age":10,"addr":"东北"}
],
"persons2":[
{"name":"霜儿","age":25,"addr":"北京"},
{"name":"多可爱","age":15,"addr":"上海"}
]
}
//取出 多可爱
alert(json.persons2[1].name)
//取出 15
alert(json.persons2[1].age)
</script>
4.JQuery的Ajax(重点)
JQuery在线文档:https://jquery.cuishifeng.cn/
AjaxServlet
package com.darksnow.ajax;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.gson.Gson;
public class AjaxServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
int i = 1/0;
String name = request.getParameter("name");
String age = request.getParameter("age");
System.out.println(name + "---" + age);
// try {
// TimeUnit.SECONDS.sleep(5); //让线程睡5秒
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
Map<String,Object> map = new HashMap<>();
map.put("msg", "接口访问成功~");
map.put("code", "200");
map.put("key", UUID.randomUUID().toString());
//需要导入gson的jar包,阿里的fastjson这个jar包也能做json转换
Gson gson = new Gson();
//将一个对象转为json字符串
String res = gson.toJson(map);
response.getWriter().write(res);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
test.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="/Ajax_Demo/jquery-3.1.1.min.js"></script>
<script type="text/javascript">
function fun1(){
//$.post和$.get默认都是异步请求,如果需要同步请求,则可以进行以下操作
$.ajaxSettings.async = false;
$.post(
"/Ajax_Demo/ajaxServlet", //发送请求地址
{"name":"张三","age":18}, //请求参数
function(res){ //发送成功时(请求成功)回调函数,作用:资源响应的数据就在这个回调函数的res参数中
console.log(res)
console.log(res.key)
console.log(res.code)
console.log(res.msg)
},
"json" //返回内容格式,资源响应的数据的格式,就是指定是res参数的格式
)
}
function fun2(){
$.get(
"/Ajax_Demo/ajaxServlet", //发送请求地址
{"name":"张三","age":18}, //请求参数
function(res){ //发送成功时(请求成功)回调函数,作用:资源响应的数据就在这个回调函数的res参数中
console.log(res)
console.log(res.key)
console.log(res.code)
console.log(res.msg)
},
"json" //返回内容格式,资源响应的数据的格式,就是指定是res参数的格式
)
}
function fun3(){
//推荐使用!!!
//此方式的参数全部是以json的格式存在的
$.ajax({
url:"/Ajax_Demo/ajaxServlet",
async:true, //true代表异步请求,false代表同步请求
type:"POST", //请求方式,默认为GET
data:{"name":"张三","age":18}, //请求参数
dataType:"json", //返回内容格式,资源响应的数据的格式
success:function(res){ //发送成功时(请求成功)回调函数,作用:资源响应的数据就在这个回调函数的res参数中
console.log(res.key)
console.log(res.code)
console.log(res.msg)
},
error:function(){ //请求失败时调用此函数
alert("请求失败!!!")
}
})
}
</script>
</head>
<body>
<input type="button" value="$.post操作" onclick="fun1()"><br>
<input type="button" value="$.get操作" onclick="fun2()"><br>
<input type="button" value="$.ajax操作" onclick="fun3()"><br>
<input type="button" value="测试" onclick="alert('测试按钮被点击了...')"><br>
</body>
</html>
21.文件上传案例
upload.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<!--
multipart/form-data是指表单数据有多部分构成,既有文本数据,又有文件等二进制数据的意思。
默认情况下,enctype的值是application/x-www-form-urlencoded,不能用于文件上传,
只有使用了multipart/form-data,才能完整的传递文件数据。
-->
<form action="/upload_demo/upload" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"><br>
<input type="file" name="filename"><br>
<input type="submit" value="文件上传">
</form>
</body>
</html>
UploadServlet
package com.darksnow;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
//需要导入commons-fileupload-1.2.1.jar 和 commons-io-1.4.jar
public class UploadServlet extends HttpServlet {
private static final long serialVersionUID = 6468244120721891168L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//创建磁盘文件项工程(设置临时文件的大小,设置临时文件的存储位置),单位字节
String tPath = this.getServletContext().getRealPath("temp");
DiskFileItemFactory factory = new DiskFileItemFactory(1024 * 1024, new File(tPath));
//创建文件上传的核心类
ServletFileUpload upload = new ServletFileUpload(factory);
//设置上传文件的名称的编码
upload.setHeaderEncoding("UTF-8");
//判断是否是一个上传的表单
boolean multipartContent = ServletFileUpload.isMultipartContent(request);
if(multipartContent) {
//是文件上传的表单
//解析request请求,获取文件项集合
List<FileItem> parseRequest = upload.parseRequest(request);
if(parseRequest != null) {
//遍历文件项集合
for(FileItem item : parseRequest) {
//判断是普通表单项还是文件上传项
boolean formField = item.isFormField();
if(formField) {
//是普通表单项
String fieldName = item.getFieldName(); //普通表单项的name属性值
String fieldValue = item.getString("UTF-8");
System.out.println(fieldName + "---" + fieldValue);
//当表单为enctype="multipart/form-data"时,request.getParameter等相关操作会失效的
}else {
//是文件上传项
//获取的上传文件的名称
String fileName = item.getName(); // 22.jpg 1.2.txt 1 2 txt
//获取上传文件的内容
InputStream in = item.getInputStream();
//将in这个流的字节数据拷贝到服务器上
String path = this.getServletContext().getRealPath("/images");
String[] fix = fileName.split("\\.");
FileOutputStream out = new FileOutputStream(path + "/" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "." + fix[fix.length-1]);
IOUtils.copy(in, out);
in.close();
out.close();
item.delete();
}
}
}
}else {
//不是文件上传的表单,意思就是此次请求没有涉及文件上传
//你就可以正常的像以前一样写代码,比如使用request.getParameter等操作
}
}catch (Exception e) {
e.printStackTrace();
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}

浙公网安备 33010602011771号