JavaWeb笔记
1、基本概念
web开发:
- 静态web:
- html,css;
- 始终不会变化;
- 动态web:
- 现在几乎所有网站都是“千人千面”;(例如淘宝)
- 技术栈:Servlet/JSP,ASP,PHP;
在java中,动态web资源开发的级数统称为JavaWeb;
1.2、web应用程序
web应用程序:可以提供浏览器访问的程序;
- a.html、b.html……多个web资源,这些web可以被外界访问,对外界提供服务;
- 我们能够访问到任何一个资源;(URL)
- web资源会被统一放在一个文件夹下,web应用程序--> Tomcat:服务器;
- 一个web应用由多个部分组成(静态web,动态web)
- html,css,js;
- jsp,servlet;
- java程序;
- jar包;
- 配置文件;(properties)
web应用程序编写完毕后,若想给外界访问,则需要一个服务器来统一管理!
1.3、静态web
- *.htm 、 *.html、如果服务器上一直存在这些东西,我们就可以直接进行读取;
- 缺点:
- web无法动态更新;
- 轮播图,点击特效:微动态
- JavaScript【实际开发中,用的最多】
- VBScript
- 无法与数据库进行交互(数据无法持久化,用户无法交互)
- web无法动态更新;
1.4、动态web
页面会动态展示:“web展示页面因人而异”
缺点:
- 加入服务器的动态web资源出现了错误,需要重新编写后台程序,重新发布;
- 停机维护;
有点:
- 可以动态更新web;
- 可以与数据库交互(数据持久化,用户交互)
2、web服务器
2.1、介绍
ASP:
- 微软:国内最早流行的;
- 在html中嵌入了VB脚本,ASP+COM;
- 在ASP开发中,一个基本页面中,要嵌入大量的java代码业务,代码更乱;
- 使用C#语言;
- IIS
PHP:
- PHP开发速度很快,功能很强大,跨平台,代码简单(70%的网页都使用);
- 无法承载大访问量,具有局限性,支持不了大型网站;
JSP/Servlet:
B/S架构:浏览器和服务器;
C/S架构:客户端和服务器;
- sun公司主推的B/S架构;
- 基于java语言的(所有的大公司,或者一些开源的组件,都是用java写的);
- 可以承载三高问题带来的影响;(高可用,高并发,高性能)
- 语法像ASP,加强时长竞争力;
2.2、web服务器
服务器是一种被动操作,用来处理用户的请求,和响应信息;
- IIS
- 微软,ASP……Windows中自带;
- Tomcat:
- Apache,性能稳定,技术先进,免费,深受喜爱。
- 免费开源的web应用服务器,属于轻量级应用服务器,中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP的首选。
- Tomcat处理静态HTML的能力不如Apache服务器。
- Tomcat实际上运行JSP页面和Servlet,目前Tomcat最新版本为10.0;
下载Tomcat:
- 安装./解压;
- 了解配置文件和目录结构;
- 作用;
3、Tomcat
3.1、安装Tomcat
3.2、启动/关闭Tomcat
bin目录下的startup.bat
/ shutdown.bat
;
测试:访问 localhost:8080
可能遇到的问题:
- java环境变量没有配置;
- 闪退问题,需要配置兼容性;
- 乱码问题;去配置文件中设置(conf/server.xml);
3.3、配置
- 查看配置文件;
- 默认网站应用存放位置:webapps
3.4、发布一个自己的网站
模仿:
- 将自己写的网站,放到服务器(Tomcat)中指定的web应用的文件夹(webapps)下,就可以访问了;
网站结构:
--webapps:Tomcat服务器的web目录
-ROOT
-myweb:网站的目录名
- WEB-INF
-classes:java程序
-lib:web应用所依赖的jar包
-web.xml:网站配置文件
- index.html 默认的首页
- static
-css
-style.css
-js
-img
- ……
5、Maven
为什么需要这个技术?
- 在JavaWeb开发中,需要使用大量的jar包,需要手动导入;
- 需要一个东西自动帮我们导入和配置jar包;
因此,maven诞生!
Maven项目架构管理工具:就是用来方便导入jar包的!
Maven的核心思想:约定大于配置
Maven会规定好该如何编写java代码,必须按照规范来;
5.1、下载安装Maven
下载、解压;(所有环境放在同一个文件夹中);
注意:(我下载这个版本遇到了bug,建议3.6.2以下)
5.2、配置环境变量
- 在系统环境变量中添加配置:
- MAVEN_HOME:Maven文件目录
- M2_HOME(后期会用到):maven文件下的bin目录
-
添加到path路径中:
-
检查配置是否成功:
5.3、修改阿里云镜像
加速下载;
-
打开maven的配置文件:conf\settings.xml
-
添加阿里云镜像:
<mirror> <id>nexus-aliyun</id> <mirrorOf>*,!jeecg,!jeecg-snapshots</mirrorOf> <name>Nexus aliyun</name> <url>https://maven.aliyun.com/nexus/content/groups/public</url> </mirror>
5.4、建立本地仓库
本地仓库:
默认位置:
建立过程:
-
在maven目录下新建本地仓库文件夹:
maven-repo
: -
修改配置文件本地仓库路径:
5.5、在IDEA中使用Maven
-
打开IDEA创建新的MavenWeb项目:
-
等待项目初始化:
-
观察Maven仓库中的文件:
-
IDEA中的Maven设置:
注意(问题):在IDEA中,项目自动创建完成后,MavenHome经常会使用IDEA默认的配置!!需要手动修改!
所以,项目创建成功后,注意瞟一眼配置;
- 到这里IDEA的Maven创建和配置就OK了!
5.6、创建一个普通的MavenWeb项目
这次不使用Maven模板,直接创建一个Maven项目
项目结构:
(对比Web应用的项目结构:)
(纯净maven)步骤:
-
修改项目结构;创建两个文件夹:java、resources;并标记为对应文件夹;
-
得到maven初始的pom.xml文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>javaweb-session-cookie</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> </dependencies> </project>
-
防止maven默认web项目中的web.xml版本问题:
修改
web.xml
:(复制于Tomcat目录里的原始web.xml,因为这样子对Tomcat的兼容性最强(原生))<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version="5.0" metadata-complete="true"> </web-app>
-
写java代码;
-
web.xml中注册,添加映射:
<servlet> <servlet-name>CookieDemo01</servlet-name> <servlet-class>com.study.CookieDemo01</servlet-class> </servlet> <servlet-mapping> <servlet-name>CookieDemo01</servlet-name> <url-pattern>/c1</url-pattern> </servlet-mapping>
-
去Tomcat中发布项目;
-
启动Tomcat进行访问;
发布Servlet时遇到的bug:
- 服务器500报错;
解决:
Tomcat10版本环境下,将 javax.servlet 都改为了jakarta.servlet:
<dependency> <groupId>jakarta.servlet.jsp</groupId> <artifactId>jakarta.servlet.jsp-api</artifactId> <version>3.0.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>5.0.0</version> <scope>provided</scope> </dependency>
Mark文件来:
或者:设置中的项目结构配置:
5.7、在IDEA中配置Tomcat
- 添加配置:
- 创建一个artifacts,解除警告:
此处我遇到一个deployment里没有artifacts选项的问题:
(idea:2021.3.2;Tomcat:10.0.8)
解决办法见:https://blog.csdn.net/qq_31887267/article/details/90775368
配置的项目不写路径,默认端口号8080访问服务器;
为什么会有这个警告?------->因为访问布一个网站,需要指定一个文件名字;
-
启动Tomcat:
启动成功!
5.8、 Pom文件
这里我遇到一个问题:idea中maven project只看到lifecycle而没plugins和dependencies:
原因:idea和3.6.2(及以上)版本的maven不兼容(此处我用的是3.8版本);
解决办法,下载3.6.2以下版本并重新配置环境变量和项目即可;
pom.xml是Maven的核心配置文件;
maven的高级之处就在于他能够自动帮忙导入jar包及其所需的其他包!
Maven由于约定大于配置,可能遇到的问题:
我们之后写的配置文件可能无法导出!
解决资源到处问题:
在biuld中配置resources,防止资源导出失败;
<!--在build中配置resources,来防止我们资源导出失败的问题--> <build> <resources> <resource> <directory>src/main/resources</directory> <excludes> <exclude>**/*.properties</exclude> <exclude>**/*.xml</exclude> </excludes> <filtering>false</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build>
5.11、IDEA生成目录树
Maven中jar包的联系关联图:
Maven仓库的使用
添加Servlet的maven依赖:
-
首次使用去官网下载jar包:https://mvnrepository.com/:
既然Tomcat的examples里面有Servlet的例子,能启动就一定有对应的jar包,进入文件目录找到:
于是就去官网中搜索:
下载使用者最多的一个;
-
复制仓库依赖进pom.xml配置文件:
-
继续导入其他需要的包:
6、Servlet
Setvlet接口规定了两个必须继承的实现类:HttpServlet
、GevericServlet
6.1、Servlet简介
- Servlet就是sun公司开发动态web的技术;
- Sun在这些API中提供了一个借口叫做:Servlet,如果想开发一个Servlet程序,就只需要:
- 编写一个类,实现Servlet接口;
- 把开发好的java类部署到web服务器中;
实现了Servlet接口的java程序就叫做Servlet;
6.2、HelloServlet
- 构建一个普通的Maven项目,删掉里面的src项目,以后学习就在这个文件下创建module,这个空的工程就是Maven主工程;
- 关于Maven父子工程的理解;父项目中的java子项目可以直接使用;
- Maven环境优化:
- 修改web.xml为最新的;
- 讲maven的结构搭建完整;
- 编写一个Servlet程序;
- 编写一个普通类;
- 实现一个Servlet接口;继承HttpServlet接口;
- 编写Servlet的映射
理由 :因为我们写的是java程序,但是要通过浏览器访问,而浏览器需要链接web服务器,所以我们要在web服务中注册我们写的Servlet,还需要给他一个浏览器能够访问的路径; - 配置Tomcat;(注意配置项目发布的路径!)
- 启动测试;(启动后会生成target目录)
6.3、Servlet运行原理
6.4、Mapping问题
请求路径的映射问题
- 一个Servlet可以指定一个映射路径:
- 一个Servlet可以指定多个映射路径
- 一个Servlet可以指定通用映射路径
- 一个Servlet可以指定一些后缀或前缀等等……
- 指定了固有路径的映射优先级最高,它的优先级大于通配符的映射;
可以用映射来解决一些error(404……)
例如:*.error
6.5、ServletContext
web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前web应用;
6.5.1、共享数据
-
在这个Servlet中保存的数据,可以在另外一个Servlet中拿到(传递);
-
放置数据类:
public class HelloServlet extends HttpServlet{ @Override protected void doGet(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{ //this.getInitialParermeter();//初始化参数 //this.getServletConfig(); //获得Servlet配置 //this.getServletContext; //获得Servlet上下文 ServletContext context=this.getServletContext; String username = "秦疆"; context.getAttribute("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.setContextType("text/html"); 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-name>hello</servlet-name> <servlet-class>com.kuang.servlet.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> <servlet> <servlet-name>getc</servlet-name> <servlet-class>com.kuang.servlet.GetServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>getc</servlet-name> <url-pattern>/getc</url-pattern> </servlet-mapping>
-
测试访问结果:
6.5.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.getInitParammeter("url");
resp.getWriter().print(url);
}
6.5.3、请求转发
@Override
protected void doGet(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{
ServletContext context=this.getServletContext;
RequestDispatcher requestDispatcher=context.getRequestDispatcher("/gp");//转发的请求路径
requestDispatcher.forward(req,resp);//执行转发请求
}
6.5.4、读取资源文件
Properties:
- 在java目录下新建properties;
- 在resources目录下进件properties;
执行程序发现,都被打包到了同一个target目录下,classes,俗称这个路径为classpath
通过context读取properties配置文件中的信息:(需要一个文件流getResourceAsStream()
)
@Override
protected void doGet(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{
InpuStream is=this.getServletContext.getResourceAsStream("/WEB-INF/classes/db.properties");//将配置文件作为流输入
Properties prop=new Properties();
prop.load(is);
String username=prop.getProperty("username");
String pwd=prop.getProperty("password");
resp.getWriter().print(username+":"+pwd);
}
6.6、HttpServletResponse
web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletResponse对象和一个代表响应的HttpServletRequest对象;
1、简单分类
- 负责向浏览器发送数据的方法:
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
-
负责向浏览器发送响应头的方法:(编码、解码之类)
-
响应的状态码:
2、常见应用
-
向浏览器输出信息:(前面一直在使用)
-
下载文件:
- 获取下载文件的路径:
- 下载文件的文件名;
- 设置让浏览器能够支持下载我们需要的文件
- 获得文件输入流;
- 创建缓冲区;
- 获取Outputstream对象
- 将FileOutputStream流写入到缓冲区buffer;
- 使用OutputStream对象将缓冲区中的数据输出到客户端;
@Override protected void doGet(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{ //1. 获取下载文件的路径: String realPath="F:\\XXXX\\JavaWeb\\javaweb-02-servlet\\response\\target\\classes\\秦疆.png"; //2. 下载文件的文件名; String fileName=realPath.subString(RealPath.lastIndexOf("\\")+1); //3. 设置让浏览器能够支持(Content-Disposition)下载我们需要的文件,(URLEncoder.encode(fileName,"UTF-8"))解决中文文件名问题) resp.setHeader("Content-Disposition","attatchment;filename="+URLEncoder.encode(fileName,"UTF-8")); //4. 获得文件输入流; FileInputStream in = new FileInputStream(realPath); //5. 创建缓冲区; byte[] buffer=new byte[1024]; int len=0; //6. 获取Outputstream对象 ServletOutputStream out=resp.getOutputStream(); //7. 将FileOutputStream流写入到缓冲区buffer; while((len=in.read(buffer))!=-1){ out.write(buffer,0,len); } in.close(); out.close(); }
-
写完doget之后,记得要去pom.xml中注册新的web;
<servlet> <servlet-name>filedown</servlet-name> <servlet-class>com.kuang.servlet.FileServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>filedown</servlet-name> <url-pattern>/down</url-pattern> </servlet-mapping>
-
在Tomcat中发布新的项目;
3、验证码功能
实例:在网页上显示验证码(图片形式),每3s刷新一次
@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_RGN);//宽80,高20
//得到图片
Graphics2D g = (Graphics2D) image.getGraphics();//画笔
//设置图片的背景颜色
g.setColor(Color.white);
g.fillRect(0,0,80,20);
//给图片写入随机验证码
g.setColor(Color.BLUE);//切换蓝色画笔
g.setFont(new Font(null,Font.BOLD,20));
g.drawString(makeNum(),0,0);
//告诉浏览器,这个请求用图片方式打开
resp.setContentType("image/jpeg");//这里的JPEG也可以是JPG、png等格式
//网站存在缓存,设置不让缓存
resp.setDateHeader("expires",-1);
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma","no-cache");
//把图片写给浏览器--->ImageIO--->专门写图片的IO
ImageIO.write(image,"jpg",resp.getOutputStream());
}
//生成随机数方法
private String makeNum(){
Random random=new Random();
String num= random.nextInt(9999999)+"";//7位数,+""--->把int转换为String
//随机生成的num有可能小于7位,验证码少几位,就在StringBuffer里面添加几个0,之后再拼接起来,确保7位验证码
StringBuffer sb=new StringBUffer();
for(int i;i<7-num.length;i++){
ab.append("0");
}
num=sb.toString()+num;
return num;
}
接下来去 pom.xml中注册对应服务;
这种方式产生的验证码,可以再网站中下载验证码图片,因为我们是让浏览器把它识别为图片的形式("image/jpeg")
4、实现重定向
含义:一个web资源收到客户端请求之后,通知客户端去访问另一个web资源;
重定向实现:
void sendDirect(String var1) throws IOException;
测试:
@Override
protected void doGet(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{
resp.sendDirect("/r/img");
/*
resp.setHeader("Location","/r/img");
resp.setStatus(302);//重定向代码
*/
}
重定向和转发的区别:
- 相同:页面实现跳转
- 不同:
- 转发时,URL不会发生变化;
- 重定向时,客户端跳转到访问新的URL;
表单提交重定向实例:
index.jsp:(需要在maven中导入jsp的包)
<%-- action中,放入跳转的请求地址 -- 这里提交的路径,需要寻找到项目路径; -- ${pageContext.request.contextPath}代表当前项目 --%> <form action="${pageContext.request.contextPath}/login" method="get"> 用户名:<input type="text" name="username"> 密码:<input type="password" name="password"> <input type="submit"> </form>
发起请求的java类:
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("/r/success.jsp");//成功后跳转的jsp页面 } @Override protected void doPost(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException{ doGet(); } }
配置web.xml:网站中输入/login 就跳转到服务,将这个地址添加到jsp的action中
<servlet> <servlet-name>request</servlet-name> <servlet-class>com.kuang.servlet.RequestTest</servlet-class> </servlet> <servlet-mapping> <servlet-name>request</servlet-name> <url-pattern>/login</url-pattern> </servlet-mapping>
6.7、HttpServletRequest
HttpServletRequest代表客户端发送的请求,通过这个对象的方法,能够获得客户端提交的所有信息。
1、获取前端传递参数、请求转发
req.getParameter(String s) ---> 返回一个String
req.getParameterValues(String s) ---> 返回String[]数组
实例:
获取多选表单的值(代码和上例登录实例类似)
//解决乱码
req.setCharacterEncoding("utf-8"); //请求,解决控制台输出中文乱码问题
resp.setCharacterEncoding("utf-8"); //响应返回的解决乱码问题
//视频中的转发路径问题
req.getRequestDispatcher("/r/success.jsp").foward(req,resp);//转发,不需要 /r/success.jsp,直接写目标网页就行。现在这种方式会跳转到/r/r/success.jsp路径(404)
//sendRedirect() 重定向:方法中才需要添加 --> /路径
7、Cookie、Session
7.1、会话
会话:
有状态会话:服务器端会记录来过的客户端
一个网站如何知道一个客户端是否来访问过?
-
服务端给客户端一个信件,客户端下次访问的时候携带上这个信件;Cookie
-
服务端登记客户端已来过,下次客户端再来时,服务端对客户端进行匹配;Session;
7.2、保存会话的两种技术
Cookie:
- 客户端技术(响应,请求;);
Session:
- 服务器技术,我们可以利用这个技术,把客户端的会话信息保存在session中;
7.3、Cookie
-
从请求中拿到cookie信息;
-
服务器响应给客户端cookie
Cookie[] cookies=req.getCookies();//获得cookie cookie.getName();//获得cookie中的key cookie.getValue();//获得cookie中的value new Cookie("lastLoginTime",Sytem.currentTimeMillis()+"");//新建cookie cookie.setMaxAge(24*60*60);//设置cookie的有效期 resp.addCookie(cookie);//响应给客户端一个cookie
cookie:一般会保存在本地的 用户目录名下的appdata;
IO中解决中文乱码问题会用到:
//编码 Cookie cookie =new Cookie("name",URLEncoder.encode("秦疆","utf-8")); //解码 out.write(URLDecoder.decode(cook.getValue(),"utf-8"));
//解决中文乱码 req.setCharacterEncoding("UTF-8"); resp.setCharacterEncoding("UTF-8"); resp.setContentType("text/html;charset=UTF-8"); //我电脑上少了这条的话,输出还是中文乱码
关于cookie的一些细节:
- cookie:一般会保存在本地的用户目录下appdata;
- 一个网站的cookie是否存在上限?
- 一个cookie只能保存一个信息;
- 一个web站点可以给浏览器发送多个cookie,最多20个;
- cookie大小限制4kb;
- 300个cookie浏览器上限;
删除cookie:
- 不设置有效期,关闭浏览器就自动失效;
- 设置有效期为0;
实例:
通过cookie获得最后登录时间;
package com.study; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.Date; //保存用户上一次访问的时间 public class CookieDemo01 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"); //响应输出流 PrintWriter out = resp.getWriter(); //从客户端获得cookie Cookie[] cookies = req.getCookies();//这里返回的是一个cookie数组,说明cookie可能存在多个! //判断cookie是否存在 if (cookies != null) { //若存在则进行的操作 out.write("您上一次登录的时间是:"); for (int i = 0; i < cookies.length; i++) { Cookie cookie = cookies[i]; //获取cookie的值 if (cookie.getName().equals("LastLoginTime")) //将时间转换为long { long LastLoginTime = Long.parseLong(cookie.getValue()); Date date = new Date(LastLoginTime); out.write(date.toLocaleString()); } } } else { //否则通知这是第一次来,还没有cookie System.out.println("这是您的第一次访问"); } //服务器为客户端设置(添加)cookie,最后一次登录时间 Cookie cookie = new Cookie("LastLoginTime", System.currentTimeMillis() + ""); resp.addCookie(cookie); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } }
配置文件:web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd" version="5.0" metadata-complete="true"> <display-name>Welcome to Tomcat</display-name> <description> Welcome to Tomcat </description> <servlet> <servlet-name>CookieDemo01</servlet-name> <servlet-class>com.study.CookieDemo01</servlet-class> </servlet> <servlet-mapping> <servlet-name>CookieDemo01</servlet-name> <url-pattern>/c1</url-pattern> </servlet-mapping> </web-app>
pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>javaweb-session-cookie</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>jakarta.servlet.jsp</groupId> <artifactId>jakarta.servlet.jsp-api</artifactId> <version>3.0.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>5.0.0</version> <scope>provided</scope> </dependency> </dependencies> </project>
7.4、Session(重点)
什么是Session:
- 服务器会给每一个用户(浏览器)创建一个会话;
- session区别于cookie,他可以在值中传入对象!
session新建时,相当于:
Cookie cookie=new Cookie("JSESSIONID",sessionId); resp.addCookie(cookie);
相当于新建了一条sessionID,创建到了新的cookie中;
Session和Cookie的区别:
- Cookie是把用户的数据写给用户的浏览器,由浏览器保存;
- Session把用户的数据写到用户独占的Session中,只要浏览器没关,这个session就一直存在。由服务器端保存(只保存重要信息,减少服务器资源的浪费);
- Session对象由服务器创建;
使用场景:
- 保存用户登录的信息;
- 购物车信息;
- 在整个项目中经常访问到的数据,保存在Session中;
实例:
s1:获得session 的id,查看session是否已经存在,给session设置属性;
s2:得到session中的属性值
s3:使session失效;
s1:
package com.study; import com.study.pojo.Person; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import java.io.IOException; import java.io.PrintWriter; 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", new Person("Lemon", 1)); //获得session的id String id = session.getId(); //判断session是否是新创建的 if (session.isNew()) { PrintWriter writer = resp.getWriter(); writer.write("session创建成功!ID:" + id); } else { resp.getWriter().write("session已经存在!ID为" + id); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } }
s2:
//先解决乱码问题 req.setCharacterEncoding("utf-8"); resp.setCharacterEncoding("utf-8"); resp.setContentType("text/html;charset=utf-8"); //获得session HttpSession session = req.getSession(); //得到属性,输出到控制台 Person person = (Person) session.getAttribute("name"); System.out.println(person);
s3:
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); Object name = session.getAttribute("name"); //手动注销session session.invalidate(); }
在web.xml中设置session的有效时间:
<!-- 设置Session的默认生效时间--> <session-config> <!-- 15分钟之后失效 ,以分钟为单位--> <session-timeout>15</session-timeout> </session-config>
8、JSP
1、什么是JSP?
JSP:java server pages;Java服务器端页面,和Servlet一样属于动态web技术
特点:
- JSP像在写html;
- 区别:
- html 只给用户提供静态的数据;
- jsp页面中可以嵌入java代码,提供动态数据;
2、JSP原理
思路:JSP怎么执行的?
-
代码层面,服务器文件中jsp文件还是jsp文件;
-
服务器内部工作:
-
Tomcat中有一个work目录:
-
IDEA中使用Tomcat也会在IDEA的Tomcat工作空间中生成一个work目录:
C:\Users\Hanzo\AppData\Local\JetBrains\IntelliJIdea2021.3\tomcat\54acedf1-7fb2-4912-999b-53e632cac06c\work\Catalina\localhost\javaweb_session_cookie_war_exploded\org\apache\jsp
-
发现在work中jsp文件转换为了java文件;
-
-
分析:
-
浏览器向服务器发送请求,不论是访问什么资源,其实都是访问Servlet!
-
jsp文件在被访问的过程中,最终还是转换为了一个java文件!
-
所以,JSP本质上还是一个Servlet文件;
-
打开index_jsp.java,能发现其中的初始化,销毁,服务请求、响应方法(同Servlet中的方法)
public void _jspInit() { } public void _jspDestroy() { } //JSPService public void _jspService(HttpServletRequest request,HttpServletResponse response) throws java.io.IOException, jakarta.servlet.ServletException {
-
在这个转换的java文件的service方法中:
-
判断请求;
-
内置了一些对象:
//页面上下文, final jakarta.servlet.jsp.PageContext pageContext; //session jakarta.servlet.http.HttpSession session = null; //applicationContext final jakarta.servlet.ServletContext application; //config final jakarta.servlet.ServletConfig config; //out! jakarta.servlet.jsp.JspWriter out = null; //page:当前 final java.lang.Object page = this; //请求 HttpServletRequest request //响应 HttpServletResponse response
-
输出页面前,增加了一些代码:
response.setContentType("text/html"); //设置响应的页面类型 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;
-
以上这些变量可以再JSP中直接使用;
-
-
JSP页面在服务器的执行流程:
-
在JSP页面中:
-
只要是Java代码就会原封不动的输出
-
如果是HTML代码,就会转换为:
out.write("<html>\n"); out.write("<body>\n"); out.write("<h2>Hello World!</h2>\n"); out.write("</body>\n"); out.write("</html>\n");
这种样式进行输出渲染;
-
-
-
3、JSP基础语法
补充:IDEA建立一个普通maven项目,在通过添加支持转换为web项目
创建普通项目:
添加支持:
不过这种方式插入的项目结构跟标准的maven目录结构不一样
创建jsp项目需要导入的包:
<dependencies> <!--Servlet依赖--> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <!--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-api</artifactId> <version>1.2</version> </dependency> <!--standard标签--> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency> </dependencies
给项目配置Tomcat环境;
任何语言都有自己的语法,JSP作为java技术的一种应用,不仅支持java的所有语法,还额外扩充了一些语法(了解即可);
JSP表达式
<%-- jsp表达式
作用:用来将程序的输出,输出到客户端页面
--%>
<%= new java.util.Date()%>
JSP脚本片段
jsp脚本片段:
<%
for (int i = 0; i < 5; i++) {
out.println(i);
}
%>
-
脚本片段和html代码嵌套、交替使用;
-
作用域问题:
-
脚本中的作用域都是在jsp.java文件中的service方法中的局部变量;注意同名问题;
-
要想定义全局的变量、方法,就需要使用jsp声明标签:
<%! %>
包裹起来;用该标签框起来的代码,被放在了类下的全局位置,打开work生成的java文件可以看到:
-
JSP声明
jsp声明:会被编译到JSP生成的Java类中,其他的生成到_jspService方法中;
jsp声明使用标签:<%! %>
包裹起来;
在JSP中,嵌入java代码即可;
EL表达式:${};
小结
<%%> ---代码块
<%=%> --输出到页面
<%!%> ---声明
<%--注释--%>
JSP 注释 与 html注释 在JSP代码中的差异:
JSP的注释不会在客户端中显示,而html的注释会显示;
4、JSP指令
<%@ page args …… %>
<%@ include file="" %>
错误页面的转发:
<%@ page errorPage="error/500.jsp" %> |
直接在jsp页面头部写入错误转发指令(注意我这里如果用了<%=out.print(1/0)%>它并没有给我转发到我的页面,我也不知道原因) |
---|---|
另一种方法是在web.xml中配置响应的page属性,也能实现错误时的页面转发;(修改web.xml的话,就需要重启Tomcat) |
注意:这里转发page的时候,如果在另外一个jsp中
页面公用部分拼接:
<%@include file="/common/header.jsp" %> <h2>项目主体</h2> <%@include file="common/footer.jsp" %> |
|
---|---|
header.jsp | |
footer.jsp | |
效果 |
-
还可以用另外一种写法,JSP标签:
<jsp:include page="common/header.jsp"/>
注意:
JSP标签
&<%@%>
两种标签拼接方式的差异:
jsp:include page=""
@include file=""
拼接页面,本质上还是三个页面; @include
会将多个页面合为一个html页面(一套html代码)灵活性更高(常用) 在被拼接的页面中如果也有java的代码,并且与主页面的变量名可以重复,不会冲突 这种方式如果有重名的java变量,就会报错500
5、9大内置对象
- PageContext 存东西;
- Request 存东西;
- Response ;
- Session 存东西;
- Application 【ServletContext】 存东西;
- config 【ServletConfig】;
- out;
- page;不用了解
- Exception;
pageContext:保存的数据只能在一个页面中有效,一旦跳转到其他页面就无法访问了;
request:在一次请求过程中有效,请求转发会携带这个信息数据;客户端向服务器发送请求,产生的数据,在传递转发到下一个页面之后就没用了,比如:新闻;
session:保存的数据在一次会话中有效,打开浏览器到关闭浏览器;客户端向服务器发送请求,服务器替一个用户(客户端)保存数据,待会儿可能还会使用,比如:购物车;
application:保存的数据在服务器中有效,打开服务器到关闭服务器有效;客户端向服务器发送请求,产生的数据保存在服务器端,一个用户用完了,其他的用户也可以访问使用;
6、JSP标签、JSTL标签、EL表达式
导入需要的包:
<!--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>
EL表达式:$
- 获取数据;
- 执行运算;
- 获取web开发的常用对象;
作用域获取(当不同作用域存储键值对中的 key 相同时,想要直接获取该域对象的值可以通过 EL 表达式的方式)
<%-- EL 表达式的方式 --%>
<%-- 在作用域范围我们的名字取成一样了,怎么去找到特定的一个作用域中的值呢?? --%>
${pageScope.name}
${requstScope.name}
<%-- session比较特别,因为session是存在浏览器中的,只有关闭浏览器之后才可以拿到后面作用域中的值 --%>
${sessionScope.name}
${applicationScope.name}
JSP标签:
<%jsp:include%>
<%-- http://localhost:8080/jsptag.jsp?name=kuangshen&age=12 --%>
<%--等价于--%>
<jsp:forward page="/jsptag.jsp">
<jsp:param name="name" value="kuangshen"></jsp:param>
<jsp:param name="age" value="12"></jsp:param>
</jsp:forward>
JSTL标签:
JSTL标签库的使用就是为了弥补HTML标签的不足,自定义了许多的标签类,可以供我们使用,标签的功能和java代码一样;
- 核心标签:(掌握部分)
- 格式化标签:
- SQL标签:
- XML标签:
JSTL标签库使用步骤:
- 引入对应的taglib:
- 使用其中的标签;
- 注意:在Tomcat服务器中也需要引入JSTL的包,否则会报错;JSTL解析错误!
9、JavaBean
实体类
JavaBean有特定的写法:
- 必须要有一个无参构造;
- 属性必须私有化;
- 必须有对应的get/set方法;
一般用来和数据库的字段做映射;
ORM:对象关系映射
- 表-->类;
- 字段-->属性
- 行---->类对象
10、MVC三层架构
什么是MVC: Model View Controller 模型 视图 控制器
11、Filter过滤器
filter过滤器:过滤网站的请求、数据
- 处理中文乱码;
- 登录验证;
web 服务器启动后,过滤器加载初始化
//chain:链
1.过滤的所有代码,在过滤特定请求的时候都会执行;
2.必须要让过滤器继续通行; chain.doFilter(request,respone);
web服务器关闭时,过滤销毁;
12、监听器
实现一个监听器接口:(有N种)
实例:
编写监听网站在线访问人数!
13、过滤器、监听器的常见应用
用户登录之后才能进入主页,注销之后无法进入主页。
注意点:
页面中经常需要用到的值,例如用户登录之后保存在session中的
user_session
,可以再src下去创建一个静态常量的类,(有点类似枚举),Constant类,去给会反复用到的值定义一个变量名,例如:plubic class Constant{ public final static String USER_SESSION="USER_SESSION"; }
这么做的好处是,方便后期对他的值进行修改;(例如重命名)
权限管理:可以利用过滤器,在过滤器中对服务器中保存的对象进行等级判断,然后跳转到不同的界面;
14、JDBC回顾
JDBC:连接数据库的接口
jar包依赖:
- java.sql
- javax.sql
- mysql-connecter-java 连接驱动(必须导入)
事务
ACID原则:保证数据安全;
开启事务
提交事务 commit()
事务回滚 rollback()
关闭事务
补充知识点:
Test单元测试,可以直接执行方法,不需要main方法在调用(只在方法上有效)
package org.junit @Test
JDBC中通知数据库开启事务,setAutoCommit(false);
//false 是开启,即关闭自动提交
connection.setAutoCommit(false);
// 提交
connection.commit();
一般在事务中通过捕获异常,让事务回滚;
try{
//执行事务
}catch(Eception e){
try{
//如果事务出现异常,回滚
connection.rollback();
}catch(SQLException e1){
e1.printStackTrace();
}
e.printStackTrace();
}finally{
//关闭资源
connection.close();
}
项目:SMBMS----超市订单管理系统
详细见项目管理笔记;
BaseDao:
关闭jdbc连接资源的方法:
public static boolean closeResource(Connection conn,PreparedStatement pst,ResultSet rs){
boolean flag=true;
if(resultSet!=null){
try{
resultSet.close();
//设置为空,让GC回收
resultSet=null;
}catch(SQLException e){
e.printStackTrace();
flag=false;
}
}
if(pst!=null){
try{
pst.close();
//设置为空,让GC回收
pst=null;
}catch(SQLException e){
e.printStackTrace();
flag=false;
}
}
if(conn!=null){
try{
conn.close();
//设置为空,让GC回收
conn=null;
}catch(SQLException e){
e.printStackTrace();
flag=false;
}
}
return flag;
}