JavaWeb
本篇Web以 html5 + css3 +js为基础
html5:html5回顾
css3:css3回顾
JS:JS回顾
文章目录
1、基本概念
1.1、前言
-
静态web
- html,css
- 提供给所有人看的数据始终不会发生变化
-
动态web
- 淘宝
- 提供给所有人看的数据始终会发生变化,每个人在不同的时间,不同的地点看到的信息各不同!
- 技术栈:Servlet/JSP,ASP,PHP
在Java中,动态web资源开发的技术统称为JavaWeb;
1.2、web应用程序
web应用程序:可以提供浏览器访问的程序;
- a.html、b.html…多个web资源,这些web资源可以被外界资源访问
1.3、静态web
- 如果服务器上一致存在这些东西,我们就可以进行读取
一次请求与响应:
- 静态web存在的缺点
-
Web页面无法动态更新,所有用户看到的都是同一个页面
- 轮播图,点击特效:伪动态
- JavaScript [实际开发中,它用的最多]
-
它无法和数据库交互(数据无法持久化,用户无法交互)
-
1.4、动态web
页面会动态展示:“Web的页面展示的效果因人而异”
缺点:
- 加入服务器的动态web资源出现了错误,我们需要重新编写我们的后台程序
- 停机维护
优点:
- 可以动态更新,每个用户看到的是不一样的页面
- 可以数据交互(数据持久化:注册-登录,商品信息,用户信息)
2、Web服务器
服务器是一种被动的操作,用来处理用户的一些请求和给用户一些响应信息;
Tomcat
面向百度编程:
最新的Servlet和JSP 规范总是能在Tomcat 中得到体现。性能稳定,免费,目前比较流行的Web应用服务器。
工作3-5年之后,可以尝试手写Tomcat服务器;
3、Tomcat
可以配置启动的端口号
- tomcat的默认端口号为:8080
- mysql:3306
- http:80
- https:443
查看网页时,F12,Network-Name中即可查看 Remote Address
可以配置主机的名称
- 默认的主机名为:localhost -> 127.0.0.1
- 默认的网站应用存放的位置为:webapps
高难度面试题
请你谈谈网站是如何进行访问的。
-
输入一个域名;回车
-
检查本机的 【地址】 配置文件下有没有这个域名映射;
- 有:直接返回对应的ip地址,这个地址有我们需要访问的web程序,可以直接访问
- 没有:去DNS服务器(全世界的域名都在这里管理)找,找到的话就返回,找不到就返回找不到(报错)
网页该有的结构
--webapps : Tomcat服务器的web目录
--ROOT
-xx : 网站的目录名
- WEB-INF
- web.xml
- lib:web应用所依赖的jar包
- index.html 默认的首页
- static(资源文件)
- css
- style.css
- js
4、Http
4.1、什么是HTTP
HTTP(超文本传输协议)是一个简单的请求-响应协议,它通常运行在TCP之上。
- 文本:html,字符串
- 超文本:图片,音乐,视频,定位,地图(超过文本之上)
http:80
https:443
s-安全的
4.2、Http请求与响应
客户端 -> 发请求(Request) -> 服务器
Request URL:请求地址
Request Method: get/post方法
Status Code:200 状态码:200
Remote(远程)Address:
服务器 -> 响应 -> 客户端
Cache-Control: 缓存控制
Connection:Keep-Alive 连接
Content-Encoding:gzip 编码
Content-Type:text/html 类型
4.3、请求行
1、请求方式:
get:请求携带参数,大小有限制,高效,不安全
post:没有参数,无限制,安全,不高效
2、响应体
Accept:告诉浏览器,所支持的数据类型
Accept-Encoding:支持那种编码格式 GBR UTF-8 GB2312 ISO8859-1
Accpet-Language:告诉浏览器的语言环境
Cache-Control:缓存控制
Connection:告诉浏览器请求完成后,断开还是保持连接
HOST:主机
Refresh:告诉客户端,多久刷新一次
Location:让网页重新定位
3、响应状态码(重点)
200:请求响应成功
3xx:请求重定向
4xx:找不到资源 404
5xx:服务器代码错误 500 502[网关错误]
4.4、两个时代
- http1.0
- HTTP/1.0:客户端可以与web服务器连接后,只能获取一个web资源,断开连接
- http2.0
- HTTP/1.1:客户端可以与web服务器连接后,可以获得多个web资源,
常见面试题
当你的浏览器中地址栏输入地址并回车的一瞬间到页面能够展示回来,经历了什么?
答:在我们输入网址按下回车后,DNS服务器会解析这一网址的ip。解析IP后,浏览器会像服务器发起一个tcp连接请求,请求包括三次握手。
- 第一次握手:建立链接时,客服端浏览器会发送syn包到服务器,并进入SYS_SENT状态,等待服务器的确认。system
- 第二次握手:服务器收到syn包后,必须确认客户端的syn包,同时发送一个ack包,此时为syn+ack包。服务器进入SYN_RECV状态,此时服务器被动打开后,接收到客户端的syn并且发送了ack时状态;
- 第三次握手:客户端接收到服务器的syn+ack包后,给服务器发送确认ack包,包发送完毕之后。客户端和服务器端进入ESTABLISHED状态【tcp链接成功】,完成了第三次握手。
当三次握手结束后,客户端和服务器端就建立好了连接,此时tcp协议断开,开始访问服务器下的默认index.html页面,并调用该访问的资源文件,展示相应的内容。
DNS解析:
5、Maven
目前用来就是方便导入jar包的。
Maven的核心思想:约定大于配置
- 有约束,不要去违反。
Maven会规定好你该如何去编写我们的Java代码,必须按照这个规范来;
5.1、下载安装
官网:Maven下载链接
下载完成后解压即可;
配置环境变量
系统环境变量中配置:
- M2_HOME -> maven目录下的bin目录
- MAVEN_HOME -> maven的目录
在系统的path中配置MAVEN_HOME:%MAVEN_HOME%\bin
测试Maven是否安装成功,保证必须配置完毕!
5.2、阿里云镜像
- 镜像:mirrors
- 作用:加速我们的下载
- 国内建议使用阿里云的镜像
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
5.3 本地仓库
在本地的仓库,远程仓库;
建立一个本地仓库:
<localRepository>C:\Program Files\apache-maven-3.6.3\maven-repo</localRepository>
5.6、在IDEA中使用Maven
-
创建一个MavenWeb项目
-
勾选Create from archetype目的是:可以使用Maven模板
-
填写mavenGAV
- 创建好项目点击Enable Auto-import
- 出现这个代表下载成功
- 观察maven仓库多了什么东西?
- IDEA中的Maven设置
注意:IDEA项目创建完成后,看一眼Maven的配置。经常在IDEA中会出现一个问题:就是项目自动创建完成后,它这个MavenHome会使用IDEA默认,我们如果发现了这个问题,手动改为本地的。
Sources可以选择性的勾上,导入源码
- 到这里就ok了
标记文件夹
- Sources Root 源码目录
- Test Sources Root 源码测试目录
- Resources Root 资源目录
- Test Resources Root 测试资源目录
- Excluded
- Generated Sources Root
配置Tomcat
Application context:虚拟路径映射
Maven遇到的一些问题
- 资源导入错误
maven3.6.3降级为maven3.6.1 - IDEA中每次都要重复配置Maven
- 在IDEA中的全局默认中去配置configure
<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>
- 替换为webapp-4.0版本和tomcat一致
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="true">
</web-app>
6、Servlet(重点)
6.1、Servlet简洁
- Servlet是sun公司开发动态web的一门技术
- Sun在API中提供一个接口叫:Servlet,开发一个Servlet程序,只需完成两步
- 编写一个类,实现Servlet接口
- 把开发好的Java类部署到web服务器中
把实现了Servlet接口的Java程序叫做,Servlet
6.2、HelloServlet
-
构建一个Maven项目
-
将web.xml改为最新
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="true">
</web-app>
-
将Maven结构搭建完整
-
编写一个Servlet程序
- 编写一个普通类
- 实现Servlet接口,这里继承HttpServlet
public class HelloServlet extends HttpServlet {
//由于get或者post只是请求实现的不同方式,可以相互调用,业务逻辑都一样;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// ServletOutputStream outputStream = resp.getOutputStream();
PrintWriter writer = resp.getWriter();//响应流
writer.println("Hello Servlet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
- 编写Servlet的映射
为什么需要映射:我们写的是Java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要在web服务中注册我们写的Servlet,还需要给他一个浏览器访问的路径
<!--注册servlet-->
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.swae.servlet.HelloServlet</servlet-class>
</servlet>
<!--Servlet请求路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
- 配置Tomcat
6.3、Servlet原理
Servlet是由Web服务器调用,web服务器在收到浏览器请求之后,会:
6.4、Mapping问题
- 一个Servlet可以指定一个映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
- 一个Servlet可以指定多个映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello2</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello3</url-pattern>
</servlet-mapping>
- 一个Servlet可以指定通用映射路径
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
- 一个Servlet可以指定后缀或前缀映射路径
<!--可以自定义后缀实现请求映射-->
<!--注意: *前面不能加映射的路径
/*.slatt ❌
*.slatt ✔
-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>hello.slatt</url-pattern>
</servlet-mapping>
- 通配符优先级问题
指定了固有的映射路径优先级最高,找不到就会走默认的处理请求<servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping>
6.5、ServletContext
web容器在启动的时候,ServletContext(中间商),它会为每个web程序都创建一个对应的Servlet对象,它代表了当前的web应用。
1. 共享数据(尽量少用)
我在这个Servlet中保存到的数据,可以在另外一个servlet中拿到;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("It's that go shit");
// this.getInitParameter() 初始化参数
// this.getServletConfig() Servlet配置
// this.getServletContext() Servlet上下文
ServletContext context = this.getServletContext();
String name = "李";//数据
context.setAttribute("username",name);
//将一个数据保存到了ServletContext中,名字为:username,值 name
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String username = (String) context.getAttribute("username");
resp.setContentType("text/html");
resp.setCharacterEncoding("utf-8");
resp.getWriter().print("名字为:" + username);
}
配置mapping后,测试访问结果(乱码问题)
2. 获取初始化参数(几乎不用)
<!--配置一些web应用初始化参数-->
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
拿到
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
String url = context.getInitParameter("url");
resp.getWriter().print(url);
}
3. 请求转发
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
System.out.println("进入了ServletDemo04");
RequestDispatcher dispatcher = context.getRequestDispatcher("/getc");//转发的请求路径
dispatcher.forward(req, resp);//调用forward方法实现请求转发
}
200:请求响应成功
3xx:请求重定向
此处为200
4. 读取资源文件
需要用到Properties
防止资源导出错误
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
- 在java目录下新建properties
- 在resources目录下新建properties
发现:都被打包到了同一路径下:classes,我们俗称这个路径为类路径
思路:需要一个文件流;
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext context = this.getServletContext();
InputStream is = context.getResourceAsStream("/WEB-INF/classes/db.properties");
Properties prop = new Properties();
prop.load(is);
String user = prop.getProperty("username");
String pwd = prop.getProperty("password");
resp.getWriter().print(user + ":" + pwd);
}
访问测试即可
6.6、HttpServletResponse
响应
web服务器接收到客户端的http请求,
针对这个请求,
分别创建一个代表请求的 HttpServletRequest对象,代表 响应的一个HttpServletResponse;
- 如果要获取客户端请求过来的参数:找Request
- 如果要给客户端响应一些信息:找Response
1、简单分类
负责向浏览器发送数据的方法
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
负责向浏览器发送响应头的方法
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
void setBufferSize(int var1);
相应的状态码
int SC_CONTINUE = 100;
int SC_SWITCHING_PROTOCOLS = 101;
int SC_OK = 200;
int SC_CREATED = 201;
int SC_ACCEPTED = 202;
int SC_NON_AUTHORITATIVE_INFORMATION = 203;
int SC_NO_CONTENT = 204;
int SC_RESET_CONTENT = 205;
int SC_PARTIAL_CONTENT = 206;
int SC_MULTIPLE_CHOICES = 300;
int SC_MOVED_PERMANENTLY = 301;
int SC_MOVED_TEMPORARILY = 302;
int SC_FOUND = 302;
int SC_SEE_OTHER = 303;
int SC_NOT_MODIFIED = 304;
int SC_USE_PROXY = 305;
int SC_TEMPORARY_REDIRECT = 307;
int SC_BAD_REQUEST = 400;
int SC_UNAUTHORIZED = 401;
int SC_PAYMENT_REQUIRED = 402;
int SC_FORBIDDEN = 403;
int SC_NOT_FOUND = 404;
int SC_METHOD_NOT_ALLOWED = 405;
int SC_NOT_ACCEPTABLE = 406;
int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
int SC_REQUEST_TIMEOUT = 408;
int SC_CONFLICT = 409;
int SC_GONE = 410;
int SC_LENGTH_REQUIRED = 411;
int SC_PRECONDITION_FAILED = 412;
int SC_REQUEST_ENTITY_TOO_LARGE = 413;
int SC_REQUEST_URI_TOO_LONG = 414;
int SC_UNSUPPORTED_MEDIA_TYPE = 415;
int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
int SC_EXPECTATION_FAILED = 417;
int SC_INTERNAL_SERVER_ERROR = 500;
int SC_NOT_IMPLEMENTED = 501;
int SC_BAD_GATEWAY = 502;
int SC_SERVICE_UNAVAILABLE = 503;
int SC_GATEWAY_TIMEOUT = 504;
int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
2、下载文件
- 向浏览器输出消息 (get)
- 下载文件(IO)
- 获取下载文件的路径
- 获取下载的文件名
- 想办法让浏览器能够支持下载我们需要的东西
- 获取下载文件的输入流
- 创建缓冲区
- 获取OutputStream对象
- 将FileOutputStream流写入到buffer缓冲区
- 使用OutputStream将缓冲区中的数据输出到客户端!
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 下载文件(IO)
// 1. 获取下载文件的路径
String realPath = "C:\\Users\\86182\\IdeaProjects\\javaweb-02-servlet\\repsonse\\target\\classes\\ASTROWORLD.JPG";
System.out.println("下载文件的路径:" + realPath);
// 2. 获取下载的文件名
String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
// 3. 想办法让浏览器能够支持(Content-Disposition)下载我们需要的东西,中文文件需要加URLEncoder.encode
resp.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
// 4. 获取下载文件的输入流
FileInputStream in = new FileInputStream(realPath);
// 5. 创建缓冲区 三步走
int len = 0;
byte[] buffer = new byte[1024];
// 6. 获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
// 7. 将FileOutputStream流写入到buffer缓冲区
//使用OutputStream将缓冲区中的数据输出到客户端!
while ((len=in.read(buffer))>0){
out.write(buffer,0,len);//参数: 缓存,从0,到len
}
// 8.关闭流
in.close();
out.close();
}
3、验证码功能
验证怎么来的?
- 前端实现
- 后端实现,需要用到 Java 的图片类,生产一个图片
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//如何让浏览器5秒自动刷新一次
resp.setHeader("refresh", "3");
//在内存中创建一个图片
BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_3BYTE_BGR);
//得到图片
Graphics2D pen = (Graphics2D) image.getGraphics();// 一支笔 2D
//设置图片的背景颜色
pen.setColor(Color.yellow);
pen.fillRect(0, 0, 80, 20);
//给图片写数据
pen.setColor(Color.blue);
pen.setFont(new Font(null, Font.ITALIC, 20));
pen.drawString(getRandom(), 0, 20);
//** 告诉浏览器,这个请求用图片的方式打开
resp.setContentType("image/png");
//网站存在缓存,不让浏览器缓存
resp.setDateHeader("expires", -1);
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Pragma", "no-cache");
//把图片写给浏览器
ImageIO.write(image, "png", resp.getOutputStream());
}
//生成随机数
private String getRandom(){
Random random = new Random();
String num = random.nextInt(99999) + "";//5个9代表5个数
StringBuffer sb = new StringBuffer();//StringBuffer
//这里代码代表: 我们现在的num锁定5位,不足5位用0填充
for (int i = 0; i < 5-num.length(); i++) {
sb.append("0");
}
num = sb.toString() + num;
return num;
}
4、实现重定向
一个web资源(B)收到客户端(A)请求后,他会通知客户端去访问另外一个web资源(C),这个过程叫做重定向
常见场景:
- 用户登录
void sendRedirect(String var1) trows IOException;
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//重定向本质剖析:
resp.setHeader("Location", "/image");
resp.setStatus(302);
//此处考虑 与Tomcat配置的地址搭配
resp.sendRedirect("/image");//不要随便写index.jsp
}
提交重定向
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 处理请求参数
String username = (String) req.getParameter("username");
String pwd = (String) req.getParameter("pwd");
System.out.println(username + ":" + pwd);
//重定向一定要注意,路径问题,否则404
resp.sendRedirect("/success.jsp");
System.out.println("进入RequestServlet请求了");
}
<%-- 这里提交的路径,需要寻找项目的路径 --%>
<%-- 这里的路径为:<url-pattern>/login</url-pattern> --%>
<%-- pageContext.request.contextPath 代表当前项目【推荐】--%>
<form action="${pageContext.request.contextPath}/login" method="get">
<p>
用户名:<input type="text" name="username">
</p>
<p>
密码:<input type="password" name="pwd">
</p>
<input type="submit">
</form>
6.7、HttpServletRequest
HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest,通过这个方法,可以获得客户端的所有信息
1、获取前端传递的参数
2、请求转发
注意req地址
说明:
这里的 / 代表当前的web应用,转发后面不用加tomcat配置的地址重定向需要加tomcat内配置的地址
Servlet
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setCharacterEncoding("utf-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
String age = req.getParameter("age");
String[] hobbies = req.getParameterValues("hobbies");
System.out.println("=====================");
//后台接受中文乱码问题
System.out.println(username);
System.out.println(password);
System.out.println(age);
System.out.println(Arrays.toString(hobbies));
System.out.println("=====================");
//通过请求转发
/*注意此处地址
* 说明:
* 这里的 / 代表当前的web应用,转发后面不用加tomcat配置的地址
* 重定向需要加tomcat内配置的地址
* */
req.getRequestDispatcher( "/success.jsp").forward(req, resp);
req.setCharacterEncoding("utf-8");
}
Index
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<div style="text-align: center">
<%--一般表单提交用post--%>
<%--以post方式提交表单,提交到我们的login请求--%>
<form action="${pageContext.request.contextPath}/login" method="post">
用户名:
<input type="text" name="username" required><br>
密 码:
<input type="password" name="password"><br>
年龄
<input type="text" name="age"><br>
爱 好:
<input type="checkbox" name="hobbies" value="trap">Trap
<input type="checkbox" name="hobbies" value="boombap">Boombap
<input type="checkbox" name="hobbies" value="drill">Drill
<input type="checkbox" name="hobbies" value="布鲁斯">布鲁斯
<br>
<input type="submit">
</form>
</div>
</body>
</html>
面试题
请你聊聊重定向和转发的区别?
相同点
- 页面都会实现跳转
不同点
- 请求转发的时候,url不会产生变化 307
- 重定向的时候,url地址栏会发生变化 302
7、Cookie、Session
7.1、会话
会话: 用户打开一个浏览器,点了很多超链接,访问多个web资源,关闭浏览器,这个过程可以称之为会话
有状态会话: 一个同学来过教室,下次再来,我们会知道这个同学,曾经来过,称之为有状态会话
怎么证明你是xx大学的学生?
你 | 学校
- 发票 | 学校给你发票
- 学校登记 | 学校标记你来过了
一个网站,怎么证明你来过?
客户端 | 服务端
- 服务端给客户端一个信件[cookie],客户端下次访问服务端带上信件就可以了;cookie
- 服务器登记你来过了,下次你来的时候我来匹配你;seesion
7.2、保存会话的两种技术
cookie
- 客户端技术(响应,请求)
session
- 服务器技术,利用这个技术,可以保存用户的会话信息,我们可以把信息或者数据放在Session中
常见:网站登录之后,下次不用登录。第二次访问直接就上去了!
7.3、Cookie
- 从请求中拿到cookie
- 服务器响应给客户端
Cookie[] cookies = req.getCookies();// 获得Cookie
cookie.getName()// 获得cookie中的key
cookie.getValue()// 获得cookie中的value
Cookie cookie = new Cookie("lastLoginTime",System.currentTimeMillis()+"" );// 新建一个cookie
resp.addCookie(cookie);// 响应给客户端一个cookie
cookie.setMaxAge(24*60*60);// 设置cookie的有效期
cookie:一般会保存在本地的 用户目录下 appdata;
思考:一个网站cookie是否存在上限!聊聊细节问题
- 一个Cookie只能保存一个信息;
- 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie;
- Cookie大小有限制4kb;
- 300个cookie,浏览器上限;
删除Cookie
- 不设置有效期,关闭浏览器,自动失效;
- 设置有效期时间为 0
编码解码:
Cookie cookie = new Cookie("Name", URLEncoder.encode("扎个", "utf-8"));
out.write(URLDecoder.decode(cookie.getValue(),"utf-8"));
一个简单的demo
//Cookie,服务器从客户端获取
Cookie[] cookies = req.getCookies();//此处返回数组说明,Cookie可能存在多个
//判断Cookie是否存在
if (cookies!=null){//判断cookies是否为空,判断来没来过
//如果存在,怎么办?
out.write("您上一次访问的时间是:");
for (int i = 0; i < cookies.length; i++) {
Cookie cookie = cookies[i];
//获取Cookie的名字
if(cookie.getName().equals("lastLoginTime")){
//获取cookie键的值
long time = Long.parseLong(cookie.getValue());//此处需要解析
Date date = new Date(time);
out.write(date.toLocaleString());
}
}
}else {
//第一次访问一定没有值
out.write("这是我们的第一次香芋");
}
//服务器给客户端响应一个Cookie
Cookie cookie = new Cookie("lastLoginTime",System.currentTimeMillis()+"" );//Cookie只能存String,不能存Object
//给cookie的有效期为1天
cookie.setMaxAge(24*60*60);
resp.addCookie(cookie);//这就响应一个Cookie了
7.4、Session(重点)
什么是Session:
- 服务器会给每一个用户(浏览器)创建一个Session对象;
- 一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在;
- 用户登录之后,整个网站他都可以访问->保存用户的信息,保存购物车的信息(进入B站,点击每一个页面都是不一样的请求,但账号都在)
Session和cookie的区别:
- Cookie是把用户的数据写给用户的浏览器,浏览器保存(可以保存多个)
- Session是把用户的数据写到用户独占Session中,服务器端保存(保存重要的信息,减少服务器资源的浪费)
- Session对象由服务器创建
使用场景:
- 保存一个登录用户的信息;
- 购物车信息;
- 在整个网站中经常会使用的数据,我们将它保存在Session中;
1
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//解决乱码
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
//得到Session
HttpSession session = req.getSession();
//给Session中存东西
session.setAttribute("name", new Person("思唯",15));//String Object
//获取Session的ID
String sessionid = session.getId();
//判断Session是不是新创建的
if(session.isNew()){
resp.getWriter().write("session创建成功,ID:" + sessionid);
}else {
resp.getWriter().write("session已经在服务器中存在了,ID:"+sessionid);
}
//Session创建的时候做了什么事情
//创建Cookie值为sessionid,name固定jsessionid,再把cookie响应过去
// Cookie cookie = new Cookie("JSESSIONID",sessionid);
// resp.addCookie(cookie);
}
2
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
HttpSession session = req.getSession();
Person person = (Person) session.getAttribute("name");
System.out.println(person.toString());
}
3
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
session.removeAttribute("name");//取消
session.invalidate();//注销,注销后会立马创建一个新的SessionID
}
会话自动过期:web.xml中配置
<!--设置Session默认失效时间-->
<session-config>
<!--15分钟后Session自动失效,以分种为单位-->
<session-timeout>15</session-timeout>
</session-config>
回到第一个问题
一个网站,怎么证明你来过?
Cookie:
Session:
ServletContext ApplicationContext:
8、JSP
8.1、什么是JSP
Java Server Pages:Java服务器端页面,也和Servlet一样,用于动态Web技术;
最大的特点:
- 写JSP就像再写HTML
- 区别:
- HTML只给用户提供静态的数据
- JSP页面中可以嵌入JAVA代码,为用户提供动态数据;
8.2、JSP原理
思路:JSP到底怎么执行的?
- 代码层面没有任何问题
- 服务器内部怎么工作
- tomcat中有一个work目录;
- IDEA中使用Tomcat的会在IDEA的tomcat中生产一个目录
- 发现页面转变成了Java程序!
- index_jsp.class
- index_jsp.java
浏览器向服务器发送请求,不管访问什么资源,其实都在访问Servlet
JSP最终也会被转成一个Java类!
JSP本质上就是一个Servlet
//初始化
_jspInit()
//销毁
_jspDestroy()
//JSPService
_jspService()
- 判断请求
- 内置一些东西
jsp.PageContext pageContext // 页面上下文
http.HttpSession session = null; // Session
servlet.ServletContext application //applicationContext
servlet.ServletConfig config // config
jsp.JspWriter //out
java.lang.Object page = this; // page:当前
request // 请求
response // 响应
- 输出也面前增加的代码
- 以上的这些个对象我们可以在JSP页面直接使用
当我们访问hello.jsp页面时 localhost中ROOT下的hello.java和hello.class才会生成
在JSP页面中;
只要是JAVA代码就会原封不动的输出.
如果是HTML代码,就会被转为
out.write("<html>\r\n")
这样的格式,输出来
8.3、JSP基础语法
<!--JSTL表达式依赖-->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!--standard标签库-->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
任何语言都有自己的语法,JAVA有。JSP作为java技术的一种应用,他拥有自己扩充的语法,Java所有语法都支持!
JSP表达式
<%--JSP表达式
作用:用来将程序的输出,输出到客户端
<%=变量或表达式%>
--%>
<%= new java.util.Date()%>
JSP脚本片段
<%--脚本片段--%>
<%
int sum = 0;
for (int i = 1; i < 199; i++) {
sum+=i;
}
//out可以直接使用,在脚本已经定义了
out.println("<h1>Sum=" + sum + "</h1>");
%>
脚本片段的再实现
<%
int x = 10;
out.println(x);
%>
<p>这是一个jsp文本</p>
<%
//相当于都在一个Java类里,所以不能定义2个x,上面的变量,在下面输出也可以
// int x = 10;
out.println(x);
%>
<%--在代码嵌入HTML元素--%>
<%
for (int i = 0; i < 10; i++) {
%>
<h1>hello,world! <%=i
%></h1>
<%
}
%>
JSP声明
<%!
static {
System.out.println("Loading Servlet!!!");
}
private int globalVar = 0;
public void Lee(){
System.out.println("进入了Lee方法");
}
%>
JSP声明:会被编译到JSP生成Java的类中!其他的,就会被生成到_jspService方法中!
在JSP,嵌入Java代码中!
<%%>片段
<%=%>变量
<%!%>全局
<!--我是HTML的注释-->
<%--我是JSP的注释--%>
JSP代码不会显示在客户端上,HTML会!
8.4、JSP指令
- 定制错误页面
<%--定制错误页面--%>
<%@page errorPage="WEB-INF/error/500.jsp" %>
web.xml中配置
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/error/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/error/500.jsp</location>
</error-page>
- 提取公共网页
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--jsp标签
jsp:include:拼接页面,本质还是三个
一般用这个,灵活性更高
--%>
<jsp:include page="/WEB-INF/common/Header.jsp"/>
<h1>我是Body网页主页2</h1>
<jsp:include page="/WEB-INF/common/footer.jsp"/>
<%--这个@include会将两个页面合二为一--%>
<%@include file="WEB-INF/common/Header.jsp"%>
<h1>我是Body网页主页</h1>
<%@include file="WEB-INF/common/footer.jsp"%>
</body>
</html>
8.5、九大内置对象
- PageContext 存东西
- Request 存东西
- Response
- Session 存东西
- Application 存东西
- config 存东西
- out
- page 几乎不用
- exception
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--内置对象--%>
<%
pageContext.setAttribute("name1", "111");//保存的数据只在一个页面中有效
request.setAttribute("name2","222" );//保存的数据只在一次请求中有效,请求转发会携带这个数据
// request.getParameter()
session.setAttribute("name3", "333");//在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name4", "444");//在服务器中有效
%>
<%--脚本片段中的代码,会被原封不动的生成到.JSP.java
要求:这里面的代码:必须保证Java语法的正确性
--%>
<%
/*通过pageContext取出我们保存的值*/
//从底层到高层(作用域); page->request->session->application
//JVM:双亲委派机制:去包(自定义类加载器)里找,找不到去应用程序类加载器找,找不到去根加载器(rt.jar包)找
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>
</html>
request: 客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻
session: 客户端向服务器发送请求,产生的数据,用户看完一会还有用,比如:购物车;Hystrix
Application: 一个用户用完了,其他用户还可以用,比如:聊天数据
8.6、JSP标签、JSTL标签、EL表达式
ELb表达式:
- 获取数据
- 执行运算
- 获取web开发的常用对象
JSP标签
<%--转发的时候携带参数--%>
<jsp:forward page="jsp2.jsp">
<jsp:param name="name" value="SwaeLee"></jsp:param>
<jsp:param name="age" value="12"></jsp:param>
</jsp:forward>
JSTL表达式(特别的多)
JSTL标签库的使用就是为了弥补HTML标签的不足;它自定义了许多标签,可以供我们使用
核心标签(掌握部分)
<c:out>
<c:set>
<c:remove>
<c:if>
<c:forEach>
<c:url>
JSTL标签库使用步骤
- 引入对应的 taglib
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
注意,此处引入 JSTL核心标签库,可能出现问题
1. 解决方案:将Maven中jstl.jar包放和standard.jar包复制一份到apache-maven的lib下,重启Tomcat即可
2. 如果还不可以,在WEB-INF下创建lib包,把刚才的两个包在复制到lib下,重启Tomcat即可
- 使用其中的方法
c:if
<%--取name参数,${param.参数名}
取value参数,${参数名}
--%>
<h4>if测试</h4>
<hr>
<form action="coreif.jsp" method="get">
<%--
EL表达式获取表单中的数据
${param.参数名}
--%>
<input type="text" name="username" value="${param.username}">
<input type="password" name="pwd" value="${param.pwd}">
<input type="submit" value="登录">
</form>
<%--判断如果提交的用户名是管理员,则登录成功--%>
<c:if test="${param.username=='admin'}" var="isAdmin">
<c:out value="管理员上线!"/>
</c:if>
<c:out value="${isAdmin}"/>
c:when
<%--定义一个变量叫score--%>
<c:set var="score" value="95"/>
<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:when test="${score<=60}">
您的成绩拉了
</c:when>
</c:choose>
c:foreach(常用)
<%
ArrayList<String> people = new ArrayList<>();
/*
* 注意:list中index从0开始遍历
* */
people.add(0,"张三");
people.add(1,"李四");
people.add(2,"王五");
people.add(3,"赵六");
people.add(4,"田七");
request.setAttribute("list", people);
%>
<c:forEach var="people" items="${list}">
<c:out value="${people}"/> <br>
</c:forEach>
<%--
var, 每次遍历出来的遍历
items, 要遍历的对象
begin, 从哪里开始
end, 从到哪里
step, 步长
--%>
<c:forEach begin="1" end="3" step="2" var="people" items="${list}">
<c:out value="${people}"/>
</c:forEach>
下面的了解即可
格式化标签
SQL标签
XML标签
9、JavaBean
实体类
JavaBean有特定的写法:
- 必须要有一个无参构造
- 属性必须私有化
- 必须有对应的get/set方法;
一般用来和数据库的字段做映射 ORM;
ORM:对象关系映射
- 表->类
- 字段->属性
- 行记录->对象
id | name | age | address |
---|---|---|---|
1 | 思唯1号 | 82 | 内蒙 |
2 | 思唯2号 | 85 | 辽宁 |
3 | 思唯3号 | 90 | 北京 |
class People{
private int id;
private String name;
private int age;
private String address;
}
class A{
new People(1,"思唯1号",82,"内蒙")
new People(2,"思唯2号",85,"辽宁")
new People(3,"思唯3号",90,"北京")
}
Javabean
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--
People people = new People();
people.setId();
people.setAge();
people.setName();
people.setAddress();
--%>
<jsp:useBean id="people" class="com.lee.pojo.People" scope="page"/>
<jsp:setProperty name="people" property="id" value="1"/>
<jsp:setProperty name="people" property="age" value="20"/>
<jsp:setProperty name="people" property="name" value="思唯"/>
<jsp:setProperty name="people" property="address" value="内蒙"/>
<%--
等价于: <%=people.getAddress()%>
--%>
姓名:<jsp:getProperty name="people" property="name"/>
id:<jsp:getProperty name="people" property="id"/>
年龄:<jsp:getProperty name="people" property="age"/>
地址:<jsp:getProperty name="people" property="address"/>
</body>
</html>
- 过滤器
- 文件上传
- 邮件发送
- JDBC复习:如何使用JDBC,JDBC crud,jdbc 事务
10、MVC三层框架
MVC: Model View Controller 模型、视图、控制器
早期:
用户直接访问控制层,控制层就可以直接操作数据库
servlet--crud--数据库
servlet的代码中:处理请求、响应、视图跳转、处理JDBC、处理业务代码、逻辑代码
架构:没有什么是加一层解决不了的!
Model
- 业务处理:业务逻辑(Service)
- 数据持久层:CRUD(Dao)
View
- 展示数据
- 提供连接发起Servlet请求(a,form,img…)
Controller(Servlet)
- 接收用户的请求:(req:请求参数、Session信息…)
- 交给业务层处理对应的代码
- 控制视图的跳转
11、Filter
Shiro
Filter:过滤器,用来过滤网站的数据
- 处理中文乱码
- 登录验证
- 导入依赖
- 编写过滤器
- 导包不要错javax.servlet
- Filter方法
public class CharacterEncodingFilter implements Filter {
//初始化:web服务器启动,就已经初始化了,随时等待过滤对象出现!
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("CharacterEncodingFilter初始化");
}
//Chain:链
/*
1. 过滤中的所有代码,在过滤特定请求的时候都会执行
2. 必须要让过滤器继续同行
*/
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执行后...");
}
//销毁
public void destroy() {
System.out.println("CharacterEncodingFilter销毁");
}
}
- web.xml配置
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>com.lee.filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<!--只要是 /servlet的任何请求,都会经过这个过滤器-->
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
12、监听器
实现一个监听器的接口
- 编写一个监听器
- 实现监听接口
//统计网站在线人数:统计session
public class OnlineCountListener implements HttpSessionListener {
//创建Session监听:看你的一举一动
//一旦创建一个Session,就会触发一次这个事件!
public void sessionCreated(HttpSessionEvent se) {
ServletContext ctx = se.getSession().getServletContext();
Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
if (onlineCount==null){
onlineCount = new Integer(1);
}else {
int count = onlineCount.intValue();//装箱,拆箱
onlineCount = new Integer(count+1);
}
ctx.setAttribute("OnlineCount", onlineCount);//现在可以得到在线人数
}
//销毁Session监听
//一旦销毁Session,就会触发一次这个事件!
public void sessionDestroyed(HttpSessionEvent se) {
ServletContext ctx = se.getSession().getServletContext();
Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
if (onlineCount==null){
onlineCount = new Integer(0);
}else {
int count = onlineCount.intValue();
onlineCount = new Integer(count-1);
}
ctx.setAttribute("OnlineCount", onlineCount);
}
/*
Session销毁的两种情况
1. 手动销毁 se.getSession().invalidate();
2. 自动销毁
<session-config>
<session-timeout>1</session-timeout>
</session-config>
设置过期时间(单位:分钟)
*/
}
- web.xml配置监听器
<!--注册监听器-->
<listener>
<listener-class>com.lee.listener.OnlineCountListener</listener-class>
</listener>
- 看情况是否使用
<h1>当前有 <span><%=request.getServletContext().getAttribute("OnlineCount")%></span> 人在线</h1>
13、过滤器与监听器的常见应用
监听器:GUI编程经常使用
(略)
system.exit(0):正常退出,程序正常执行结束退出
system.exit(1):是非正常退出,就是说无论程序正在执行与否,都退出,
demo:用户不可通过直接访问登录页面登录
- 用户登录之后,向Session中放入用户的数据进入主页的时候要判断用户是否已经登录;要求;
- 在过滤器中实现!
public class SysFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {
//ServletRequest HttpServletRequest不一样,需要转换
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)resp;
if (request.getSession().getAttribute(Constant.USER_SESSION)==null){
response.sendRedirect("/error.jsp");
}
filterChain.doFilter(req, resp);
}
public void destroy() {
}
}
笔记参照视频:B站up主:遇见狂神说