文章目录
1 tomcat
1.1启动和关闭
/Users/zhaoyang/Library/tomcat/bin下的startup.sh和shutdown.sh文件在终端里执行,命令是sudo sh ./startup.sh 或者直接运行对应的.bat文件
1.2 配置
tomcat服务器的核心配置文件是tomcat下conf下的server.xml文件。
可以配置启动的端口号
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
- tomcat默认的端口号为:8080
- mysql:3306
- http:80
- https:443
端口号都是可以改的,主机名称也是可以改的,在文件的host里。
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
- 默认的主机名为:localhost–>127.0.0.1
- 默认网站应用存放的位置为:webapps
1.3 发布一个Web网站
将自己写的网站放到tomcat服务器中指定的web应用文件夹(webapps)下,网站文件的目录结构为:
--webapps:tomcat服务器的web目录
--ROOT
--kuangstudy:网站的目录名
--WEB-INF
-- classes:Java程序
--lib:web应用所依赖的jar包
--web.xml:网站的配置文件
--insex.html 默认的网站首页
--static
--css
--style.css
--js
--img
--...
2 HTTP中比较重要的内容
2.1 请求行
- 请求方式get、post
- get:请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效。
- post:请求能够携带的参数没有数量限制,大小没有限制,不会再浏览器的URL地址栏显示数据内容,安全,但不高效。
2.2 响应体中的状态响应码
200:请求响应成功
3xx:请求重定向
- 重定向:重新转到新的网址,例如找不到你所查询的网站跳转到网页找不到了这个页面就是重定向。
4xx:找不到资源
- 资源不存在
5xx:服务器代码错误
3 Maven
在JavaWeb开发中,需要大量的jar包,Maven能帮我们到如何配置这个jar包。
注意:创建maven项目之后,要在设置里设置maven的路径以及仓库路径。
3.1 Maven项目架构管理工具
我们目前就是用来方便导入jar包。
Maven的核心思想:约定大于配置,有约束不要违反,Maven会规定好你该如何去编写Java代码,必须要按照这个规范来。
3.2 配置
- 添加阿里云镜像,在conf文件夹下的setting.xml文件中添加
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
-
本地仓库
在本地建立一个仓库,还是在setting.xml中
<localRepository>/Users/zhaoyang/Library/apache-maven-3.8.6</localRepository>
3.3 在IDEA中使用Maven
- 启动IDEA,创建一个mavenWeb项目,勾选Create from grchetype,可以使用maven的很多模版。
- 选中maven-archetype-webapp,下方显示A simple Java web application。点击next。
- GroupId填小组ID,一般写公司域名或者个人名字。ArtifactId是项目名,javaweb-01-maven这样命名。版本默认。
- Maven地址,maven用户设置文件,本地仓库地址。其中maven地址可选,有两个是IDEA自带,要是自己装了多个版本的maven那就可选,前提是配置好环境变量。选好对应的maven之后底下的设置文件和本地仓库地址一定和当前maven对应。
- 项目名字会自动参考之前填的,但最好加上横杠-。只需注意项目保存地址。
- catalog一定选internal
- 创建完后,下载完资源出现BUILD SUCCESS说明创建成功。本地仓库可以查看多了很多东西。
- 创建完后在IDEA的设置里查看maven设置,查看本地仓库和设置文件的地址是否正确。
下面是maven项目的标准目录
src/main/java
src/main/resources
src/test/java
src/test/resources
以上的文件目录都是directory,新创建的都是灰色的,需要将directory标记为需要的包类型,按如下操作:
src/main/java 关联为 Sources Root
(右击java》选择mark Dirctory as》选择Sources Root)
src/main/resources 关联为Resources Root
(右击resources》选择mark Dirctory as》选择Resources Root)
src/test/java 关联为Test Sources Root
(右击test文件夹下test》选择mark Dirctory as》选择Test Sources Root)
src/test/resources 关联为 Test Resources Root
(右击test文件夹下resources》选择mark Dirctory as》选择Test Resources Root)
也可以在ProjectStruct里边修改
3.4 在IDEA里配置Tomcat
切记是tomcat server 不是tomcatEE server
解决警告问题
ok之后可以点击运行会在浏览器显示hello world!显示的就是默认的index.jsp里的内容。
对于war包,我们用哪个项目就创建哪个war包,像目前我们一个一项目里有多个module,所以每一个module对应一个war包,保证项目里只有一个war包,这样比较快。
3.5 maven侧边栏的使用
3.6 pom文件
pom.xml是maven的核心配置文件
maven由于预定大于配置,我们之后可能遇到我们写的配置文件不能导出或者生效的问题,解决方案在build标签里配置resources
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
4 Servlet
4.1 servlet简介
- Servlet是sun公司开发动态web的一门技术
- Sun公司在这些API中提供了一个接口叫做:servlet,如果你想开发一个Servlet程序,只需要完成两个步骤:
- 编写一个类,实现Servlet接口
- 把开发好的java类部署到web服务器中
实现了Servlet接口的Java程序叫做Servlet。
4.2 Hello Servlet
- 构建一个普通的maven项目,**就是不选webapp这个选项。**删掉里边的src目录,以后我们的许诶下就在这个项目里建立Module,这个空的工程就是maven主工程。
- 创建好后最好把依赖都写进pom.xml里,就是maven servlet jsp和jsp-api的依赖。最新的可以去https://mvnrepository.com/官网复制,在官网的搜索框输入Javax.Servlet-api
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
- 在项目名中右键新建一个module。因为开始我们新建的是一个普通的maven项目,现在在这个普通maven项目里新建webapp项目。
这样创建其实就是为了把更多的项目汇总起来放在一个大的目录下,当然也可以不需要父项目,直接创建maven的webapp项目。
- 关于maven父子项目的理解
父项目中会有:
<modules>
<module>servlet-01</module>
</modules>
子项目中会有:
<parent>
<artifactId>javaweb-02-servlet</artifactId>
<groupId>zy.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
父项目中的jar包子项目可以直接使用。
- 要在WEB-INF目录下的web.xml内容换成最新的。可以在下载的tomcat目录下webapps\ROOT\WEB-INF里的web.xml里复制。
- 将项目里的目录结构补充完整。
- 5和6称之为maven项目优化
- 在Java目录下就可新建class文件或者package。
4.2.1 编写一个servlet程序
servlet接口sun公司有两个默认的实现类,HttpServlet和GenericServlet,主要用HttpServlet。
-
编写一个普通类;
-
实现Servlet接口,直接继承HttpServlet类;
-
编写servlet类
package com.zy.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class HelloServlet extends HttpServlet {
//由于get或者post只是请求实现的不同方式,可以互相调用,业务逻辑一样;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter(); //响应流
writer.print("Hello Servlet!");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
- 编写Servlet的映射
为什么需要映射:我们写的是Java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,我们需要在web服务器中注册我们写的Servlet,还需要给他一个浏览器能够访问的路径;(写在web.xml里)
<!--注册servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.zy.servlet.HelloServlet</servlet-class>
</servlet>
<!--servlet的请求路径-->
<!--localhost:8080/servlet_01_war/hello-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern> <!--请求路径一定要加/-->
</servlet-mapping>
- 配置tomcat
注意:配置项目发布的路径,就是上边图片里最底下那个路径。
- 启动项目
4.3 servlet原理
servlet是由web服务器调用,web服务器在收到浏览器请求后,会:
4.4 mapping问题
- 一个servlet可以指定一个映射路径
<!--servlet的请求路径-->
<!--localhost:8080/servlet_01_war/hello-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
- 一个servlet可以指定多个映射路径
<!--servlet的请求路径-->
<!--localhost:8080/servlet_01_war/hello-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello2</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello3</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello4</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
- 一个servlet可以指定通用映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
*是通配符,所有hello/后面写还是不写
默认路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
- 指定一些后缀或者前缀等等…
<!--可以自定义后缀实现请求映射
注意点:自定义后缀情况下,*前面不能加项目映射路径,也就是/符号以及/hello-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>*.zy</url-pattern>
</servlet-mapping>
4.4.1 404异常处理
也就是当输入错误路径就会进入当前设置的默认404页面,输入的路径正确就会进入指定的正确路径。
public class ErrorServlet extends HelloServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
PrintWriter writer = resp.getWriter();
writer.print("<h1>404<h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<servlet>
<servlet-name>error</servlet-name>
<servlet-class>com.zy.servlet.ErrorServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>error</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
这里配的url是/*所以一运行就会进error页面。url就是指定的映射路径。
优先级:指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求,也就是上边提到的默认路径,使用通配符。
4.5 ServletContext
web容器在启动时,它会为每一个web程序创建一个对应的ServletContext对象,它代表了当前的web应用;
1.共享数据
- 我们在这个Servlet中保存的数据,可以在另外一个servlet中拿到;一个servlet把数据会放到ServletContext中,其它servlet可以到ServletContext中拿到。
放置数据的类:
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//this.getServletContext(); servlet上下文
ServletContext context = this.getServletContext();
String username = "zy";
context.setAttribute("username",username);//将一个数据保存在ServletContext中,名字是username,值是变量username,是一个键值对
}
}
读取数据的类:
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String username = (String) context.getAttribute("username");
resp.setContentType("text/http");
resp.setCharacterEncoding("utf-8");
resp.getWriter().print("名字是:"+username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
配置web.xml
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>gets</servlet-name>
<servlet-class>com.zy.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>gets</servlet-name>
<url-pattern>/gets</url-pattern>
</servlet-mapping>
测试访问进入到/hello路径设置数据,进入到/gets读出数据。
2. 获取初始化参数
可以在web.xml里初始化一个参数:
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String url = context.getInitParameter("url");
resp.getWriter().print(url);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
3 请求转发
public class ServletDemo04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext(); //现在展示的是把这几行话分开写,当然也可以写成一句话
System.out.println("进入了ServletDemo04");
RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp"); //转发的请求路径
requestDispatcher.forward(req,resp);
//context.getRequestDispatcher("/gp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
<servlet>
<servlet-name>sd4</servlet-name>
<servlet-class>com.zy.servlet.ServletDemo04</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>sd4</servlet-name>
<url-pattern>/sd4 </url-pattern>
</servlet-mapping>
请求的是/sd4,但是显示的是/gp页面,而且路径还是/sd4不发生改变,但是重定向的路径是会发生改变的。
4 读取资源文件
Properties
- 在Java目录下新建properties
- 在resources目录下新建properties
发现:都被打包到了同一路径下:classes。我么俗称这个路径为classpath;
思路:需要一个文件流
username = root
password = 123456
public class ServletDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
InputStream resourceAsStream = context.getResourceAsStream("WEB-INF/classes/db.properties"); //返回一个流
Properties properties = new Properties();
properties.load(resourceAsStream); //拿到文件的流
String username = properties.getProperty("username");
String password = properties.getProperty("password");
resp.getWriter().print(username+","+password);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<servlet>
<servlet-name>sd5</servlet-name>
<servlet-class>com.zy.servlet.ServletDemo05</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>sd5</servlet-name>
<url-pattern>/sd5 </url-pattern>
</servlet-mapping>
访问测试即可ok;
4.6 HttpServletResponse
创建新module要修改的有webapp.xml和添加新的artifact文件,就是:war文件
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的一个HttpServletResponse;
- 如果要获取客户端请求过来的参数:找HttpServletRequest
- 如果要给客户端相应一些信息:找HttpServletResponse
1.简单分类
负责向浏览器发送数据的方法
ServletOutputStream getOutStream() throws IOException; //写一般的流用这个
PrintWriter getWriter() throw IOException; //写中文用这个
向浏览器发送相应头的方法
void setCharacterEncoding(String Var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
void setDateHeader(String var1,long var2);
void addDateHeader(String var1,long var2);
2. 下载文件
- 向浏览器输出消息(前面有),就是在浏览器页面上输出,sout是在IDEA的控制台输出。
- 下载文件
- 要获取下载文件的路径
- 下载文件名是啥?
- 设置想办法让浏览器能够支持下载我们需要的东西
- 获取下载文件的输入流
- 创建缓冲区
- 获取OutputStream对象
- 将FileOutputStream流写入到buffer缓冲区
- 使用OutputStream将缓冲区中的数据输出到客户端。
package com.zy.response;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 要获取下载文件的路径
String realPath = this.getServletContext().getRealPath("/1.png"); //在文件名钱加/表示在当前web中找
System.out.println("下载的路径是"+realPath);//下载的路径是/Users/zhaoyang/Library/tomcat 9/webapps/response_war/1.png
//而真真路径在Users/zhaoyang/IdeaProjects/javaweb-02-servlet/response/target/classes/1.png,所以在上边语句更换为现在的绝对路径
//2. 下载文件名是啥?
String filename = realPath.substring(realPath.lastIndexOf("//") + 1);//截取路径的最后,/是转义字符,也就是开始截取/后边的字符串
//3. 设置想办法让浏览器能够支持下载我们需要的东西,中文文件名使用URLEncoder.encode(filename,"编码格式utf-8"),也就是替换底下的filename
resp.setHeader("Content-disposition","attachment;filename"+filename); //头信息不用记,直接上网搜web项目下载文件头信息
//4. 获取下载文件的输入流
FileInputStream fileInputStream = new FileInputStream(realPath);
//5. 创建缓冲区
int length = 0;
byte[] buffer = new byte[1024];
//6. 获取OutputStream对象
ServletOutputStream outputStream = resp.getOutputStream();
//7. 将FileOutputStream流写入到buffer缓冲区
while ((length = fileInputStream.read(buffer)) != -1) {
outputStream.write(buffer,0,length);
}
fileInputStream.close();
outputStream.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
3. 验证码功能
验证怎么来的?
- 前端实现
- 后端实现,需要用到Java的图片类,生产一个图片
package com.zy.response;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
public class ImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如何让浏览器3秒刷新一次
resp.setHeader("refresh", "3");
//在内存中创建一个图片
BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);//宽高颜色,空白图片
//得到图片
Graphics graphics = image.getGraphics(); //相当于一个笔
//设置图片的背景颜色
graphics.setColor(Color.WHITE); //把笔设置为白色
graphics.fillRect(0, 0, 80, 20);
//给图片写数据
graphics.setColor(Color.BLUE); //笔变为蓝色
graphics.setFont(new Font(null, Font.BOLD, 20));
graphics.drawString(makeNum(), 0, 20);
//告诉浏览这个请求用图片的方式打开
resp.setContentType("image/jpeg");
//网站存在缓存,不让浏览器缓存
resp.setHeader("expires","-1");
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma", "no-cache");
//把图片写给浏览器
ImageIO.write(image, "jpg", resp.getOutputStream());
}
//生成随机数
private String makeNum() {
Random random = new Random();
String num = random.nextInt(9999999)+""; //理解为串的连接,结果为返回字符串
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 7 - num.length(); i++) { //保证num是七位数
sb.append("0");
}
num = sb.toString() + num; //是字符串连接
return num;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
4.重定向
一个web资源B收到客户端A的请求后,B会通知A客户端去访问另一个web资源C,这个过程叫重定向。
常见场景:
- 用户登录
void sendRedirect(String var1) throws IOException
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//重定向
/*
resp.setHeader("Location", "response_war/image");
resp.setStatus(302);
*/
resp.sendRedirect("/response_war/image"); //response_war是在编辑tomcat里添加war文件的时候设置的路径
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
重定向和转发的区别:
-
相同点:
- 页面都会实现跳转
-
不同点:
- 请求转发的时候,url不会发生改变
- 重定向的时候,url地址栏会发生改变
public class RequestTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username + ":" + password);
//重定向时候一定要注意路径问题,否则404
resp.sendRedirect("/response_war/success.jsp");//这里已经写了重定向路径,所以不用在xml文件里写映射
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
在index.jsp中写上登陆按键,新建一个success.jsp文件,和index.jsp同一级,登陆后会在url地址栏显示用户名和密码
<body>
<h2>Hello World!</h2>
<!--action这里是提交的路径,需要寻找到项目的路径
${pageContext.request.contextPath}代表当前的项目-->
<form action="${pageContext.request.contextPath}/login" method="get">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit">
</form>
</body>
4.7 HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest,通过这个HttpservletRequest的方法,获得客户端的所有信息;
1. 获取前端传递的参数,请求转发
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbys = req.getParameterValues("hobbys");
System.out.println("============================");
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobbys));
//通过请求转发
//这里的/代表当前的web应用
resp.setCharacterEncoding("UTF-8");
req.getRequestDispatcher("/success.jsp").forward(req, resp); //重定向中不用写绝对路径,相对路径就行了
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.zy.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
5 Cookie、Session
5.1 会话
会话:用户打开一个浏览器,点击了很多超链接,访问了多个web资源,再关闭浏览器,这个过程称之为回话。
**有状态回话:**一个同学来过教室,下次再来教室,我们就会知道这个同学曾经来过,这就是有状态回话。
一个网站,怎么证明客户来过?
客户端 服务端
- 服务端给客户端一个信件,客户端下次访问服务端带上信件就可以了;Cookie
- 服务器端登记你来过,下次你来的时候我来匹配你;Session
5.2 保存会话的两种技术
cookie
- 客户端技术(响应,请求)
session
- 服务器技术,利用这个技术可以保存用户的会话信息。我们可以把信息或者数据放在session中。
常见场景:网站登陆之后,你下次不用再登陆了,第二次访问直接就上去了!
5.3 Cookie
- 从请求中拿到cookie信息
- 服务器响应给客户端cookie
Cookie[] cookies = req.getCookies(); //获得cookie
cookie.getName(); //获得cookie中的key
cookie.getValue(); //获得cookie中的value
new Cookie("lastLoginTime", System.currentTimeMillis()+""); //新建一个cookie
cookie.setMaxAge(24*60*60); //给cookie设置有效期
resp.addCookie(cookie); //响应给客户端一个cookie
//保存上一次访问的时间
public class CookieDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//服务器把你来的时间封装成一个信件,下次你来的时候带着,服务器就知道是你来了
//解决中文乱码
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
PrintWriter out = resp.getWriter();
//Cookie,服务器端从客户端获取
Cookie[] cookies = req.getCookies(); //返回的是一个数组,说明cookie可能存在多个
//判断cookie是否存在
if (cookies != null) {
//如果存在
out.write("上一次访问的时间是:");
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
if (cookie.getName().equals("lastLoginTime")){
//获取cookie中的值
//cookie.getValue();
long lastLoginTime = Long.parseLong(cookie.getValue());
Date date = new Date(lastLoginTime); //util包下的
out.write(date.toLocaleString());
}
}
}else {
out.write("这是你第一次访问本网站");
}
//服务器给客户端响应一个cookie,需要什么参数就new什么参数
Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis()+"");//变成字符串类型
cookie.setMaxAge(24*60*60); //给cookie设置有效期为24小时,一秒为单位
resp.addCookie(cookie); //给客户端响应一个cookie
//resp.addCookie(new Cookie("name","赵阳"));
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
一个网站cookie是否存在上限!
- 一个cookie只能保存一个信息;
- 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie;
- Cookie大小有限制20KB;
- 300个cookie浏览器上限
删除cookie
- 不设置有效期的关闭浏览器,cookie自动失效;
- 设置有效期为0
public class CookieDemo02 extends HttpServlet{
public class CookieDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建一个cookie,名字必须要和要删除的cookie名字一致
Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
cookie.setMaxAge(0); //将cookie有效期设置为0,立即过期
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
}
编码解码
URLEncoder.encode("汉字","utf-8")
URLDecoder.decode(cookie.getValue(),"utf-8")
7.4 Session(重点)
什么事session:
- 服务器会给每个用户(浏览器)创建一个Session对象;
- 一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在;
- 用户登陆之后,整个网站都可以访问,在这个网站的每个页面都是登陆状态(许多请求共用了一个用户),保存用户的信息,保存购物车的信息
ok之后可以点击运行会在浏览器显示hello world!显示的就是默认的index.jsp里的内容。
对于war包,我们用哪个项目就创建哪个war包,像目前我们一个一项目里有多个module,所以每一个module对应一个war包,保证项目里只有一个war包,这样比较快。
3.5 maven侧边栏的使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zsxTBNro-1678368461387)(image/maven%E4%BE%A7%E8%BE%B9%E6%A0%8F.png)]
3.6 pom文件
pom.xml是maven的核心配置文件
maven由于预定大于配置,我们之后可能遇到我们写的配置文件不能导出或者生效的问题,解决方案在build标签里配置resources
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
4 Servlet
4.1 servlet简介
- Servlet是sun公司开发动态web的一门技术
- Sun公司在这些API中提供了一个接口叫做:servlet,如果你想开发一个Servlet程序,只需要完成两个步骤:
- 编写一个类,实现Servlet接口
- 把开发好的java类部署到web服务器中
实现了Servlet接口的Java程序叫做Servlet。
4.2 Hello Servlet
- 构建一个普通的maven项目,**就是不选wepapp这个选项。**删掉里边的src目录,以后我们的许诶下就在这个项目里建立Module,这个空的工程就是maven主工程。
- 创建好后最好把依赖都写进pom.xml里,就是maven servlet jsp和jsp-api的依赖。最新的可以去https://mvnrepository.com/官网复制,在官网的搜索框输入Javax.Servlet-api
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
-
在项目名中右键新建一个module。因为开始我们新建的是一个普通的maven项目,现在在这个普通maven项目里新建webapp项目。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lx2y4fmd-1678368461387)(image/maven%E9%A1%B9%E7%9B%AE%E5%88%9B%E5%BB%BA.png)]
这样创建其实就是为了把更多的项目汇总起来放在一个大的目录下,当然也可以不需要父项目,直接创建maven的webapp项目。
- 关于maven父子项目的理解
父项目中会有:
<modules>
<module>servlet-01</module>
</modules>
子项目中会有:
<parent>
<artifactId>javaweb-02-servlet</artifactId>
<groupId>zy.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
父项目中的jar包子项目可以直接使用。
- 要在WEB-INF目录下的web.xml内容换成最新的。可以在下载的tomcat目录下webapps\ROOT\WEB-INF里的web.xml里复制。
- 将项目里的目录结构补充完整。
- 5和6称之为maven项目优化
- 在Java目录下就可新建class文件或者package。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kA7FCG7F-1678368461387)(image/%E7%88%B6%E5%AD%90%E9%A1%B9%E7%9B%AE%E7%9B%AE%E5%BD%95.png)]
4.2.1 编写一个servlet程序
servlet接口sun公司有两个默认的实现类,HttpServlet和GenericServlet,主要用HttpServlet。
- 编写一个普通类;
- 实现Servlet接口,直接继承HttpServlet类;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jhQV2zOK-1678368461388)(image/servlet%E6%8E%A5%E5%8F%A3%E5%AE%9E%E7%8E%B0%E7%B1%BB.png)]
- 编写servlet类
package com.zy.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class HelloServlet extends HttpServlet {
//由于get或者post只是请求实现的不同方式,可以互相调用,业务逻辑一样;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PrintWriter writer = resp.getWriter(); //响应流
writer.print("Hello Servlet!");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
- 编写Servlet的映射
为什么需要映射:我们写的是Java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,我们需要在web服务器中注册我们写的Servlet,还需要给他一个浏览器能够访问的路径;(写在web.xml里)
<!--注册servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.zy.servlet.HelloServlet</servlet-class>
</servlet>
<!--servlet的请求路径-->
<!--localhost:8080/servlet_01_war/hello-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern> <!--请求路径一定要加/-->
</servlet-mapping>
- 配置tomcat
注意:配置项目发布的路径,就是上边图片里最底下那个路径。
- 启动项目
4.3 servlet原理
servlet是由web服务器调用,web服务器在收到浏览器请求后,会:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xpDxeKQt-1678368461388)(image/servlet%E5%8E%9F%E7%90%86.png)]
4.4 mapping问题
- 一个servlet可以指定一个映射路径
<!--servlet的请求路径-->
<!--localhost:8080/servlet_01_war/hello-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
- 一个servlet可以指定多个映射路径
<!--servlet的请求路径-->
<!--localhost:8080/servlet_01_war/hello-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello2</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello3</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello4</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
- 一个servlet可以指定通用映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
*是通配符,所有hello/后面写还是不写
默认路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
- 指定一些后缀或者前缀等等…
<!--可以自定义后缀实现请求映射
注意点:自定义后缀情况下,*前面不能加项目映射路径,也就是/符号以及/hello-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>*.zy</url-pattern>
</servlet-mapping>
4.4.1 404异常处理
也就是当输入错误路径就会进入当前设置的默认404页面,输入的路径正确就会进入指定的正确路径。
public class ErrorServlet extends HelloServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
PrintWriter writer = resp.getWriter();
writer.print("<h1>404<h1>");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<servlet>
<servlet-name>error</servlet-name>
<servlet-class>com.zy.servlet.ErrorServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>error</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
这里配的url是/*所以一运行就会进error页面。url就是指定的映射路径。
优先级:指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求,也就是上边提到的默认路径,使用通配符。
4.5 ServletContext
web容器在启动时,它会为每一个web程序创建一个对应的ServletContext对象,它代表了当前的web应用;
1.共享数据
- 我们在这个Servlet中保存的数据,可以在另外一个servlet中拿到;一个servlet把数据会放到ServletContext中,其它servlet可以到ServletContext中拿到。
放置数据的类:
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//this.getServletContext(); servlet上下文
ServletContext context = this.getServletContext();
String username = "zy";
context.setAttribute("username",username);//将一个数据保存在ServletContext中,名字是username,值是变量username,是一个键值对
}
}
读取数据的类:
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String username = (String) context.getAttribute("username");
resp.setContentType("text/http");
resp.setCharacterEncoding("utf-8");
resp.getWriter().print("名字是:"+username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
配置web.xml
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>gets</servlet-name>
<servlet-class>com.zy.servlet.GetServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>gets</servlet-name>
<url-pattern>/gets</url-pattern>
</servlet-mapping>
测试访问进入到/hello路径设置数据,进入到/gets读出数据。
2. 获取初始化参数
可以在web.xml里初始化一个参数:
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String url = context.getInitParameter("url");
resp.getWriter().print(url);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
3 请求转发
public class ServletDemo04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext(); //现在展示的是把这几行话分开写,当然也可以写成一句话
System.out.println("进入了ServletDemo04");
RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp"); //转发的请求路径
requestDispatcher.forward(req,resp);
//context.getRequestDispatcher("/gp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
<servlet>
<servlet-name>sd4</servlet-name>
<servlet-class>com.zy.servlet.ServletDemo04</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>sd4</servlet-name>
<url-pattern>/sd4 </url-pattern>
</servlet-mapping>
请求的是/sd4,但是显示的是/gp页面,而且路径还是/sd4不发生改变,但是重定向的路径是会发生改变的。
4 读取资源文件
Properties
- 在Java目录下新建properties
- 在resources目录下新建properties
发现:都被打包到了同一路径下:classes。我么俗称这个路径为classpath;
思路:需要一个文件流
username = root
password = 123456
public class ServletDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
InputStream resourceAsStream = context.getResourceAsStream("WEB-INF/classes/db.properties"); //返回一个流
Properties properties = new Properties();
properties.load(resourceAsStream); //拿到文件的流
String username = properties.getProperty("username");
String password = properties.getProperty("password");
resp.getWriter().print(username+","+password);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<servlet>
<servlet-name>sd5</servlet-name>
<servlet-class>com.zy.servlet.ServletDemo05</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>sd5</servlet-name>
<url-pattern>/sd5 </url-pattern>
</servlet-mapping>
访问测试即可ok;
4.6 HttpServletResponse
创建新module要修改的有webapp.xml和添加新的artifact文件,就是:war文件
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的一个HttpServletResponse;
- 如果要获取客户端请求过来的参数:找HttpServletRequest
- 如果要给客户端相应一些信息:找HttpServletResponse
1.简单分类
负责向浏览器发送数据的方法
ServletOutputStream getOutStream() throws IOException; //写一般的流用这个
PrintWriter getWriter() throw IOException; //写中文用这个
向浏览器发送相应头的方法
void setCharacterEncoding(String Var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
void setDateHeader(String var1,long var2);
void addDateHeader(String var1,long var2);
2. 下载文件
- 向浏览器输出消息(前面有),就是在浏览器页面上输出,sout是在IDEA的控制台输出。
- 下载文件
- 要获取下载文件的路径
- 下载文件名是啥?
- 设置想办法让浏览器能够支持下载我们需要的东西
- 获取下载文件的输入流
- 创建缓冲区
- 获取OutputStream对象
- 将FileOutputStream流写入到buffer缓冲区
- 使用OutputStream将缓冲区中的数据输出到客户端。
package com.zy.response;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 要获取下载文件的路径
String realPath = this.getServletContext().getRealPath("/1.png"); //在文件名钱加/表示在当前web中找
System.out.println("下载的路径是"+realPath);//下载的路径是/Users/zhaoyang/Library/tomcat 9/webapps/response_war/1.png
//而真真路径在Users/zhaoyang/IdeaProjects/javaweb-02-servlet/response/target/classes/1.png,所以在上边语句更换为现在的绝对路径
//2. 下载文件名是啥?
String filename = realPath.substring(realPath.lastIndexOf("//") + 1);//截取路径的最后,/是转义字符,也就是开始截取/后边的字符串
//3. 设置想办法让浏览器能够支持下载我们需要的东西,中文文件名使用URLEncoder.encode(filename,"编码格式utf-8"),也就是替换底下的filename
resp.setHeader("Content-disposition","attachment;filename"+filename); //头信息不用记,直接上网搜web项目下载文件头信息
//4. 获取下载文件的输入流
FileInputStream fileInputStream = new FileInputStream(realPath);
//5. 创建缓冲区
int length = 0;
byte[] buffer = new byte[1024];
//6. 获取OutputStream对象
ServletOutputStream outputStream = resp.getOutputStream();
//7. 将FileOutputStream流写入到buffer缓冲区
while ((length = fileInputStream.read(buffer)) != -1) {
outputStream.write(buffer,0,length);
}
fileInputStream.close();
outputStream.close();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
3. 验证码功能
验证怎么来的?
- 前端实现
- 后端实现,需要用到Java的图片类,生产一个图片
package com.zy.response;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
public class ImageServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如何让浏览器3秒刷新一次
resp.setHeader("refresh", "3");
//在内存中创建一个图片
BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);//宽高颜色,空白图片
//得到图片
Graphics graphics = image.getGraphics(); //相当于一个笔
//设置图片的背景颜色
graphics.setColor(Color.WHITE); //把笔设置为白色
graphics.fillRect(0, 0, 80, 20);
//给图片写数据
graphics.setColor(Color.BLUE); //笔变为蓝色
graphics.setFont(new Font(null, Font.BOLD, 20));
graphics.drawString(makeNum(), 0, 20);
//告诉浏览这个请求用图片的方式打开
resp.setContentType("image/jpeg");
//网站存在缓存,不让浏览器缓存
resp.setHeader("expires","-1");
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma", "no-cache");
//把图片写给浏览器
ImageIO.write(image, "jpg", resp.getOutputStream());
}
//生成随机数
private String makeNum() {
Random random = new Random();
String num = random.nextInt(9999999)+""; //理解为串的连接,结果为返回字符串
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 7 - num.length(); i++) { //保证num是七位数
sb.append("0");
}
num = sb.toString() + num; //是字符串连接
return num;
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
4.重定向

一个web资源B收到客户端A的请求后,B会通知A客户端去访问另一个web资源C,这个过程叫重定向。
常见场景:
- 用户登录
void sendRedirect(String var1) throws IOException
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//重定向
/*
resp.setHeader("Location", "response_war/image");
resp.setStatus(302);
*/
resp.sendRedirect("/response_war/image"); //response_war是在编辑tomcat里添加war文件的时候设置的路径
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
重定向和转发的区别:
-
相同点:
- 页面都会实现跳转
-
不同点:
- 请求转发的时候,url不会发生改变
- 重定向的时候,url地址栏会发生改变
public class RequestTest extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
String password = req.getParameter("password");
System.out.println(username + ":" + password);
//重定向时候一定要注意路径问题,否则404
resp.sendRedirect("/response_war/success.jsp");//这里已经写了重定向路径,所以不用在xml文件里写映射
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
在index.jsp中写上登陆按键,新建一个success.jsp文件,和index.jsp同一级,登陆后会在url地址栏显示用户名和密码
<body>
<h2>Hello World!</h2>
<!--action这里是提交的路径,需要寻找到项目的路径
${pageContext.request.contextPath}代表当前的项目-->
<form action="${pageContext.request.contextPath}/login" method="get">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit">
</form>
</body>
4.7 HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest,通过这个HttpservletRequest的方法,获得客户端的所有信息;
1. 获取前端传递的参数,请求转发
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
String[] hobbys = req.getParameterValues("hobbys");
System.out.println("============================");
System.out.println(username);
System.out.println(password);
System.out.println(Arrays.toString(hobbys));
//通过请求转发
//这里的/代表当前的web应用
resp.setCharacterEncoding("UTF-8");
req.getRequestDispatcher("/success.jsp").forward(req, resp); //重定向中不用写绝对路径,相对路径就行了
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.zy.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
5 Cookie、Session
5.1 会话
会话:用户打开一个浏览器,点击了很多超链接,访问了多个web资源,再关闭浏览器,这个过程称之为回话。
有状态会话:一个同学来过教室,下次再来教室,我们就会知道这个同学曾经来过,这就是有状态回话。
一个网站,怎么证明客户来过?
客户端 服务端
- 服务端给客户端一个信件,客户端下次访问服务端带上信件就可以了;Cookie
- 服务器端登记你来过,下次你来的时候我来匹配你;Session
5.2 保存会话的两种技术
cookie
- 客户端技术(响应,请求)
session
- 服务器技术,利用这个技术可以保存用户的会话信息。我们可以把信息或者数据放在session中。
常见场景:网站登陆之后,你下次不用再登陆了,第二次访问直接就上去了!
5.3 Cookie
- 从请求中拿到cookie信息
- 服务器响应给客户端cookie
Cookie[] cookies = req.getCookies(); //获得cookie
cookie.getName(); //获得cookie中的key
cookie.getValue(); //获得cookie中的value
new Cookie("lastLoginTime", System.currentTimeMillis()+""); //新建一个cookie
cookie.setMaxAge(24*60*60); //给cookie设置有效期
resp.addCookie(cookie); //响应给客户端一个cookie
//保存上一次访问的时间
public class CookieDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//服务器把你来的时间封装成一个信件,下次你来的时候带着,服务器就知道是你来了
//解决中文乱码
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
PrintWriter out = resp.getWriter();
//Cookie,服务器端从客户端获取
Cookie[] cookies = req.getCookies(); //返回的是一个数组,说明cookie可能存在多个
//判断cookie是否存在
if (cookies != null) {
//如果存在
out.write("上一次访问的时间是:");
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
if (cookie.getName().equals("lastLoginTime")){
//获取cookie中的值
//cookie.getValue();
long lastLoginTime = Long.parseLong(cookie.getValue());
Date date = new Date(lastLoginTime); //util包下的
out.write(date.toLocaleString());
}
}
}else {
out.write("这是你第一次访问本网站");
}
//服务器给客户端响应一个cookie,需要什么参数就new什么参数
Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis()+"");//变成字符串类型
cookie.setMaxAge(24*60*60); //给cookie设置有效期为24小时,一秒为单位
resp.addCookie(cookie); //给客户端响应一个cookie
//resp.addCookie(new Cookie("name","赵阳"));
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
一个网站cookie是否存在上限!
- 一个cookie只能保存一个信息;
- 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie;
- Cookie大小有限制20KB;
- 300个cookie浏览器上限
删除cookie
- 不设置有效期的关闭浏览器,cookie自动失效;
- 设置有效期为0
public class CookieDemo02 extends HttpServlet{
public class CookieDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建一个cookie,名字必须要和要删除的cookie名字一致
Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
cookie.setMaxAge(0); //将cookie有效期设置为0,立即过期
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
}
编码解码
URLEncoder.encode("汉字","utf-8")
URLDecoder.decode(cookie.getValue(),"utf-8")
5.4 Session(重点)
什么事session:
- 服务器会给每个用户(浏览器)创建一个Session对象;
- 一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在;
- 用户登陆之后,整个网站都可以访问,在这个网站的每个页面都是登陆状态(许多请求共用了一个用户),保存用户的信息,保存购物车的信息
改代码用来写session
public class SessionDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决乱码问题
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
//得到session
HttpSession session = req.getSession();
//给session中存东西
session.setAttribute("name", "zy");
//获取session的ID
String sessionId = session.getId();
//判断session是不是新创建的
if (session.isNew()) {
resp.getWriter().write("session创建成功,ID:" + sessionId);
} else {
resp.getWriter().write("session已经在服务器中存在了,ID:" + sessionId);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
读取上边session里的name,但是要先请求s1再请求s2,要不然输出的就是null。
public class SessionDemo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决乱码问题
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
//得到session
HttpSession session = req.getSession();
String name = (String) session.getAttribute("name");
System.out.println(name);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
注销session,每个浏览器保存一个session,注销之后,sessionID就会消失。
public class SessionDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
session.removeAttribute("name");
session.invalidate(); //注销
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
在web.xml里设置session的有效时间
<!--设置session默认的失效时间-->
<session-config>
<!--1分钟后session自动失效,以分钟为单位-->
<session-timeout>1</session-timeout>
</session-config>
8 JSP
8.1 什么是JSP
Java Server Page:Java服务器端页面,和servlet一样,用于动态web技术!
最大的特点:
- 写jsp就像是在写HTML
- 区别
- HTML只能给用户提供静态的数据
- JSP页面中可以嵌入java代码,为用户提供动态数据
8.2 JSP原理
思路:JSP到底是怎么执行的
- 代码层面没有任何问题
- 服务器内部工作:
tomcat中有一个work目录;
IDEA中使用tomcat的会在IDEA的tomcat中产生一个work目录
发现jsp页面转变成了java程序!
注意:浏览器像服务器发送请求,不管访问什么资源,其实都是在访问servlet!
jsp最终也会转换成一个java类
jsp本质上就是一个servlet
在JSP页面中:
只要是java代码就会原封不懂的输出;
如果是HTML代码,就会转换为:
out.write("<html>\r\n);
这样的格式,输出到前端!
8.3 JSP基础语法
JSP表达式
<%--JSP表达式
作用:用来将程序的输出,输出到客户端
<%=变量或表达式%>
--%>
<%= new java.util.Date()%>
JSP脚本片段
<%--jsp脚本片段--%>
<%
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
out.println("<h1>sum="+sum+"</h1>");
%>
JSP声明
<%!
static {
System.out.println("Loading Servlet");
}
private int globalVar = 0;
public void kuang() {
System.out.println("进入到了方法kuang!");
}
%>
JSP声明:会编译到JSP生成的Java类中!
上边两个标题的内容生成到_jspService方法中!
JSP的注释不会在客户端心事,HTML注释会在客户端查看代码中显示。
8.4 JSP指令
<%@page ...%>
<%--定制错误页面--%>
<%@page errorPage="error/500.jsp" %>
网页公用体
就是页面最上面和最下面在每一个页面都是一样的。
1.使用jsp命令
header.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>我是Header</h1>
</body>
</html>
Footer.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>我是Footer</h1>
</body>
</html>
jsp3.jsp
<%@include file="comment/header.jsp"%>
<h1>网页主体</h1>
<%@include file="comment/Footer.jsp"%>
2. jsp标签
<%--jsp标签--%>
<jsp:include page="/comment/header.jsp"/>
<h1>网页主体</h1>
<jsp:include page="/comment/Footer.jsp"/>
8.5 9大内置对象
- PageContext 存东西
<body>
<%--内置对象--%>
<%
pageContext.setAttribute("name1", "zy1");
request.setAttribute("name2", "zy2");
session.setAttribute("name3", "zy3");
application.setAttribute("name4", "zy4");
%>
<%--脚本片段中的代码,会被原封不动的生成.jsp.java
要求:这里的代码,必须保证Java语法的正确性
--%>
<%
//从pageContext取出,通过寻找的方式来
//从底层到高层(作用域)
// pageContext.getAttribute("name1");
String name1 = (String) pageContext.findAttribute("name1");
String name2 = (String) pageContext.findAttribute("name2");
String name3 = (String) pageContext.findAttribute("name3");
String name4 = (String) pageContext.findAttribute("name4");
String name5 = (String) pageContext.findAttribute("name5"); //不存在
%>
<%--使用EL表达式输出 ${} --%>
<h1>取出来的值</h1>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<h3>${name5}</h3>
</body>
要是使用EL表达式没有效果,需要加上isELIgnored=“false”
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
- Request 存东西
- Response
- Session 存东西
- Application 【ServletContext】 存东西
- config 【ServletConfig】
- out
- page 几乎不用
- exception
<%--内置对象--%>
<%
pageContext.setAttribute("name1", "zy1"); //保存的数据只在一个页面内有效
request.setAttribute("name2", "zy2"); //保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3", "zy3"); //保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name4", "zy4"); //保存的数据知道服务器中有效,从打开服务器到关闭服务器
%>
request:客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻;
session:客户端向服务器发送请求,产生的数据,用户用完一会还有用,比如:购物车;
application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用,比如:聊天数据。
8.6 JSP标签、JSTL标签、EL表达式
<!--jsp依赖-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
</dependency>
<!--JSTL表达式的依赖-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--standard标签库-->
<!-- https://mvnrepository.com/artifact/taglibs/standard -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
EL表达式: ${ }
- 获取数据
- 执行运算
- 获取web开发的常用对象
JSP标签
<jsp:forward page="/jsptag2.jsp">
<jsp:param name="name" value="zhaoyang"/>
<jsp:param name="age" value="18"/>
</jsp:forward>
名字:<%=request.getParameter("name")%>
年龄:<%=request.getParameter("age")%>
JSTL标签
JSTL标签库的使用就是为了弥补HTML标签的不足;他自定义许多标签,可以供我们使用,标签的功能和Java代码一样。
JSTL标签库
JSTL 核心(core)标签是最常用的 JSTL 标签,导入核心标签库的语法如下(引入JSTL核心标签库,才能使用JSTL标签):
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
JSTL标签库使用步骤
- 引入对应的taglib
- 使用其中的方法
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h4>if测试</h4>
<hr>
<form action="coreif.jsp" method="get">
<%--
EL表达式获取表单数据
${param.参数名}
--%>
<input type="text" name="username" value="${param.username}">
<input type="submit" value="登陆">
</form>
<%--判断如果提交的用户名是管理员,则登录成功--%>
<c:if test="${param.username=='admin'}" var="isAdmin">
<c:out value="欢迎您,管理员!"/>
</c:if>
<c:out value="${isAdmin}"/>
</body>
</html>
when标签
<%@taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<%--出现 根据标记文件中的TLD或attribute指令,attribute[test]不接受任何表达式这个报错,所以把上面的core改为core_rt--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--定义一个变量score,值为85--%>
<c:set var="score" value="85"/>
<c:choose>
<c:when test="${score>=90}">
你的成绩为优秀
</c:when>
<c:when test="${score>=80}">
你的成绩为良好
</c:when>
<c:when test="${score>=70}">
你的成绩为一般
</c:when>
<c:when test="${score>=60}">
你的成绩为及格
</c:when>
</c:choose>
</body>
</html>
9 JavaBean
实体类
JavaBean有特定的写法:
- 必须要有一个无参构造
- 属性必须私有化
- 必须有对应的get/set方法
一般用来和数据库的字段做映射 ORM;
ORM:对象关系映射 - 表—>类
- 字段—>属性
- 行记录—>对象
people表
id | name | age | address |
---|---|---|---|
1 | ken | 3 | 西安 |
2 | ken2 | 18 | 西安 |
3 | ken3 | 100 | 西安 |
class People{
private int id;
private String name;
private int age;
private String address;
}
class A{
new People(1, "ken", 3, "西安");
new People(2, "ken2", 18, "西安");
new People(3, "ken3", 100, "西安");
}
<%@ page import="com.zy.pojo.People" %>
<%@taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
// People people = new People();
// people.setAddress("西安");
// people.setAge(18);
// people.setId(1);
// people.setName("ken");
%>
<jsp:useBean id="people" class="com.zy.pojo.People" scope="page"/> <%--这句相当于上面创建对象的那句--%>
<jsp:setProperty name="people" property="address" value="西安"/>
<jsp:setProperty name="people" property="id" value="1"/>
<jsp:setProperty name="people" property="age" value="18"/>
<jsp:setProperty name="people" property="name" value="ken"/>
<%--<%=people.getAddress%>--%>
地址:<jsp:getProperty name="people" property="address"/>
ID:<jsp:getProperty name="people" property="id"/>
年龄:<jsp:getProperty name="people" property="age"/>
姓名:<jsp:getProperty name="people" property="name"/>
</body>
</html>
10 MVC三层架构
Model view controller 模型 视图 控制器
10.1 早些年
servlet--->CRUD(增删改查)--->数据库
弊端:JDBC的代码得写在servlet里,程序十分臃肿,不利于维护。
10.2 MVC三层架构
Model
- 业务处理:业务逻辑(service)
- 数据持久层:CRUD(Dao)
View - 展示数据
- 提供链接发起Servlet请求(a,form,img…)
Controller - 接受用户的请求:(req:请求参数、Session信息)
- 交给业务层处理对应的代码
- 控制视图的跳转
登陆--->接受用户的登录请求--->处理用户的请求(获取用户登录的参数,username,password)--->交给业务层处理登陆业务(判断用户名密码是否正确:事物)--->Dao层查询用户名和密码是否正确--->数据库
11 Filter 过滤器
Filter:过滤器,用来过滤网站的数据
Filter开发过程
- 导包
- 编写过滤器
一个servlet
package com.zy.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class ShowServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("你好啊,世界!");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
package com.zy.filter;
import javax.servlet.*;
import java.io.IOException;
//编码过滤器
public class CharacterEncodingFilter implements Filter {
//初始化:web服务器启动,就已经初始化了,随时等待过滤对象出现!
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("CharacterEncodingFilter已经初始化了");
}
//chain 链
/*
1.过滤中的所有代码,在过滤特定请求的时候都会执行,特定请求就是在web.xml中写的filter
2.必须让过滤器同行
chain.doFilter(request,response);
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=UTF-8");
System.out.println("CharacterEncodingFilter执行前");
chain.doFilter(request,response); //让我们的请求继续走,如果不写,程序到这里就被拦截停止!
System.out.println("CharacterEncodingFilter执行后");
}
//销毁:web服务器关闭的时候,过滤器会销毁
public void destroy() {
System.out.println("CharacterEncodingFilter销毁");
}
}
- 在web.xml中配置Filter
web.xml
<servlet>
<servlet-name>ShowServlet</servlet-name>
<servlet-class>com.zy.servlet.ShowServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ShowServlet</servlet-name>
<url-pattern>/servlet/show</url-pattern>
</servlet-mapping>
<filter>
<filter-name>CharaterEncodingFilter</filter-name>
<filter-class>com.zy.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharaterEncodingFilter</filter-name>
<!--只要是/servlet路径下的任何请求,都会经过这个过滤器-->
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
11.1 过滤器应用 权限拦截
package com.zy.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class SysFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
// ServletRequest HttpServletrequest
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
if (request.getSession().getAttribute("USER_SESSION") == null) {
response.sendRedirect("/error.jsp");
}
chain.doFilter(req,resp);
}
public void destroy() {
}
}
12 JDBC
什么是JDBC:Java链接数据库
需要jar包的支持
- java.sql
- javax.sql
- mysql-connecter-java… 连接驱动(必须要导入)
实验环境搭建
CREATE TABLE `users` (
`id` int NOT NULL,
`name` varchar(45) DEFAULT NULL,
`password` varchar(45) DEFAULT NULL,
`email` varchar(60) DEFAULT NULL,
`birthday` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id`)
导入数据库依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
JDBC固定步骤
- 加载驱动
- 连接数据库,代表数据库
- 向数据库发送sql的对象Statement,用来CRUD
- 编写sql(根据业务,不同sql)
- 执行sql
- 关闭连接
package com.zy.test;
import java.sql.*;
public class TestJdbc {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//配置信息
//useUnicode=true&characterEncoding=utf-8 解决中文乱码
String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
String username = "root";
String password = "12345678";
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//2.连接数据库,代表数据库
Connection connection = DriverManager.getConnection(url, username, password);
//3.像数据库发送SQL的对象statement 用来做CRUD
Statement statement = connection.createStatement();
//4.编写sql语句
String sql = "select * from users";
//5.执行查询sql,返回一个ResultSet:结果集
ResultSet rs = statement.executeQuery(sql);
while (rs.next()) {
System.out.println("id=" + rs.getObject("id"));
System.out.println("name=" + rs.getObject("name"));
System.out.println("email=" + rs.getObject("email"));
System.out.println("birthday=" + rs.getObject("birthday"));
}
//6.关闭连接,释放资源(一定要做) 先开后关 先创建的最后关闭
rs.close();
statement.close();
connection.close();
}
}
12.1 事务
要么都成功,要么都失败!
ACID原则:保证数据的安全。
ACID原则是数据库事务正常执行的四个,分别指原子性、一致性、独立性及持久性
- 原子性(Atomicity):是指一个事务要么全部执行,要么不执行,也就是说一个事务不可能只执行了一半就停止了。比如你从取款机取钱,这个事务可以分成两个步骤:1划卡,2出钱。不可能划了卡,而钱却没出来。这两步必须同时完成,要么就不完成。
- 一致性(Consistency):是指事务的运行并不改变数据库中数据的一致性。例如,完整性约束了a+b=10,一个事务改变了a,那么b也应该随之改变。
- 独立性(Isolation):事务的独立性也称作隔离性,是指两个以上的事务不会出现交错执行的状态。因为这样可能会导致数据不一致,更加具体的来讲,就是事务之间的操作是独立的。
- 持久性(Durability):事务的持久性是指事务执行成功以后,该事务对数据库所作的更改便是持久的保存在数据库之中,不会无缘无故的回滚。
开启事务
事务提交 commit()
事务回滚 rollback()
关闭事务
举例:转账
Junit单元测试
在任何方法中写@test注解就可以允许该方法,不用在写main方法调用
依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
package com.zy.test;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class TestJDBC2 {
@Test
public void test() throws ClassNotFoundException, SQLException {
String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
String username = "root";
String password = "12345678";
Connection connection = null;
try {
//1.加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.连接数据库,代表数据库
connection = DriverManager.getConnection(url, username, password);
//3.通知数据库开始事务,false 开启
connection.setAutoCommit(false);
String sql = "update account set money=money-100 where name = 'A'";
connection.prepareStatement(sql).executeUpdate();
//制造错误
//int i = 1 / 0;
String sql2 = "update account set money=money+100 where name = 'B'";
connection.prepareStatement(sql2).executeUpdate();
connection.commit();
} catch (Exception e) {
try {
//如果出现异常,就通知数据库回滚
connection.rollback();
} catch (Exception e2) {
e2.printStackTrace();
}
e.printStackTrace();
}finally {
connection.close();
}
}
}