JavaWeb动态Web资源开发
- 静态Web: 用户看到的数据始终不变;动态Web:各人看到的信息不同
- 动态Web:1.页面动态展示,淘宝"千人千面";2.和数据库交互
Web应用程序:给浏览器访问的程序 = 静态Web+动态Web
浏览器访问网络资源流程图
-
客户端通过网络协议(如Http),请求进入服务器,
-
WebSeervice收到请求,其中的**WebServerPlugin(服务插件)**判断要访问的文件是静态还是动态的.
-
静态直接找到资源文件(FIle System),动态的经过jsp,servlet(web技术)增强后(比如用jdbc连接数据库),
-
再通过WebServer(Web服务)找到对应的资源文件,然后以一种编码格式,响应回给客户端.
Http:基于Tcp的超文本传输协议
-
http使用80端口;https使用443端口,s指的是SSL/TLS协议,在HTTP和TCP/IP协议中间
-
http2.0=http/1.1版本,允许客户端与服务器连接后,获得多个资源.1.0是只能获取一个资源
请求行
- post:参数,大小没有限制,浏览器url栏不显示数据,安全,但get比它更高效
消息头(请求头):告诉服务器怎样响应你这个请求
Accept: */* 你这个请求的数据类型
Accept-Encoding: gzip, deflate, br 编码格式
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 语言环境
cache-control: max-age=0 缓存控制,最大缓存存活时间
Connection: keep-alive 连接成功后的持久策略,keep-alive始终保持连接
Host: www.dealctr.com 主机信息
响应行(响应头):告诉客户端(浏览器)应该怎样接收数据
reFresh :告诉客户端,多久刷新一次
location :网页是否重新定位
响应状态码
200:请求响应成功
300:请求重定向
404:找不到资源
500:服务器代码错误.502:网关错误
思考:浏览器从输入网址回车,到页面展示回来,经历了什么
Web服务器
服务器是一种被动的操作,处理请求与给用户回复响应
-
微软windows自带IIS服务器.
-
Tomcat是一款先进,稳定,免费的轻量级应用服务器
Tomcat
重新部署项目是重新加载当前一个项目.
-
java的根加载器在r(runtiem)t.jar包里.
-
启动startup.bat后,Tomcat的默认网址是http://localhost:8000/
-
conf下的server.xml文件是tomcat的配置文件,可以在里面修改初始参数,如端口号
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
一个webapps文件夹,就是一个Web应用
- webapps(Web项目)\ROOT下的index.jsp是默认访问的主页面:localhost:8080/index.jsp
浏览器访问网站流程
- 输入网址后,先去C:\Windows\System32\drivers\etc\hosts文件中找,有没有网址对应的域名映射(如下面的activate.navicat.com).
- 找到后返回对应的ip地址(activate.navicat.com对应的是127.0.0.1)
- 这个地址中有我们需要的web程序,找到后直接访问.
- 如果没有找到,就去**DNS(运营商根服务器)**上找,找的到就返回,找不到就提示"404(not found)找不到资源"
改变本机ip地址访问前缀:修改C:\Windows\System32\drivers\etc\hosts中的127.0.0.1(本地ip地址)的映射网址.
# 设置本机的ip地址中的,127.0.0.1和LiChangGe127.0.0.1的,映射域名为activate.navicat.com
127.0.0.1 LiChangGe127.0.0.1 activate.navicat.com
127.0.0.1 LiChangGe
# 都可以用
http://activate.navicat.com:8080/
http://lichangge:8080/
如果不可以,就再修改server.xml的host属性
<Host name="LiChangGe" appBase="webapps" unpackWARs="true" autoDeploy="true"></Host>
Web项目结构
-
一个常见的TomcatWeb项目基本结构如下:webapps(项目顶级目录)\ROOT(自己的web项目)+index.jsp\WEB-INFO(web程序配置信息目录)\web.xml(web程序配置文件)
-
Tomcat的examples是官方API包
Maven项目架构管理工具
- 核心思想:约定大于配置
idea可以不配置环境变量,自带maven
# 查看maven版本
C:\Users\林木>mvn -version
Apache Maven 3.8.3 (ff8e977a158738155dc465c6a97ffaf31982d739)
# 环境变量配置,你自己的maven安装目录
MAVEN_HOME
D:\Maven\apache-maven-3.8.3
# maven的运行程序bin目录,用idea可以不配置
M2_HOME
D:\Maven\apache-maven-3.8.3\bin
# Path引用maven,必须有
Path
%MAVEN_HOME%\bin
-
conf\settings.xml配置maven本地仓库目录和镜像
# 本地仓库,配置后去看看C:\用户\李长歌(这里是你自己的电脑账户)\.m2(默认隐藏的),这是maven默认的本地仓库,可以删掉. <localRepository>D:\Maven\apache-maven-3.8.3\mvn_properties</localRepository> # 镜像 <mirrors> <!-- 阿里镜像配置 --> <mirror> <id>aliyunmaven</id> <mirrorOf>*</mirrorOf> <name>Nexus aliyun</name> <url>https://maven.aliyun.com/repository/public</url> </mirror> </mirrors>
idea创建MavenWeb项目
一键直达
用模板创建
maven常用的全局设置
创建新模块时选择Maven构建,实现联动父项目
添加webapp目录
添加框架支持时选择web服务,来添加web目录
一个标准的web子模块
- 必须配置工件:因为访问一个网站,必须指定一个文件夹
http://localhost:8080/xiaomi
pom.xml(Maven核心配置文件)代码解读
父类和子类模块的maven代码会联动
<groupId>com.changGe.li</groupId>
<artifactId>JavaWeb</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>XiaoMi</module>
</modules>
<name>JavaWeb</name>
<packaging>pom</packaging>
# 子类
<groupId>com.changGe.li</groupId>
<artifactId>XiaoMi</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>XiaoMi Maven Webapp</name>
配置运行时的java版本信息
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
配置依赖
<!-- 配置依赖:maven会自动导入这个jar包,所需要的其他jar包 -->
<dependencies>
</dependencies>
- maven遇到配置文件,无法导出或使用时 (1条消息) Maven解决配置文件无法导出或者无法生效_ReolPurion的博客-CSDN博客
- idea的日志文件,记载了我们的错误
- bat是批处理文件,可以在里面写cmd代码
HttpServlet
需要导包
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<!--注意这里是配置作用域为测试和编译时有效
不配置的话,maven打包时会把这个包也打进项目中,
就会和tomcat本身的servlet-api包冲突,报500错误等
-->
<scope>provided</scope>
</dependency>
当存储数据时,其下所有类本质是一个Map集合
继承HttpServlet(javax.servlet.http包下),需要Maven导入依赖
只要是实现了Servlet接口,就叫做Servlet小程序
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
重写方法
package com.changGe.li.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 TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
resp.getWriter().print("Hello World");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
配置域名映射
<servlet>
<!--设置名字和要加载的Servlet-->
<servlet-name>he</servlet-name>
<servlet-class>com.changGe.li.servlet.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<!--对应he下面的Servlet的域名映射是/he,注意,一定要/(代表当前web项目,也就是localhost:8080)-->
<servlet-name>he</servlet-name>
<url-pattern>/he</url-pattern>
</servlet-mapping>
tomcat日志文件配置
Servlet原理
- 如果需要携带数据去到新页面,用request转发.
- 不携带数据,只是单纯的页面跳转或刷新,就用respons重定向(respons也可以携带参数,后面再说).
浏览器向服务器发送请求,访问的是web容器(可以理解为服务器)
-
浏览器访问的是映射,然后返回IP地址
-
客户端发送请求,到达web容器.
-
如果是第一次的话,web容器直接访问servlet,第二次和以后,就是带着请求,找servlet的对应的service方法(如doGet()和doPost()等).
-
同时web容器的respons对象,也会监听接收,来自serivice的响应.监听到数据,web容器响应给客户端
作用到代码上的执行流程
表单提交时携带请求和请求参数
- 按下submit提交表单,浏览器向action的值的地方发送请求
- 提取表单中所有有name属性的元素,把他们的值作为参数传递:如localhost:8080/Super/jsp/user.do?method=modify&uid=26
<form id="userForm" name="userForm" method="post" action="${pageContext.request.contextPath }/jsp/user.do">
<input type="hidden" name="method" value="modify">
<input type="text" name="userName" id="userName" value="${user.userName}">
<input class="button_box" type="submit" value="保存">
</form>
- 服务器找到web.xml,看有没有对应的映射
- 找到这个映射对应的servlet
<servlet>
<servlet-name>modifyPassword</servlet-name>
<servlet-class>com.changGe.li.servlets.ModifyPasswordServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>modifyPassword</servlet-name>
<!--
/ 代表当前路径,也就是localhost:8080
其后的路径语法根据表单的请求来写
也可以随便写,如/* 代表项目下所有资源都可以访问,或
-->
<url-pattern>/jsp/user.do</url-pattern>
</servlet-mapping>
<!--不能这样写,会报错java.lang.IllegalArgumentException: servlet映射中的<url pattern>[/*.do]无效
因为/*,是匹配localhost:8080/下所有servlet上,/*.do服务器就认为你要访问所有以.do为后缀的Servlet,找不到
-->
<url-pattern>/*.do</url-pattern>
- 优先访问最接近的映射
<a href="${pageContext.request.contextPath}/hello">
<!--优先访问最接近的-->
<url-pattern>/hello</url-pattern>
<!--不访问-->
<url-pattern>/hello.do</url-pattern>
-
调用对应的service(),如上面的表单提交方式是get,就找到doGet();
-
执行其中的实现代码
public class ModifyPasswordServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//重定向发送一个请求且携带参数
resp.sendRedirect(req.getContextPath()+"/jsp/user.do?method=query");
}
ServletContxet:管理所有Servlet,实现Servlet间共享数据
每个程序都有一个对应的上下文对象(ServletContext)
实现Servlet间共享数据
<servlet>
<servlet-name>he</servlet-name>
<servlet-class>com.changGe.li.servlet.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>he</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.changGe.li.servlet.TestServletOne</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello.do</url-pattern>
</servlet-mapping>
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
ServletContext servletContext = this.getServletContext();
servletContext.setAttribute("name","武则天");
}
}
public class TestServletOne extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
ServletContext servletContext = this.getServletContext();
Object name = servletContext.getAttribute("name");
System.out.println(name);
}
}
servletContext转发
// 一定要写上/代表当前web项目目录下
servletContext.getRequestDispatcher("/info.jsp").forward(req,resp);
配置初始化参数后,其下所有servlet都可以获取
<context-param>
<param-name>name</param-name>
<param-value>李长歌</param-value>
</context-param>
ServletContext servletContext = this.getServletContext();
String name = servletContext.getInitParameter("name");
System.out.println(name);//李长歌
项目构建后会被打包,在target/项目/WEB-INF/classes下
classes俗称类路径
如果java目录下的资源没有导入成功,在pom.xml中添加下列代码
然后删除target目录,重新部署或重启tomcat服务器
filtering的作用 (4条消息) maven Filtering true 作用_滕青山YYDS的博客-CSDN博客
<!--构建项目时,控制某些资源可以被导入或导出-->
<build>
<resources>
<resource>
<!-- 导入src/main/java下,所有目录下,
以.properties和以.xml为后缀名的文件 -->
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<!--允许我这个文件用一些字符,替换掉导入文件中对应的字符-->
<filtering>true</filtering>
</resource>
</resources>
</build>
读取资源文件
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
ServletContext servletContext = this.getServletContext();
InputStream resourceAsStream = servletContext.getResourceAsStream("/WEB-INF/classes/com/changGe/li/lib/test1.properties");
Properties properties = new Properties();
properties.load(resourceAsStream);
String name = properties.getProperty("name");
System.out.println(name);
}
}
Request请求转发:直接请求另一个资源的路径,直接写相对路径就可以
服务器接收到浏览器的请求后,针对这个请求自动创建Request(请求)和Respons(响应)对象
HttpServlet源码中有初始的状态码
public interface HttpServletRequest extends ServletRequest {
/**
* String identifier for Basic authentication. Value "BASIC"
*/
public static final String BASIC_AUTH = "BASIC";
/**
* String identifier for Form authentication. Value "FORM"
*/
public static final String FORM_AUTH = "FORM";
/**
* String identifier for Client Certificate authentication. Value "CLIENT_CERT"
*/
public static final String CLIENT_CERT_AUTH = "CLIENT_CERT";
请求转发时,可以携带参数
req.setAttribute("name","李长歌");
req.getRequestDispatcher("info.jsp").forward(req,resp);
获取复选框中数据
<form action="${pageContext.request.contextPath}/hello">
<input type="checkbox" name="check" value="篮球">
<input type="checkbox" name="check" value="足球">
<input type="checkbox" name="check" value="排球">
<input type="submit" value="点我一下看看"/>
</form>
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取所有的值,返回数组
String[] names = req.getParameterValues("check");
for (String name : names) {
System.out.println(name);
}
}
Respons响应重定向:从你现在的路径去到一个新的路径,url栏会发生变化,要写绝对路径
//req.getContextPath() = 当前项目目录路径:localhost:8080
resp.sendRedirect(req.getContextPath()+"/info.jsp");
重写向时不会传递参数
响应数据
//把集合响应回去,ajax用做函数的data
resp.setContentType("application/json");
resp.setCharacterEncoding("utf-8");
try {
PrintWriter writer = resp.getWriter();
//将集合变成json对象输出
writer.write(JSON.toJSONString(result));
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
下载文件:respons负责把读取到的数据,响应回给浏览器
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//以utf-8解析请求
resp.setCharacterEncoding("utf-8");
//让浏览器以utf-8模式解析响应
resp.setHeader("Content-type", "text/html;charset=UTF-8");
String imgUrl = "E:\\JAVA\\JavaWeb\\XiaoMi\\src\\main\\resources\\test.properties";
//从最后一个/开始截取,获得文件名
String fileName = imgUrl.substring(imgUrl.lastIndexOf("\\") + 1);
//输入流
FileInputStream fileInputStream = new FileInputStream(imgUrl); //设置响应头:内容类型,附件;文件名字=
resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"utf-8"));
//获取响应的文件输出流
ServletOutputStream outputStream = resp.getOutputStream();
byte[] bytes = new byte[10];
int length = 0;
while ((length = fileInputStream.read(bytes)) != -1){
outputStream.write(bytes,0,length);
}
}
resp.setCharacterEncoding("utf-8");
resp.setHeader("Content-type", "text/html;charset=UTF-8");
String imgUrl = "E:\\JAVA\\JavaWeb\\XiaoMi\\src\\main\\resources\\test.properties";
//从最后一个/开始截取,获得文件名
String fileName = imgUrl.substring(imgUrl.lastIndexOf("\\") + 1);
//输入流
FileInputStream fileInputStream = new FileInputStream(imgUrl);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
FileReader fileReader = new FileReader(imgUrl);
//设置响应头:内容类型,附件;文件名字=
resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"utf-8"));
//获取响应的文件输出流
ServletOutputStream outputStream = resp.getOutputStream();
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
// PrintWriter writer = resp.getWriter();
byte[] bytes = new byte[10];
char[] chars = new char[1024];
int length = 0;
/*
while ((length = fileInputStream.read(bytes)) != -1){
outputStream.write(bytes,0,length);
}*/
while ((length = bufferedInputStream.read(bytes)) != -1){
bufferedOutputStream.write(bytes,0,length);
bufferedOutputStream.flush();
}
bufferedInputStream.close();
bufferedOutputStream.close();
outputStream.close();
fileInputStream.close();
验证码
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//宽高,以8位RGB色彩模式打包出去
BufferedImage bufferedImage = new BufferedImage(300,300,BufferedImage.TYPE_INT_RGB);
//得到支笔
Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics();
graphics.setBackground(new Color(0xEBA9E7));
graphics.setColor(new Color(0x00F4E0));
//实心矩形,x,y,width,height
graphics.fillRect(400,400,600,600);
//字体,罗马基线,大小
graphics.setFont(new Font("方正舒体",Font.ROMAN_BASELINE,50));
//要写的字符串,x,y
graphics.drawString(verifyCode(),50,200);
//让浏览器以图片格式解析
resp.setContentType("image/png");
//设置浏览器的3秒刷新一次
resp.setHeader("refresh","3");
//多少秒后过期
resp.setDateHeader("expires",-1);
//缓存控制不缓存
resp.setHeader("Cache-Control","no-cache");
//程序不缓存,作用和上面一样,一般两者一起使用
resp.setHeader("Program","no-cache");
//把图片,按照格式,写到一个地方去
ImageIO.write(bufferedImage,"png",resp.getOutputStream());
}
public String verifyCode(){
Random random = new Random();
/**
* valueOf()的底层:return (obj == null) ? "null" : obj.toString();
* 它不会报空指针异常,但是如果是null,也会直接变成"null"
*/
String number = String.valueOf(random.nextInt(10000000));
StringBuilder stringBuilder = new StringBuilder(number);
//保证验证码是7个字符
for (int i = 0; i < 7 - number.length(); i++) {
stringBuilder.append("x");
}
System.out.println(stringBuilder);
return stringBuilder.toString();
}
请求重定向与请求转发的区别
- 请求重定向
使用responce.sendRedirect(“xx.jsp”)来进行重定向。是客户端的行为:即客户端会访问两次,第一次访问后会立即跳转到第二个重定向页面上,从本质上讲等于两次请求,而前一次的请求封装的request对象不会保存,地址栏的URL地址会改变。
2、请求转发
使用request.getRequestDispatcher(“xx.jsp”).forward(request,response)请求转发。forward(request,response)用于保存内置对象request和response。是服务器的行为:服务器会代替客户端去访问转发页面,从本质是一次请求,转发后请求对象会保存,地址栏的URL地址不会改变。
Cookie(客户端技术)
有状态会话:会话(客户端和服务器间的一次交流seesion) + 缓存(cookie)
本质是把用户的数据写给浏览器,由浏览器保存
用cookie读取上一次访问时间:刷新一次数据也刷新
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String format = new SimpleDateFormat("yyyy年MM月dd日:HH时mm分ss秒").format(new Date());
Cookie lastTime = new Cookie("lastTime", format);
boolean flag = true;
Cookie[] cookies = req.getCookies();
if(cookies != null && cookies.length > 0){
for (Cookie cookie : cookies) {
String value = cookie.getName();
if(value != null && value.equals("lastTime")){
flag = false;
//特定格式解析字符串,常用于中文解码
req.setAttribute("lastTime", URLDecoder.decode(cookie.getValue(),"utf-8"));
//更新登录时间
cookie.setValue(format);
/**
* 缓存时间0秒,就是删除cookie,
* 只有用户关闭浏览器才会删除cookie
*/
cookie.setMaxAge(60 * 60);//单位是秒
resp.addCookie(lastTime);
req.getRequestDispatcher("index.jsp").forward(req,resp);
return;
}
}//for
}
if(flag){
resp.addCookie(lastTime);
req.setAttribute("msg", "这是您第一次登录");
req.getRequestDispatcher("info.jsp").forward(req,resp);
}
}
精简版
String format = new SimpleDateFormat("yyyy年MM月dd日:HH时mm分ss秒").format(new Date());
Cookie[] cookies = req.getCookies();
boolean flag = true;
if(cookies != null && cookies.length > 0){
for (Cookie cookie : cookies) {
String value = cookie.getName();
if(value != null && value.equals("lastTime")){
flag = false;
req.setAttribute("msg", "您上一次登录时间:"+URLDecoder.decode(cookie.getValue(),"utf-8"));
}
}//for
}
if(flag){
req.setAttribute("msg", "这是您第一次登录");
}
Cookie lastTime = new Cookie("lastTime", format);
resp.addCookie(lastTime);
req.getRequestDispatcher("info.jsp").forward(req,resp);
浏览器的applition里可以看到cookie缓存.,可以删除掉特定的cookie
只有清除了localhost:8080的缓存后,cookie才真正消失,每次浏览器都默认存储了一些cookie
用everything我们可以找到**cookiie的存放位置,**只是我们不能用(没有权限,到AC就看不到了)
浏览器最多发送300个coolie,约是15个网站的,最大共1200kb数据.
Seesion(服务器技术)
服务器会为每个浏览器都创建一个seesion,在服务器上保存用户的数据,全局作用域,直到浏览器关闭.
seesion在浏览器默认存储30分钟
<session-config>
<!--15分钟自动失效-->
<session-timeout>15</session-timeout>
</session-config>
-
seesion存储在cookie中,去浏览器的applition中找到cookie看,或者请求头的cookie里保存的有
-
底层代码相当于response.addcookie(new Cookie(“JSSSIONID”,value));
//约等于此,不过是服务器内部实现的,我们这样的不行 resp.addCookie(new Cookie("JSSSIONID", "123"));
-
seesion可以存储对象,能用seesion就不要用context
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession();
session.setAttribute("name","李长歌");
session.setAttribute("age","18");
session.setAttribute("sex","女");
//如果是新创建的
if(session.isNew()){
Enumeration<String> attributeNames = session.getAttributeNames();
//sessionId是D6E8AD19A02583A4CB3DA5F36F86EB28
System.out.println("sessionId是"+session.getId());
/**
* 结果:
* 女
* 李长歌
* 18
*
* 页面自动回到浏览器主页了,因为session注销了
*/
while (attributeNames.hasMoreElements()){
String name = attributeNames.nextElement();
System.out.println(session.getAttribute(name));
//可以用于用户退出时删除用户信息
session.removeAttribute(name);
}
//注销
session.invalidate();
}
}
和全文索引的应用场景一样
JSP(Java服务页面JavaServerPages)
本质就是Servlet(小应用服务程序)
- PHP语言不能承载大访问量,微软ASP是HTML+VB脚本,用C#语言开发
- jsp基于Java的B(browser)S架构,sun公司开发,可以承载三高:高并发,高性能和高可用(数据要高可靠,服务要高可用)
index.jsp源码分析
服务器访问任何资源,本质是访问Servlet. 因为idea的tomcat>work目录下的jsp页面最后变成了.java文件.
JSP源码分析__板蓝根_的博客-CSDN博客_jsp源码
这个博客没有找到,就在这里面找
这是我最终找到的路径:C:\Users\林木\AppData\Local\JetBrains\IntelliJIdea2021.2\tomcat\61c407dd-c490-4564-89ac-71da59dbac6b\work\Catalina\localhost\XiaoMi\org\apache\jsp
-
继承自HttpJspBase类,HttpJspBase继承自HttpServlet类
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
-
初始化,销毁和服务三大方法
public void _jspInit() { } public void _jspDestroy() { } public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
-
判断请求类型.method()
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) { response.setHeader("Allow","GET, HEAD, POST, OPTIONS"); response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS"); return; }
-
初始化对象有context(改名application),page = this…
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里面,在输出页面前为一些默认的对象,如pageContext等赋值,我们可以在jsp页面中直接使用
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("\n"); out.write("<html>\n"); out.write("\n"); out.write("<body>\n");
<%--这是jsp表达式,加"="号专门用来输出--%>
<input type="text" value="名字是:<% String name="李长歌"; %>
<%= name%>"/>
<!--对应到jsp.java底层->
out.write(" <input type=\"text\" value=\"名字是:");
String name="李长歌";
out.print( name);
jsp语法
javax-servlet-jsp-api依赖
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
html的注释可以在控制台看到,jsp不显示
<%--jsp脚本在_jspService()中--%>
<!--${i}EL表达式-->
<% for(int i = 0 ; i < 5; i++){%>
<h1>
hello world<%= i %>
</h1>
<% } %>
<%--jsp声明在jsp类中--%>
<%!
public void test(){}
%>
自定义错误页面
<%@ page pageEncoding="utf-8" %>
<%--jsp标签:写在页面最上面,作为配置.显式声明这是一个错误页面--%>
<%@ page isErrorPage="true" %>
<%@page errorPage="info.jsp" %>
<html>
<body>
<% int i = 1 / 0;%>
</body>
</html>
或
<error-page>
<!--500错误时跳转页面-->
<error-code>500</error-code>
<location>/info.jsp</location>
</error-page>
添加公共页面
<%--是将页面中的代码提出来,然后和现有页面代码拼接在一起--%>
<%@ include file="info.jsp" %>
out.write("<head>\r\n");
out.write(" <title>Info</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write("\r\n");
out.write("<h3>info下的h3</h3>\r\n");
out.write(" <img src=\"WEB-INF/images/error.jpg\" width=\"1240\" height=\"800\">\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>\r\n");
out.write("\n");
out.write("<html>\n");
out.write("\n");
out.write("<body>\n");
<%--第二种方法--%>
<%--这是调用其它页面,不会出现代码冲突的问题--%>
<jsp:include page="info.jsp"/>
out.write(" <h2>22222</h2>\n");
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "info.jsp", out, false);
out.write("\n");
jsp标签
<%--作用域是request--%>
<jsp:forward page="info.jsp">
<jsp:param name="name" value="李长歌"/>
</jsp:forward>
<h3>name is:${pageContext.request.getParameter("name")}</h3>
static包下一般放前端的资源,如css,img,js和plugins等.
WEB-INF目录对用户不可见
EL表达式
作用域优先级:pageScope > requestScope > sessionScope > pageContext(页面运行时)>applicationScope(服务器)
如果没有给定取值作用域,则默认从pageScope开始查找,找到则返回,没找到则按照上述顺序继续查找,以此类推,知道找到为止。
如果最终没有找到,则返回null。
九大对象及作用域
- page对象的数据出了页面就消失了;
- seesion间可以互相访问,但是当要统计服务器中所有数据时,就要去到更高级的application对象了:
- page -> request -> seesion -> pageContext>application
- 类似于双亲委派机制,先从应用类加载器找,找不到用扩展类加载器(ext中的jar包),还找不到就最后去根加载器(rt目录下的jar包).
<body>
<%
pageContext.setAttribute("name","李长歌");
session.setAttribute("name1","session");
request.setAttribute("name2","request");
application.setAttribute("name3","application");
//获取值
String name5 = (String) pageContext.findAttribute("name5");
%>
<%--找不到也不报错,el表达式自动过滤null值,不显示在页面中--%>
<!--name:李长歌 name1:session name2:request name3:application name5:-->
name:${name}
name1:${name1}
name2:${name2}
name3:${name3}
name5:${name5}<!--不显示-->
</body>
设置作用域
<%
//等同于session.setAttribute("key","value");
pageContext.setAttribute("key","value",pageContext.SESSION_SCOPE);
%>
作用域保存数据的重要性从低到高:request(客户端看完即删,新闻图片等.) < seestion(需要二次使用,但不事关关键业务,如购物车) < application(需要全局作用,事关关键业务,如聊天数据)
<%
pagecontext.setAttribute("name","李长歌");
request = pagecontex.getRequest();
requext.forward("forward.jsp");
%>
<%--页面转发时,会自动将数据也给转发走--%>
<%--info页面下--%>
<%
String name = (String)pagecontext.getAttribute("name");
out.print(name);
%>
jstl标签
jstl-api依赖和standrard标签库依赖
如果报错说找不到这个包,可能是因为tomcat中没有这个jar包,直接粘到tomcat的work目录中去
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--写在最顶上,导入core标签库,字首(也就是前缀)为c,字首可以随便写--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%--如果报错:根据标记文件中的TLD或attribute指令,attribute[items]不接受任何表达式
是因为和tomcat中的jstl-api.jar包不兼容
就换成下面这个--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core_rt" %>
switch循环和if判断
<body>
<%--定义变量sorce,赋值为100--%>
<c:set var="score" value="100"/>
<%-- 把test判断的值,赋值给number --%>
<c:if test="${score > 0 and score <= 100}" var="number">
<c:out value="${number}"/>
<%--循环判断--%>
<c:choose>
<%--会自动break--%>
<c:when test="${score >=90 or score <= 100}">
<c:out value="优秀的成绩"/>
</c:when>
</c:choose>
</c:if>
</body>
增加for
<%
ArrayList<Object> arrayList = new ArrayList<>();
arrayList.add(0,0);
arrayList.add(1,1);
arrayList.add(2,2);
arrayList.add(3,3);
request.setAttribute("list",arrayList);
%>
<%--从1遍历到3,一次i+2,最后只输出1,3--%>
<c:forEach var="item" items="${list}" begin="1" end="3" step="2">
<%--从list集合中取值,值为item,打印item到页面上--%>
<c:out value="${item}"/>
</c:forEach>
JavaBean(咖啡豆)
jsp:useBean的用法_远方©的博客-CSDN博客_jsp:usebean
- 有无参构造+属性私有化,有对应的set,get方法
<form action="index.jsp" method="get">
<input type="text" name="name" value="李长歌">
<input type="text" name="age" value="18">
<input type="submit" value="请提交">
</form>
<%--实例化对象User--%>
<jsp:useBean id="user" class="com.changGe.li.User" scope="page"/>
<%--获取表单中的所有值,并一一赋值给User类,对应的set方法--%>
<jsp:setProperty name="user" property="*"/>
<%--从user对象中取值--%>
<jsp:getProperty name="user" property="name"/><%--李长歌--%>
<jsp:getProperty name="user" property="age"/><%--18--%>
<%=user.getAge()%><%--18--%>
<form action="index.jsp" method="get">
<input type="text" name="name" value="李长歌">
<input type="text" name="age" value="18">
<input type="submit" value="请提交">
</form>
<%--实例化对象User--%>
<jsp:useBean id="user" class="com.changGe.li.User" scope="page"/>
<%--只给setName()赋值--%>
<jsp:setProperty name="user" property="name"/>
<jsp:getProperty name="user" property="name"/><%--李长歌--%>
<%--尽管age的input输入了值,还是为0--%>
<jsp:getProperty name="user" property="age"/><%--0--%>
<%=user.getAge()%><%--0--%>
<form action="index.jsp" method="get">
<input type="text" name="name" value="李长歌">
<input type="text" name="age" value="18">
<input type="submit" value="请提交">
</form>
<%--实例化对象User--%>
<jsp:useBean id="user" class="com.changGe.li.User" scope="page"/>
<%--给setName()赋值"武则天"--%>
<jsp:setProperty name="user" property="name" value="武则天"/>
<%--最后得到的还是"武则天"--%>
<jsp:getProperty name="user" property="name"/>
<form action="index.jsp?name=李世民" method="get">
<input type="text" name="name" value="李长歌">
<input type="text" name="age" value="18">
<input type="submit" value="请提交">
</form>
<%--实例化对象User--%>
<jsp:useBean id="user" class="com.changGe.li.User" scope="page"/>
<%--从请求的参数取值name,赋值给user对象的setName--%>
<jsp:setProperty name="user" property="name" param="name"/>
<%--最后得到的还是"李长歌"--%>
<jsp:getProperty name="user" property="name"/>
<body>
<%--实例化对象User--%>
<jsp:useBean id="user" class="com.changGe.li.User" scope="page"/>
<%--赋值给user对象的setName--%>
<jsp:setProperty name="user" property="name" value="武则天"/>
<%--"武则天"--%>
<jsp:getProperty name="user" property="name"/>
</body>
MVC(Model view controller模型视图控制器)三层架构
三层架构就是在Model层进行dao(数据持久),service(业务处理)的中间过渡
Filter过滤器:在服务器与资源文件之间过滤数据
mysql-connector-java依赖,一定要用和自己数据库版本一样的jar包(依赖),不同版本的结构不同
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
实现Filter下的方法init初始化,doFIlter业务,destroy销毁,可以只实现doFilter()
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
}
@Override
public void destroy() {
Filter.super.destroy();
}
用filter实现登录验证
- 用户提交表单后,找到对应的servlet
<h1 class="title">欢迎登录</h1>
<form action="${pageContext.request.contextPath}/login" method="get">
<input class="input_box" type="text" name="userCode" value="hanlubiao" placeholder="用户名">
<input class="input_box" type="password" name="password" value="0000000" placeholder="密码">
<input class="button_box" type="submit" value="登录">
<span>${error}${param.get("error")}</span>
</form>
<servlet>
<servlet-name>login</servlet-name>
<servlet-class>com.changGe.li.servlets.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
- 这里面取值用户的信息,判断是否成功.
成功了就跳转主页,失败是失败页面.
public class LoginServlet extends HttpServlet {
//数据库工具类
private UserService userService = new UserServiceImpl();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String userCode = req.getParameter("userCode");
String password = req.getParameter("password");
//根据用户名和密码找到User对象
User user = userService.getUser(userCode,password);
//不为空就保存到session中
if(user != null){
req.getSession().setAttribute("user",user);
resp.sendRedirect(req.getContextPath()+"/jsp/frame.jsp");
}else {
//失败就重定向
req.setAttribute("error","用户名或密码错误");
req.getRequestDispatcher("login.jsp").forward(req,resp);
}
}
}
- 注销就是让用户对应的seesion删除掉:req.getSession().removeAttribute(“user”);
注销后回到登录页面.
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
User user = (User)req.getSession().getAttribute("user");
if(user != null){
req.getSession().removeAttribute("user");
resp.sendRedirect(req.getContextPath()+"/login.jsp");
}else {
//为空,代表已经被删除了
req.getRequestDispatcher("error.jsp").forward(req,resp);
}
}
- 有个过滤器每次有新请求时,都判断用户的seesion是否为空
<!-- 初始过滤:强制登录,管理系统不需要让游客看到首页 -->
<filter>
<filter-name>forcoLogin</filter-name>
<filter-class>com.changGe.li.filters.ForcoLoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>forcoLogin</filter-name>
<url-pattern>/jsp/*</url-pattern>
</filter-mapping>
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resq = (HttpServletResponse) response;
//从session中获取user对象
User user = (User)req.getSession().getAttribute("user");
if(user == null){
request.setAttribute("error","请先登录,谢谢");
resq.sendRedirect(req.getContextPath()+"/login.jsp?error="+ URLEncoder.encode("请先登录,谢谢","utf-8"));
}else{
//找到之后直接进入主页.不用登录
//必须让请求和响应 链接 起来后,程序才会继续进行,不然就卡住
chain.doFilter(req, resq);
}
}
- user的session经常用到,可以提取成静态常量
public static final String USER_SEESION = "user";
用这个思想,实现一个判断用户vip等级的demo.
主要是过滤器根据username获取到的seesion,进而获取对象,然后判断等级
<form action="hello" method="get">
等级:<input type="text" name="grade" value="2">
<input type="submit" value="提交">
</form>
info.jsp
<body>
<h2>您的等级是:${grade}</h2>
</body>
web.xml
<filter>
<filter-name>verify</filter-name>
<filter-class>com.changGe.li.FilterTest</filter-class>
</filter>
<filter-mapping>
<filter-name>verify</filter-name>
<!--监听名为he的servlet的动作-->
<servlet-name>he</servlet-name>
<!--两者可以同时存在,不存在优先级这一说-->
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>he</servlet-name>
<servlet-class>com.changGe.li.servlet.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>he</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
TestServlet
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String grade = req.getParameter("grade");
req.getSession().setAttribute("grade",grade);
req.getRequestDispatcher("info.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
FilterTest
public class FilterTest implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resq = (HttpServletResponse) response;
String grade = req.getParameter("grade");
if(grade == null || grade.equals("")){
//从session中获取user对象
grade = String.valueOf(req.getSession().getAttribute("grade"));
}
if(grade != null && !grade.equals("")){
System.out.println("if中的"+grade);
switch (grade){
case "1":
req.setAttribute("grade","1");
break;
case "2":
req.setAttribute("grade","2");
break;
default:
req.setAttribute("grade","3");
}
chain.doFilter(req, resq);
}else{
resq.getWriter().write("等级为空");
}
}
}
Listener监听器
各种类型的监听器都可以实现,如HttpSestionListener,始终监听session域
index.jsp
<form action="hello" method="get">
<input type="submit" value="提交">
</form>
<!--web.xml配置监听器-->
<listener>
<listener-class>com.changGe.li.ListenerTest</listener-class>
</listener>
监听到有session被创建时,就创建一个sess的session,赋值
有session被销毁时,得到资源
public class ListenerTest implements HttpSessionListener {
//有session被创建时
@Override
public void sessionCreated(HttpSessionEvent se) {
//创建session
se.getSession().setAttribute("sess", URLEncoder.encode("监听到session被创建了"));
}
//有session被销毁时
@Override
public void sessionDestroyed(HttpSessionEvent se) {
Object source = se.getSource();
//输出结果org.apache.catalina.session.StandardSessionFacade@8539a6f
System.out.println("监听到的资源是:"+source.toString());
}
}
servlet中先创建一个session,触发sessionCreated()
然后得到sessionCreated()创建的session打印出去
最后删除自己创建的session
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建session
req.getSession().setAttribute("name","李长歌");
//写去session
resp.getWriter().write(String.valueOf(req.getSession().getAttribute("sess")));
//删除session
req.getSession().invalidate();
}
实现统计在线人数
只要有个session被创建后,在线人数就+1;销毁时在线人数-1
servlet
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//一秒刷新一次
resp.setHeader("refresh","1");
//一定要从ServletContext中取值,ServletContext监控所有session
String online = String.valueOf(req.getServletContext().getAttribute("online"));
req.setAttribute("online",online);
req.getRequestDispatcher("info.jsp").forward(req,resp);
}
public class ListenerTest implements HttpSessionListener {
//有session被创建时
@Override
public void sessionCreated(HttpSessionEvent se) {
//在最高的作用域servletContext操作session
ServletContext servletContext = se.getSession().getServletContext();
Integer online = (Integer) servletContext.getAttribute("online");
if(online == null || online <= 0){
online = 1;
}else {
online ++;
}
servletContext.setAttribute("online",online);
}
//有session被销毁时
@Override
public void sessionDestroyed(HttpSessionEvent se) {
ServletContext servletContext = se.getSession().getServletContext();
Integer online = (Integer)servletContext.getAttribute("online");
if(online == null || online < 0){
online = 0;
}else {
online --;
}
servletContext.setAttribute("online",online);
}
}
info.jsp
<body>
<h2>在线人数是:${online}</h2>
</body>
web.xml
<session-config>
<!-- 超时(过期)时间为1分钟 写成0或负数就代表永远不超时-->
<session-timeout>1</session-timeout>
</session-config>
实现GUI窗口关闭,实现它的子类,用的是适配器模式
public static void main(String[] args) {
JFrame jFrame = new JFrame();
//适配器模式,windowAdapter实现了WindowListener
jFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
super.windowClosed(e);
}
});
}
WindowAdapter实现了WindowListener
public abstract class WindowAdapter
implements WindowListener, WindowStateListener, WindowFocusListener
{
juit单元测试依赖
<!--可以直接用@test写在方法上,不用main方法,就能直接运行了-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
q.setAttribute(“online”,online);
req.getRequestDispatcher(“info.jsp”).forward(req,resp);
}
```java
public class ListenerTest implements HttpSessionListener {
//有session被创建时
@Override
public void sessionCreated(HttpSessionEvent se) {
//在最高的作用域servletContext操作session
ServletContext servletContext = se.getSession().getServletContext();
Integer online = (Integer) servletContext.getAttribute("online");
if(online == null || online <= 0){
online = 1;
}else {
online ++;
}
servletContext.setAttribute("online",online);
}
//有session被销毁时
@Override
public void sessionDestroyed(HttpSessionEvent se) {
ServletContext servletContext = se.getSession().getServletContext();
Integer online = (Integer)servletContext.getAttribute("online");
if(online == null || online < 0){
online = 0;
}else {
online --;
}
servletContext.setAttribute("online",online);
}
}
info.jsp
<body>
<h2>在线人数是:${online}</h2>
</body>
web.xml
<session-config>
<!-- 超时(过期)时间为1分钟 写成0或负数就代表永远不超时-->
<session-timeout>1</session-timeout>
</session-config>
实现GUI窗口关闭,实现它的子类,用的是适配器模式
public static void main(String[] args) {
JFrame jFrame = new JFrame();
//适配器模式,windowAdapter实现了WindowListener
jFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
super.windowClosed(e);
}
});
}
WindowAdapter实现了WindowListener
public abstract class WindowAdapter
implements WindowListener, WindowStateListener, WindowFocusListener
{
juit单元测试依赖
<!--可以直接用@test写在方法上,不用main方法,就能直接运行了-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
ORM对象关系映射 :就是实体类和数据库字段互相对应