HTML CSS JavaScript JQuery 什么是JQuery JQuery,顾名思义,就是JavaScript和Query(查询),它是辅助JavaScript开发的js类库
使用JQuery的好处 JQuery是免费的、开源的,JQuery的语法设计可以使开发更加便捷,例如操作文档对象、选择DOM元素、制作动画效果、事件处理、使用ajax以及其他功能。
JQuery的核心函数 $是JQuery的核心函数,能完成JQuery的很多功能,$()就是调用$这个函数。 1.传入参数为函数时:表示页面加载完成之后。相当于window.onload=function(){} 2.传入参数为HTML字符串时:会为我们创建这个html标签对象 3.传入参数为选择器字符串时:如$(“#id属性值”):id选择器,还适用于标签名和类名 4.传入参数为DOM对象时,将DOM对象包装为JQuery对象返回
1 2 3 4 5 6 7 8 $(function ( ) { let $btnObj = $("#btnId" ) $btnObj.click (function ( ) { alert ('jQuery的单击事件' ) }) $(`<div>你好</div>` ).appendTo ("body" ) })
JQuery对象和DOM对象使用区别 JQuery对象不能使用DOM对象的属性和方法 DOM对象也不能使用JQuery对象的属性和方法
DOM对象和JQuery对象互转 1.DOM对象转化为JQuery对象 let $obj = $(dom对象) 2.JQuery对象转化为DOM对象 let dom = $obj[下标]
基本选择器 #ID选择器:根据id查找标签对象 .class选择器:根据class查找标签对象 element选择器:根据标签名查找标签对象 *选择器:表示任意的,所有的元素 selector1,selector2组合选择器:合并选择器1,选择器2的结果并返回 示例:
1 2 3 4 5 6 $("#id" ) $(".class" ) $("div" ) $("*" ) $("div,span" )
层级选择器 后代选择器 HTML代码:
1 2 3 4 5 6 7 8 <form > <input name ="name" > <fieldset > <label > NewSeletter</label > <input name ="newsletter" > </fieldset > </form > <input name ="none" >
JQuery代码:
父子选择器 jQuery代码:
prev+next 匹配所有紧接在prev元素后的next元素
prev + siblings 匹配在prev后所有的元素
过滤选择器 基本过滤选择器 :first 获取第一个元素
1 2 3 4 5 6 7 <ul > <li > 1</li > <li > 2</li > <li > 3</li > <li > 4</li > <li > 5</li > </ul >
jQuery代码:
:last 获取最后一个元素 jQuery代码:
:not(selector) 去除所有与给定选择器匹配的元素
1 2 <input name ="apple" > <input name ="flower" checked ="checked" >
jQuery代码:
1 $("input:not(:checked)" )
:even 匹配所有索引值为偶数的元素,从0开始计数
1 2 3 4 5 6 <table > <tr > Header</tr > <tr > Value1</tr > <tr > Value2</tr > </table >
jQuery代码:
1 2 <!--查找表格的1 、3 、5 行--> $("tr:even" )
:odd 匹配所有索引值为奇数的元素,从0开始计数 jQuery代码:
1 2 <!--查找表格的2 、4 、6 行--> $("tr:odd" )
:eq(index) 匹配一个给定索引值的元素 jQuery代码:
:gt(index) 匹配所有大于给定索引值的元素 jQuery代码:
:lt(index) 匹配所有小于给定索引值的元素 jQuery代码:
匹配如h1,h2,h3之类的标题元素
1 2 3 4 <h1 > Header 1</h1 > <p > Context1</p > <h2 > Context2</h2 > <p > Context3</p >
jQuery代码:
1 $("header" ).css ("background" ,"#EEE" )
:animated 匹配所有正在执行动画效果的元素
其它选择器请查看jQueryAPI文档
https://jquery.cuishifeng.cn/index.html
XML 什么是XML xml是可扩展的标记性语言。 主要作用为:1.用来保存数据,而且这些数据具有自我描述性。
1 2 3 4 5 6 7 8 9 10 11 <students > <student > <id > 1</id > <name > 张三</name > </student > <student > <id > 2</id > <name > 李四</name > </student > </students >
2.它还可以作为项目或者模块的配置文件。 3.还可以作为网络传输数据的格式。
XML语法 文档声明 1.创建一个xml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?xml version="1.0" encoding="utf-8" ?> <books > <book sn ="SN123456789" > <name > 时间简史</name > <author > 霍金</author > <price > 75</price > </book > <book sn ="SN123456769" > <name > Java从入门到入土</name > <author > 安格斯</author > <price > 9.9</price > </book > </books >
xml注释
xml和html的注释一样:<!– –>
xml元素
XML 命名规则 1.名称可以含字母、数字以及其他的字符 2.名称不能以数字或者标点符号开始 3.名称不能以字符 “xml”(或者 XML、Xml)开始 4.名称不能包含空格 5.可使用任何名称,没有保留的字词。
XML语法规则 1.所有XML元素都必须有关闭标签。 2.XML标签对大小写敏感。 3.XML必须正确的嵌套。 4.文档必须有一个根元素,且是唯一的。 5.XML的属性值必须加引号。 6.XML中的特殊字符,与html保持一致。 7.文本区域(CDATA区): CDATA语法可以告诉xml解析器,CDATA里的文本内容,只是纯文本,不需要xml语法解析。 CDATA格式:<![CDAT[这里可以把输入的字符原样显示,不会解析xml]]>
dom4j解析技术 使用第三方公司的dom4j.jar进行xml的解析 1.在src文件夹下创建一个com.geo.pojo的文件夹 2.在pojo文件夹下创建一个java文件 3.定义所需要的变量 4.使用alt+ins快速创建get和set 5.使用alt+ins创建构造函数 6.导入下载好的jar包,在根目录下创建一个lib文件夹用于存放
Dom4j.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.geo.pojo;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;import org.junit.Test;import java.math.BigDecimal;import java.util.List;public class Dom4jTest { @Test public void test1 () throws Exception { SAXReader saxReader = new SAXReader (); Document document = saxReader.read("D:\\IDEA_Workspace\\JavaWeb\\_05-xml\\xml\\books.xml" ); System.out.println(document); } @Test public void test2 () throws DocumentException { SAXReader reader = new SAXReader (); Document document = reader.read("src/books.xml" ); Element rootElement = document.getRootElement(); System.out.println(rootElement); List<Element> books = rootElement.elements("book" ); for (Element book : books){ Element nameElement = book.element("name" ); String nameText = nameElement.getText(); String priceText = book.elementText("price" ); String authorText = book.elementText("author" ); String snValue = book.attributeValue("sn" ); System.out.println(new Book (snValue,nameText, BigDecimal.valueOf(Double.parseDouble(priceText)),authorText)); } } }
输出结果:
JavaWeb的概念 什么是JavaWeb JavaWeb是指,所有通过Java语言编写可以通过浏览器访问的程序的总称,叫JavaWeb。 JavaWeb是基于请求和响应来开发的。
什么是请求 请求是指客户端给服务器发送数据,叫请求Request
什么是响应 响应是指服务器给客户端回传数据,叫响应Response
Web资源的分类 web资源按实现的技术和呈现的效果不同,又分为静态资源和动态资源两种。 静态资源:html、css、js、txt、mp4、jpg图片 动态资源:jsp页面,servlet程序
常用的web服务器 Tomcat、Jboss、GlassFish、Resin、WebLogic
Tomcat服务器和Servlet版本的对应关系:
Tomcat的使用 下载、安装、配置环境变量
目录介绍 bin:用于存放Tomcat服务器的可执行程序 conf:用于存放Tomcat服务器的配置文件 lib:用于存放Tomcat服务器的jar包 logs:用于存放Tomcat服务器运行时输出的日志信息 temp:用于存放Tomcat运行时产生的临时的数据 webapps:用于存放部署的web工程 work:是Tomcat工作时的目录,用来存放Tomcat运行时jsp翻译为servlet的源码,和Session钝化的目录
如何修改Tomcat的端口号 找到Tomcat目录下的conf目录,找到server.xml配置文件。 找到connector标签,将port属性更改为想要的端口号即可(1-65535)。 修改完端口号,一定要重启Tomcat服务器才会生效。
如何部署web工程到Tomcat中
第一种方法:只需要把web工程的目录拷贝到webapps的目录中即可
然后输入地址:http://localhost:8080/工程名
第二种部署方法:找到Tomcat下的conf目录\Catalina\localhost\下,创建如下的配置文件:
xxxxxxxxxx37 1public List executeQuery(Class clazz, String sql, Object… params) throws SQLException, InstantiationException, IllegalAccessException, NoSuchFieldException {2 Connection connection = JdbcUtilsV2.getConnection();3 PreparedStatement preparedStatement = connection.prepareStatement(sql);4 if (params != null && params.length != 0) {5 for (int i = 1; i <= params.length; i++) {6 preparedStatement.setObject(i, params[i - 1]);7 }8 }9 ResultSet resultSet = preparedStatement.executeQuery();10 List list = new ArrayList<>();11 ResultSetMetaData metaData = resultSet.getMetaData();12 int columnCount = metaData.getColumnCount();13 while (resultSet.next()) {14 T t = clazz.newInstance();15 for (int i = 1; i <= columnCount; i++) {16 // 对象的属性值17 Object object = resultSet.getObject(i);18 // 获取指定下角标列的名称19 String columnLabel = metaData.getColumnLabel(i);2021 // 反射,给对象的属性赋值22 Field field = clazz.getDeclaredField(columnLabel);23 // 权限可能是私有的,更改权限24 field.setAccessible(true);25 field.set(t, object);26 }27 list.add(t);2829 }30 resultSet.close();31 preparedStatement.close();32 if (connection.getAutoCommit()) {33 JdbcUtilsV2.closeConnection();34 }35 return list;3637}java
ROOT工程的访问,以及默认index.html页面的访问 当我们在浏览器地址栏中输入访问地址如下:http://localhost:8080/ ===> 没有工程名的时候,默认访问的是ROOT工程http://localhost:8080/工程名/ ===> 没有资源名,默认访问index.html页面
IDEA整合Tomcat服务器
IDEA中动态web工程的操作
创建一个新模块(JavaEE,现为JakartaEE)
如果访问servlet报500错误,可能是JavaEE版本太低了(Tomcat版本太高了)。所以选择这个。
然后打开项目结构,创建工件,选择Web应用程序展开型,基于模块。
如果没有基于模块的选项,则添加web支持。
此时如果报错无法保存源根xxxxxxx,解决方法:
控制台乱码(高版本JDK22和tomcat10.1),将文件编码全部改为GBK,tomcat运行配置虚拟机属性改为GBK,tomcat文件夹中conf文件夹下的logging.properties,其下内容改为:java.util.logging.ConsoleHandler.encoding = GBK。
在webapp目录下的WEB-INF目录下创建一个lib文件夹用于存放导入的jar包
确认Tomcat实例中有要部署运行的web工程模块
Servlet技术 什么是Servlet 1.Servlet是JaveEE规范之一,就是接口。 2.Servlet是JavaWeb三大组件之一,分别是:Servlet程序、Filter过滤器、Listener监听器。 3.Servlet是运行在服务器上的一个Java小程序,它可以接受客户端发送过来的请求,并响应数据给客户端。
手动实现Servlet程序 1.编写一个类去实现Servlet接口 2.实现一个Service方法,处理请求,并响应数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.example._06servlet;import jakarta.servlet.*;import java.io.IOException;public class HelloServlet implements Servlet { @Override public void init (ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig () { return null ; } @Override public void service (ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("Hello Servlet 被访问了" ); } @Override public String getServletInfo () { return null ; } @Override public void destroy () { } }
3.到web.xml中去配置servlet程序的访问地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?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" > <servlet > <servlet-name > HelloServlet</servlet-name > <servlet-class > com.example._06servlet.HelloServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > HelloServlet</servlet-name > <url-pattern > /hello</url-pattern > </servlet-mapping > </web-app >
在部署运行之后在路径后加上/hello就会访问到HelloServlet,并且控制台会输出Hello Servlet被访问了
Servlet的生命周期 1.执行Servlet构造器方法 2.执行init初始化方法 第一二步是在第一次访问的时候创建Servlet程序会调用。 3.执行service方法 第三步,每次访问都会调用。 4.执行destroy销毁方法 第四步,在web工程停止的时候调用。
Servlet请求的分发处理 什么是请求分发:针对get和post操作执行不同的代码 在HelloServlet.java文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 package com.example._06servlet;import jakarta.servlet.*;import jakarta.servlet.http.HttpServletRequest;import java.io.IOException;public class HelloServlet implements Servlet { public HelloServlet () { System.out.println("1.构造器方法" ); } @Override public void init (ServletConfig servletConfig) throws ServletException { System.out.println("2.init初始化" ); } @Override public ServletConfig getServletConfig () { return null ; } @Override public void service (ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("3.service === Hello Servlet 被访问了" ); HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; String method= httpServletRequest.getMethod(); if ("GET" .equals(method)){ doGet(); }else if ("POST" .equals(method)){ doPost(); } } public void doGet () { System.out.println("get请求" ); } public void doPost () { System.out.println("post请求" ); } @Override public String getServletInfo () { return null ; } @Override public void destroy () { System.out.println("4.destroy销毁方法" ); } }
通过继承HttpServlet实现Servlet程序 一般在实际项目开发中,都是使用继承HttpServlet类的方式去实现Servlet程序 1.编写一个类去继承HttpServlet类 2.根据业务需要重写doGet或doPost方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.example._06servlet;import jakarta.servlet.ServletException;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;public class HelloServlet2 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("HelloServlet2的doGet方法" ); } @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("HelloServlet2的doPost方法" ); } }
3.到web.xml中的配置Servlet程序的访问地址
1 2 3 4 5 6 7 8 <servlet > <servlet-name > HelloServlet2</servlet-name > <servlet-class > com.example._06servlet.HelloServlet2</servlet-class > </servlet > <servlet-mapping > <servlet-name > HelloServlet2</servlet-name > <url-pattern > /hello2</url-pattern > </servlet-mapping >
使用IDEA创建Servlet程序 创建方法: 然后在web.xml文件中补充一下servlet-mapping标签,IDEA会为我们自动创建Servlet标签
Servlet类的继承体系
ServletConfig类 ServletConfig类从类名上看,就知道是Servlet程序的配置信息类。 Servlet程序和ServletConfig对象都是由Tomcat负责创建,我们负责使用。 Servlet程序默认是第一次访问的时候创建,ServletConfig是每个Servlet程序创建时,就创建一个对应的ServletConfig对象
ServletConfig的三大作用 1.可以获取Servlet程序的别名 servlet-name 的值 2.获取初始化参数init-param 3.获取servletContext对象
1 2 3 4 5 6 7 8 9 10 11 12 @Override public void init (ServletConfig servletConfig) throws ServletException { System.out.println("2.init初始化" ); System.out.println("servlet的别名是" +servletConfig.getServletName()); System.out.println("初始化参数username的值是" +servletConfig.getInitParameter("username" )); System.out.println("初始化参数url的值是" +servletConfig.getInitParameter("url" )); System.out.println("获取servletContext对象是" +servletConfig.getServletContext()); }
ServletContext类 什么是ServletContext 1.ServletContext是一个接口,表示Servlet上下文对象 2.一个web工程,只有一个ServletContext对象实例 3.ServletContext是一个域对象 4.ServletContext是在web工程部署启动的时候创建,在web工程停止的时候销毁
域对象是可以像Map一样存取数据的对象,叫域对象,域指的是存取对象的操作范围。
存数据:setAttribute() 取数据:getAttribute() 删数据:removeAttribute()
ServletContext类的四个作用 1.获取web.xml中配置的上下文参数context-param 2.获取当前的工程路径:/工程路径 3.获取工程部署后在服务器硬盘上的绝对路径 4.像Map一样存取数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.example._06servlet; import jakarta.servlet.*;import jakarta.servlet.http.*;import java.io.IOException;public class ContextServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletContext context = getServletConfig().getServletContext(); String username = context.getInitParameter("username" ); System.out.println("context-param参数username的值是:" +username); String password = context.getInitParameter("password" ); System.out.println("context-param参数password的值是:" +password); System.out.println("当前工程路径:" +context.getContextPath()); System.out.println("工程部署的路径是:" +context.getRealPath("/" )); System.out.println("工程下css目录的绝对路径是:" +context.getRealPath("/css" )); System.out.println("工程下img目录的绝对路径是:" +context.getRealPath("/image/QQ图片20220907091157.jpg" )); context.setAttribute("key1" ,"value1" ); System.out.println("context1中获取域数据key1的的值是:" +context.getAttribute("key1" )); } @Override protected void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }
HTTP协议
所谓的HTTP协议就是指,客户端和服务器之间通信时,发送的数据,需要遵守的规则,叫HTTP协议,HTTP协议中的数据又叫报文。
请求的HTTP协议格式 客户端给服务器发送数据叫请求,服务器给客户端回传数据叫响应。
请求又分为get请求和post请求
GET请求 1.请求行 (1)请求的方式 GET (2)请求的资源路径[+?+请求参数] (3)请求的协议和版本号 HTTP/1.1 2.请求头 key: value组成,不同键值对表示不同的含义 第一行:请求行 1.请求的方式:GET 2.请求的资源路径:/06_servlet 3.请求的协议的版本号:HTTP/1.1 第二行及以后:请求头 Accept:告诉服务器,客户端可以接收的数据类型 Accept-Language:告诉服务器客户端可以接收的语言类型 1.zh_CN:中文中国 2.en_US:英文美国 User-Agent:就是浏览器的信息 Accept-Encoding:告诉服务器,客户端可以接收的数据编码(压缩)格式 Host:表示请求的服务器ip和端口号 Connection:告诉服务器当前连接如何处理 1.Keep-Alive:告诉服务器回传完数据不要马上关闭,保持一小段时长的连接 2.Closed:马上关闭
POST请求 1.请求行 (1)请求的方式 POST (2)请求的资源路径[+?+请求参数] (3)请求的协议和版本号 HTTP/1.1 2.请求头 key: value组成,不同键值对表示不同的含义
3.请求体 ===> 就是发送给服务器的数据 第一行:请求行 1.请求的方式:POST 2.请求的资源路径:/06_servlet/hello3 3.请求的协议和版本号:HTTP/1.1 第二行及以后:请求头 1.Accept:表示客户端可以接收的数据类型 2.Accept-Language:表示客户端可以接收的语言类型 3.Referer:表示请求发起时,浏览器地址栏中的地址(从哪来) 4.User-Agent:表示浏览器的信息 5.Content-Type:表示发送的数据的类型 application/x-www-form-urlencoded:表示提交的数据格式是:name=value&name=value,然后对其进行url编码 url编码是把非英文内容转换为:%xx%xx multipart/form-data:表示以多段的形式提交数据给服务器(以流的形式,用于上传) 6.Content-length:表示发送的数据的长度 7.Cache-Control:表示如何控制缓存,no-cache不缓存 最后一行:请求体 action=login&username=root(发送给服务器的数据)
哪些是get请求,哪些是post请求 Get请求有哪些: 1.form标签method=get 2.a标签 3.link标签引入css 4.script标签引入js文件 5.img标签引入图片 6.iframe引入html页面 7.在浏览器地址栏中输入地址后敲回车
Post请求有哪些: form标签method=post
响应的HTTP协议格式 1.响应行: (1)响应的协议和版本号 (2)响应状态码 (3)响应状态描述符 2.响应头 (1)key:value 空行 3.响应体 ===> 就是回传给客户端的数据 第一行:响应行 1.响应的协议:HTTP/1.1 2.响应状态码:200 3.响应状态描述符:OK 第二行及以下:响应头 1.Server:表示服务器的信息 2.Content-Type:表示响应体的数据类型 3.Content-length:响应体的长度 4.Date:请求响应的时间(格林时间) 空行以下:响应体
常见的响应码 200:表示请求成功 302:表示请求重定向 404:表示请求服务器已经收到了,但是数据不存在(请求地址错误) 500:表示服务器已经收到请求,但是服务器内部错误
MIME类型说明 MIME是HTTP协议中数据类型。 MIME的英文全称是”Multipurpose Internet Mail Extensions”多功能Internet邮件扩充服务。MIME类型的格式是”大类型/小类型“,并与某一种文件的扩展名相对应。 常见的MIME类型:
HttpServletRequest类 HttpServletRequest类有什么作用 每次只要有请求进入Tomcat服务器,Tomcat服务器就会把请求过来的HTTP协议信息解析好封装到Request对象中。 然后传递到service方法(doGet和doPost)中给我们使用。我们可以通过HttpServletRequest对象,获取到所有的请求信息。
HttpServletRequest类的常用方法 1.getRequestURI():获取请求的资源路径 2.getRequestURL():获取请求的统一资源定位符(绝对路径) 3.getRemoteHost():获取客户端的ip地址 4.getHeader():获取请求头 5.getParameter():获取请求的参数 6.getParameterValues():获取请求的参数(多个值的时候使用) 7.getMethod():获取请求的方式GET或POST 8.setAttribute(key,value):设置域数据 9.getAttribute(key):获取域数据 10.getRequestDispatcher():获取请求转发对象
请求的转发 请求转发是指,服务器收到请求后,从一个资源跳转到另一个资源的操作 具体实现代码:
Servlet1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Servlet1 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); System.out.println("在Servlet1中查看参数:" +username); req.setAttribute("key" ,"Servlet1的章" ); RequestDispatcher requestDispatcher = req.getRequestDispatcher("/servlet2" ); requestDispatcher.forward(req,resp); } }
Servlet2
1 2 3 4 5 6 7 8 9 10 11 12 public class Servlet2 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); System.out.println("在Servlet2中查看参数:" +username); Object key = req.getAttribute("key" ); System.out.println("Servlet1是否有章:" +key); System.out.println("servlet2处理自己的业务" ); } }
web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <servlet > <servlet-name > Servlet1</servlet-name > <servlet-class > com.geo.servlet.Servlet1</servlet-class > </servlet > <servlet-mapping > <servlet-name > Servlet1</servlet-name > <url-pattern > /servlet1</url-pattern > </servlet-mapping > <servlet > <servlet-name > Servlet2</servlet-name > <servlet-class > com.geo.servlet.Servlet2</servlet-class > </servlet > <servlet-mapping > <servlet-name > Servlet2</servlet-name > <url-pattern > /servlet2</url-pattern > </servlet-mapping >
请求转发的特点: 1.浏览器地址栏没有变化 2.他们是一次请求 3.他们共享Request域中的数据 4.可以转发到WEB-INF目录下
base标签的作用:主要用于工程中相对路径的参照,写在title标签的下方
HttpServletResponse类 HttpServletResponse类的使用 HttpServletResponse类和HttpServletRequest类一样,每次请求进来,Tomcat服务器就会创建一个Response对象传递给Servlet程序去使用。HttpServletRequest表示请求过来的信息,HttpServletResponse表示所有响应的信息。 需要返回给客户端的消息,就可以通过HttpServletResponse对象来进行设置。
两种输出流的使用 字符流 getOutputStream() 常用于下载(传递二进制数据) 字节流 getWriter() 常用于回传字符串(常用)
两个输出流同时只能使用一个。 使用了字节流,就不能再使用字符流。否则会报错。
往客户端回传数据 1.往客户端回传字符串数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.geo.servlet;import jakarta.servlet.ServletException;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;public class ResponseIOServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter writer = resp.getWriter(); writer.write("之一's content!" ); } }
客户端乱码解决 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.geo.servlet;import jakarta.servlet.ServletException;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;public class ResponseIOServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter writer = resp.getWriter(); resp.setContentType("text/html;charset=UTF-8" ); writer.write("之一's content!" ); } }
请求重定向 请求重定向,是指客户端向服务器发出请求,然后服务器告诉客户端,给一个新的地址,去新地址进行访问,称作请求的重定向。(因为之前的地址可能被废弃了)
请求重定向的特点 1.浏览器中的地址会发生变化 2.两次请求 3.不共享Request域中的数据 4.不能访问WEB-INF下的资源 5.可以访问工程外部的资源,比如百度等
Response1.java
1 2 3 4 5 6 7 8 9 10 public class Response1 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("访问到了Response1" ); resp.setStatus(302 ); resp.setHeader("Location" , "http://localhost:8080/_07_servlet/response2" ); } }
Response2.java
1 2 3 4 5 6 7 public class Response2 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("我是Response2" ); resp.getWriter().write("我是Response2" ); } }
上述代码实现的功能就是当访问Response1的时候,会跳转到Response2的页面,然后输出相关内容。从而实现重定向的功能。
第二种重定向方法,更加简洁
Response1.java
1 2 3 4 5 6 7 public class Response1 extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("访问到了Response1" ); resp.sendRedirect("http://localhost:8080/_07_servlet/response2" ) } }
JavaEE项目的三层架构 Web层负责页面展示,Service层负责业务逻辑的处理,Dao层负责与数据库进行交互。
JavaEE项目为什么要进行分层? 分层的目的是为了解耦,降低代码的耦合度,方便项目后期的维护和升级。
一般分为以下结构: Web层: com.geo.web/servlet/controller service层: com.geo.service service接口包 com.geo.service.impl service接口实现类 Dao持久层: com.geo.dao Dao接口包 com.geo.dao.impl Dao接口实现类 实体Bean对象: com.geo.pojo/entity/domain/bean JavaBean类 测试包: com.geo.test/junit 工具类: com.geo.utils
第一步:创建好项目结构 创建好之后项目结构如下所示:
第二步,在数据库中创建好相应的数据表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 CREATE DATABASE book;USE book; CREATE TABLE t_user( `id` int PRIMARY KEY auto_increment, `username` VARCHAR (20 ), `password` VARCHAR (32 ), `email` VARCHAR (200 ) ); INSERT INTO t_user(`username`,`password`,`email`) VALUES ('admin' ,'admin' ,'geo' );SELECT * FROM t_user;
可以看到已经创建好t_user的表格了。
第三步,创建好数据库表对应的JavaBean对象。
在pojo层中创建User类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 package com.geo.pojo;public class User { private Integer id; private String username; private String password; private String email; public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getUsername () { return username; } public void setUsername (String username) { this .username = username; } public String getPassword () { return password; } public void setPassword (String password) { this .password = password; } public String getEmail () { return email; } public void setEmail (String email) { this .email = email; } @Override public String toString () { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", email='" + email + '\'' + '}' ; } public User () { } public User (Integer id, String username, String password, String email) { this .id = id; this .username = username; this .password = password; this .email = email; } }
第四步,编写JdbcUtils类 首先在Utils包下简历JdbcUtils类,然后将下载好的mysql-connector-java.jar和druid.jar拖到web-inf下的lib文件夹中,然后选中这两个jar包右键添加到库。 然后将jdbc.properties的配置文件添加到src文件夹下,其中的内容如下所示: 这些都是本机数据库的一些相关配置信息。
1 2 3 4 5 6 username =root password =123456 url =jdbc:mysql://localhost:3306/book driverClassName =com.mysql.cj.jdbc.Driver initialSize =5 maxActive =10
然后在JdbcUtils类中进行连接池的创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package com.geo.utils;import com.alibaba.druid.pool.DruidDataSource;import com.alibaba.druid.pool.DruidDataSourceFactory;import java.io.InputStream;import java.sql.Connection;import java.sql.SQLException;import java.util.Properties;public class JdbcUtils { private static DruidDataSource dataSource; static { try { Properties properties = new Properties (); InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties" ); properties.load(inputStream); dataSource = (DruidDataSource) DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection () { Connection connection = null ; try { connection = dataSource.getConnection(); } catch (Exception e) { e.printStackTrace(); } return connection; } public static void close (Connection connection) { if (connection != null ) { try { connection.close(); } catch (SQLException e) { throw new RuntimeException (e); } } } }
第五步,在test包下创建JdbcUtilsTest测试类,需要添加hamcrest.jar和junit.jar到库中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.geo.test;import com.geo.utils.JdbcUtils;import org.junit.Test;import java.sql.Connection;public class JdbcUtilsTest { @Test public void testJdbcUtils () { for (int i = 0 ; i < 100 ; i++) { Connection connection = JdbcUtils.getConnection(); System.out.println(connection); JdbcUtils.close(connection); } } }
第六步,编写BaseDao,在Dao.impl包中创建BaseDao类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 package com.geo.dao.impl;import com.geo.utils.JdbcUtils;import org.apache.commons.dbutils.QueryRunner;import org.apache.commons.dbutils.handlers.BeanHandler;import org.apache.commons.dbutils.handlers.BeanListHandler;import java.sql.Connection;import java.sql.SQLException;import java.util.List;import java.util.logging.Handler;public abstract class BaseDao { private QueryRunner queryRunner = new QueryRunner (); public int update (String sql, Object... args) { Connection connection = JdbcUtils.getConnection(); try { return queryRunner.update(connection, sql, args); } catch (SQLException e) { throw new RuntimeException (e); } finally { JdbcUtils.close(connection); } } public <T> T queryForOne (Class<T> type, String sql, Object... args) { Connection connection = JdbcUtils.getConnection(); try { return queryRunner.query(connection, sql, new BeanHandler <T>(type), args); } catch (SQLException e) { throw new RuntimeException (e); } finally { JdbcUtils.close(connection); } } public <T> List<T> queryForList (Class<T> type, String sql, Object... args) { Connection connection = JdbcUtils.getConnection(); try { return queryRunner.query(connection, sql, new BeanListHandler <>(type), args); } catch (SQLException e) { throw new RuntimeException (e); } finally { JdbcUtils.close(connection); } } }
第七步,编写UserDao和测试,在Dao包中创建接口UserDao
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package com.geo.dao;import com.geo.pojo.User;public interface UserDao { User queryUserByUsername (String username) ; User queryUserByUsernameAndPassword (String username, String password) ; int saveUser (User user) ; }
实现类:UserImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.geo.dao.impl;import com.geo.dao.UserDao;import com.geo.pojo.User;public class UserImpl extends BaseDao implements UserDao { @Override public User queryUserByUsername (String username) { String sql = "select `id`,`username`,`password`,`email` from t_user where username = ?" ; return queryForOne(User.class, sql, username); } @Override public User queryUserByUsernameAndPassword (String username, String password) { String sql = "select `id`,`username`,`password`,`email` from t_user where username = ? and password = ?" ; return queryForOne(User.class, sql, username, password); } @Override public int saveUser (User user) { String sql = "insert into t_user(`username`,`password`,`email`) values(?,?,?);" ; return update(sql, user.getUsername(), user.getPassword(), user.getEmail()); } }
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package com.geo.test;import com.geo.dao.UserDao;import com.geo.dao.impl.UserImpl;import com.geo.pojo.User;import org.junit.Test;import static org.junit.Assert.*;public class UserDaoTest { @Test public void queryUserByUsername () { UserDao userDao = new UserImpl (); if (userDao.queryUserByUsername("admin1" ) == null ) { System.out.println("用户名可用!" ); } else { System.out.println("用户名已存在!" ); } } @Test public void queryUserByUsernameAndPassword () { UserDao userDao = new UserImpl (); if (userDao.queryUserByUsernameAndPassword("admin" , "admin123" ) == null ) { System.out.println("账号或密码不正确!" ); } else { System.out.println("登录成功!" ); } } @Test public void saveUser () { UserDao userDao = new UserImpl (); System.out.println(userDao.saveUser(new User (1 , "xiaojie" , "11111" , "135611" ))); } }
测试结果最终都正确无误,数据库中也新增了添加好的数据。
第八步:编写UserService和测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.geo.service;import com.geo.pojo.User;public interface UserService { void registerUser (User user) ; User login (User user) ; boolean existsUsername (String username) ; }
UserService接口的实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.geo.service.impl;import com.geo.dao.UserDao;import com.geo.dao.impl.UserImpl;import com.geo.pojo.User;import com.geo.service.UserService;import jdk.jshell.spi.ExecutionControl;public class UserServiceImpl implements UserService { private UserDao userDao = new UserImpl (); @Override public void registerUser (User user) { userDao.saveUser(user); } @Override public User login (User user) { return userDao.queryUserByUsernameAndPassword(user.getUsername(), user.getPassword()); } @Override public boolean existsUsername (String username) { if (userDao.queryUserByUsername(username) == null ) { return false ; } return true ; } }
UserService接口的测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.geo.test;import com.geo.pojo.User;import com.geo.service.UserService;import com.geo.service.impl.UserServiceImpl;import org.junit.Test;import static org.junit.Assert.*;public class UserServiceTest { UserService userService = new UserServiceImpl (); @Test public void registerUser () { userService.registerUser(new User (1 , "xiaohong" , "123456" , "qq@c.om" )); } @Test public void login () { System.out.println(userService.login(new User (null , "xiahon" , "1234" , "adwa @" ))); } @Test public void existsUsername () { System.out.println(userService.existsUsername("xiaohong" )); } }
项目阶段二:用户注册和登录的实现 需求1:用户注册
访问注册页面
填写注册信息,提交给服务器
服务器应当保存用户
当用户已经存在提示用户注册失败,用户名已存在
当用户不存在->注册成功
Tips:当前阶段,在表单提交的时候,使用base标签+相对路径,之后使用框架的阶段再使用绝对路径进行配置。
首先,在title标签下加上base标签,其中的路径写成部署后的工程路径:
1 2 3 <title > 书城首页</title > <base href ="http://localhost:8080/book/" >
下方的使用到图片资源的路径book就相当于映射到了web文件夹。
对应于图中的工程路径,图片以及资源就可以写为:
1 2 <link type ="text/css" rel ="stylesheet" href ="static/css/style.css" > <script type ="text/javascript" src ="static/script/jquery.js" > </script >
处理用户注册操作的servlet中的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package com.geo.web;import com.geo.pojo.User;import com.geo.service.UserService;import com.geo.service.impl.UserServiceImpl;import jakarta.servlet.ServletException;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;public class RegistServlet extends HttpServlet { private UserService userService = new UserServiceImpl (); @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); String password = req.getParameter("password" ); String email = req.getParameter("email" ); String code = req.getParameter("code" ); if ("abcde" .equalsIgnoreCase(code)) { if (userService.existsUsername(username)) { System.out.println("用户名" + username + "已存在" ); req.getRequestDispatcher("/pages/user/regist.html" ).forward(req, resp); } else { userService.registerUser(new User (null , username, password, email)); req.getRequestDispatcher("/pages/user/regist_success.html" ).forward(req, resp); } } else { System.out.println("验证码错误,验证码是" + code); req.getRequestDispatcher("/pages/user/regist.html" ).forward(req, resp); } } }
需求2:用户登录
访问登录页面
填写用户名密码后提交
服务器判断用户是否存在
如果登录失败返回用户名或密码的错误信息
登录成功则提示登录成功
使用LiginServlet处理登录业务,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.geo.web;import com.geo.pojo.User;import com.geo.service.UserService;import com.geo.service.impl.UserServiceImpl;import jakarta.servlet.ServletException;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;public class LoginServlet extends HttpServlet { private UserService userService = new UserServiceImpl (); @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); String password = req.getParameter("password" ); User login = userService.login(new User (null , username, password, null )); if (login == null ) { req.getRequestDispatcher("/pages/user/login.html" ).forward(req, resp); } else { req.getRequestDispatcher("/pages/user/login_success.html" ).forward(req, resp); } } }
JSP 什么是JSP,有什么作用?
JSP的全称是Java Server Page,Java的服务器页面。
JSP的主要作用是代替Servlet程序回传html页面的数据。
因为Servlet程序回传html页面数据是一件非常繁琐的事情,开发成本和维护成本都极高。
如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.geo.servlet;import jakarta.servlet.ServletException;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;public class PrintHtml extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html; charset = utf-8" ); PrintWriter writer = resp.getWriter(); writer.write("<!DOCTYPE html>\r\n" ); writer.write("<html lang=\"en\">\r\n" ); writer.write("<head>\r\n" ); writer.write("<meta charset=\"utf-8\">\r\n" ); writer.write("<title>Title</title>\r\n" ); writer.write("</head>\r\n" ); writer.write("<body>\r\n" ); writer.write("这是html页面数据\r\n" ); writer.write("</body>\r\n" ); writer.write("</html>\r\n" ); } }
JSP的本质是什么?
JSP页面本质上是一个Servlet程序,当我们第一次访问JSP页面的时候,Tomcat服务器会自动把JSP页面翻译成为一个Java源文件,并且将他编译为class字节码程序。
JSP的三种page指令
JSP的page指令可以修改JSP页面中的一些重要属性,或者行为。
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
language:表示JSP翻译后是什么语言的文件,暂时只支持Java。
contentType:表示JSP返回的数据类型是什么,也是源码中response.setContentType()参数值。
pageEncoding:表示当前JSP页面文件本身的字符集。
import:与Java一样进行导包导类。
autoFlush:设置当out输出流缓冲区满了之后,是否自动刷新缓冲区。默认值是true。
buffer:设置out输出流缓冲区的大小,默认是8kb。
errorPage:设置当JSP页面运行时错误,自动跳转的页面。
isErrorPage:设置当前页面是否是错误信息页面,默认是false,如果是true,可以获取异常信息。
session:设置访问当前JSP页面是否会创建HttpSession对象,默认是true。
extends:设置JSP翻译出来的Java类默认继承谁。
JSP中的常用脚本
声明脚本
声明脚本的格式是:<%! 中间声明Java代码 %>。
作用:可以给JSP翻译出来的Java类定义属性和方法,或静态代码块、内部类等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 <%@ page import ="java.util.Map" %> <%@ page import ="java.util.HashMap" %><%-- Created by IntelliJ IDEA. User: 13511 Date: 2024 /1 /23 Time: 16 :58 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> 这是html页面数据 <%--1. 声明类属性--%> <%! private Integer id; private String name; private static Map<String, Object> map; %> <%--2. 声明静态代码块--%> <%! static { map = new HashMap <String, Object>(); map.put("key1" , "value1" ); map.put("key2" , "value2" ); map.put("key3" , "value3" ); } %> <%--3. 声明类方法--%> <%! public int abc () { return 12 ; } %> <%--4. 声明内部类--%> <%! public static class A { private Integer id = 12 ; private String abc = "ab" ; } %> </body> </html>
表达式脚本
表达式脚本的格式是:<%=表达式%>。
表达式脚本的作用是:在JSP页面上输出数据。
1 2 3 4 5 6 7 8 <%--输出整型--%> <%=12 %> <br/> <%--输出浮点型--%> <%=12.12 %> <br/> <%--输出字符串--%> <%="我是字符串" %> <br/> <%--输出对象--%> <%=map%> <br/>
代码脚本
代码脚本的格式是:
<%
Java代码
%>
代码脚本的作用:可以在JSP页面中编写自己需要的功能(Java语句)。
JSP中的三种注释
HTML注释
Java注释
JSP注释
JSP九大内置对象
JSP内置对象是指Tomcat在翻译JSP页面成为Servlet源代码之后,内部提供的九大对象,叫内置对象。
request:请求对象
response:响应对象
pageContext:JSP的上下文对象
session:会话对象
application:ServletContext对象
config:ServletConfig对象
out:JSP输出流对象
page:指向当前JSP的对象
exception:异常对象
JSP四大域对象
pageContext(PageContextImpl类):当前JSP页面范围内有效。
request(HttpServletRequest类):一次请求有效。
session(HttpSession类):一个会话范围有效(打开浏览器访问服务器,直到关闭浏览器)。
application(ServletContext类):整个web工程内都有效(只要web工程不停止,数据都在)。
域对象是可以像Map一样存取数据,四个域对象功能一样,不同的是他们的数据存储范围。
JSP中的out和response.getWriter()输出的区别 response表示响应,我们经常用于设置返回给客户端的内容(输出)。
out也是给客户端输出用的。
1 2 3 4 5 6 7 8 <body> <% out.write("out输出1 <br/>" ); out.write("out输出2 <br/>" ); response.getWriter().write("response输出1 <br/>" ); response.getWriter().write("response输出2 <br/>" ); %> </body>
此时,我们可以看到out是要先执行的,但是结果却相反,如下图:
为什么会导致这样的结果?
当JSP页面中所有的代码执行完毕之后,会进行以下的两个操作:
执行out.flush()操作,会把out缓冲区中所有的数据追加写入到response缓冲区末尾。
会执行response的刷新操作,把全部数据写给客户端。
由于JSP翻译之后,底层源代码都是使用out进行输出,所以一般情况下,我们在JSP页面中也统一使用out进行输出,避免打乱页面的顺序。
out.write(),只适合输出字符串,输出其他类型的数据会出现错误。
out.print(),适合输出任意数据,都不会出问题(都转换为字符串之后再调用out.write()方法输出)。
总结:在任何情况下使用out.print(),都不会出问题。
JSP中的常用标签 JSP静态包含 应用举例:当有非常多的页面时,且上方、下方或者某一部分的内容是完全相同的,比如导航栏、页脚等,如果需要修改的话,所有页面对应位置都要修改,维护起来非常麻烦。所以我们将这一个相同的部分提取出来,单独写成一个JSP文件,然后在需要显示的位置引入即可。
1 2 3 4 5 6 7 <%--main.jsp的内容--%> <body> 头部信息 <br> 主体内容 <br> <%--静态包含--%> <%@include file="footer.jsp" %> </body>
1 2 3 4 <%--footer.jsp的内容--%> <body> 页脚信息 <br> </body>
静态包含的特点:
静态包含不会翻译被包含的JSP页面。
静态包含实际上就是把被包含的JSP页面的代码拷贝到包含的位置进行输出。
JSP动态包含 1 2 3 4 5 <%--动态包含--%> <jsp:include page="footer.jsp" > <%--还可以传递参数--%> <jsp:param name="password" value="root" /> </jsp:include>
动态包含的特点:
动态包含会把包含的JSP页面也翻译成Java代码。
动态包含底层使用代码去调用被包含的JSP页面进行输出。
动态包含还可以传递参数。
JSP标签-转发 就是重定向到另外一个JSP页面。
1 2 <%--请求转发--%> <jsp:forward page="footer.jsp" ></jsp:forward>
什么是Listener监听器
Listener监听器是JaveWeb的三大组件之一,分别是Servlet程序、Filter过滤器、Listener监听器。
Listener是JavaEE的规范,就是接口。
监听器的作用:监听某种事物的变化,然后通过回调函数,反馈给用户或者程序去做一些相应的处理。
ServletContextListener监听器 ServletContextListener可以监听ServletContext对象的创建和销毁。
ServletContext在Web工程启动的时候创建,在工程停止的时候销毁。
监听到创建和销毁之后,都会分别调用ServletContextListener监听器的方法反馈。
对应的两个方法如下:
1 2 3 4 5 6 7 8 public interface ServletContextListener extends EventListener { default void contextInitialized (ServletContextEvent sce) { } default void contextDestroyed (ServletContextEvent sce) { } }
调用方法,首先创建一个实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.geo.listener;import jakarta.servlet.ServletContextEvent;import jakarta.servlet.ServletContextListener;public class MyServletContextListenerImpl implements ServletContextListener { @Override public void contextInitialized (ServletContextEvent sce) { System.out.println("ServletContext对象被创建了" ); } @Override public void contextDestroyed (ServletContextEvent sce) { System.out.println("ServletContext对象被销毁了" ); } }
然后在web.xml中进行配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?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_6_0.xsd" version ="6.0" > <servlet > <servlet-name > PrintHtml</servlet-name > <servlet-class > com.geo.servlet.PrintHtml</servlet-class > </servlet > <servlet-mapping > <servlet-name > PrintHtml</servlet-name > <url-pattern > /printHtml</url-pattern > </servlet-mapping > <listener > <listener-class > com.geo.listener.MyServletContextListenerImpl</listener-class > </listener > </web-app >
接着在启动工程以及终止工程的时候会看到在控制台有相应的打印文字。
EL表达式和JSTL标签库 EL表达式 什么是EL表达式,有什么作用? EL表达式的全称是Expression Language,是表达式语言。
作用:主要是替代JSP页面中的表达式脚本在JSP页面中的数据输出。
EL表达式的格式是:${表达式}。
EL表达式在输出null值的时候是空串,JSP表达式脚本输出的是null。
对比:
1 2 3 4 5 6 7 8 9 10 <body> 使用表达式脚本设置数据: <% request.setAttribute("key" , "value" ); %><br> 使用表达式脚本读取数据: <%=request.getAttribute("key" )%><br> 使用EL表达式读取数据:${key} </body>
在页面上的效果:
EL表达式搜索四个域的数据的顺序 当四个域中都有相同的key的数据的时候,和JSP的域中数据读取顺序一样,按照四个域的从小到大的顺序读取,搜索到就输出。
EL表达式输出Bean的普通属性、数组属性、List集合属性、Map集合属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <body> <% Person person = new Person (); person.setName(12 ); person.setPhones(new String []{"123456" , "7777777" }); List<String> list = new ArrayList (); list.add("北京" ); list.add("上海" ); list.add("张家口" ); person.setCities(list); Map<String, Object> map = new HashMap (); map.put("哈哈" , 123 ); map.put("lolo" , 555 ); person.setMap(map); pageContext.setAttribute("Person" , person); %> ${Person}<br> </body>
EL表达式-运算
关系运算
逻辑运算
算数运算
empty运算
empty运算可以用来判断一个数据是否为空,如果为空,输出true,反之,输出false。
值为null值、空串、Object数组长度为0、list集合元素个数为0、map集合元素个数为0,判定为空。
三元运算
表达式1?表达式2:表达式3
如果表达式1为真,则返回表达式2;反之,返回表达式3。
.运算符和[]运算符
.运算可以输出Bean对象中某个属性的值。
[]运算可以输出有序集合中某个元素的值,并且还可以输出map集合中key里含有特殊字符key的值。写法:[“a.a.a”]。
EL表达式中11个隐含的对象 EL表达式中11个隐含对象,是EL表达式自己定义的,可以直接使用。
变量
类型
作用
pageContext
pageContextImpl
它可以获取JSP九大内置对象
pageScope
Map<String,Object>
它可以获取pageContext域中的数据
requestScope
Map<String,Object>
它可以获取Request域中的数据
sessionScope
Map<String,Object>
它可以获取Session域中的数据
applicationScope
Map<String,Object>
它可以获取ServletContext域中的数据
param
Map<String,String>
它可以获取请求参数的值
paramValues
Map<String,String[]>
它可以获取多个请求参数的值
header
Map<String,String>
它可以获取请求头的信息
headerValues
Map<String,String[]>
它可以获取多个请求头的信息
cookie
Map<String,Cookie>
它可以获取当前请求的Cookie信息
initParam
Map<String,String>
它可以获取在web.xml中配置的< context-param >上下文参数
EL获取四个特定域中的属性
pageScope:pageContext域
requestScope:Reques域
sessionScope:Session域
applicationScope:ServletContext域
pageContext对象的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <body> <% request.getScheme(); request.getServerName(); request.getServerPort(); request.getContextPath(); request.getMethod(); request.getRemoteHost(); session.getId(); %> 1. 协议:${pageContext.request.scheme}<br>2. 服务器ip:${pageContext.request.serverName}<br>3. 服务器端口:${pageContext.request.serverPort}<br>4. 获取工程路径:${pageContext.request.contextPath}<br>5. 获取请求方法:${pageContext.request.method}<br>6. 获取客户端ip地址:${pageContext.request.remoteHost}<br>7. 获取会话的id编号:${pageContext.session.id}</body>
JSTL标签库 JSTL标签库,全称是:JSP Standard Tag Library,JSP标准标签库,是一个不断完善的开放源代码的JSP标签库。EL表达式主要是为了替换JSP中的表达式脚本,而标签库是为了替换代码脚本。这样使得整个JSP页面更加简洁。
JSTL由五种不同的标签库组成。
JSTL标签库的使用步骤
先引入两个jar包:taglibs-standard-impl-1.2.5.jar、taglibs-standard-spec-1.2.5.jar
使用taglib指令引入标签库,<%@ taglib prefix=”c” uri=”http://java.sun.com/jsp/jstl/core “ %>
set标签 作用:set标签可以往域中保存数据,scope属性设置保存到哪个域。
page表示PageContext域
request表示Request域
session表示Session域
application表示ServletContext域
var属性设置key是多少
value属性设置值是多少
1 2 3 保存之前:${requestScope.abc}<br> <c:set scope="request" var ="abc" value="123" ></c:set> 保存之后:${requestScope.abc}<br>
在运行代码之后报500服务器错误,因为本人使用的Tomcat版本太高(10.0),jar包不匹配导致的,所以引入两个新的jar包jakarta.servlet.jsp.jstl-2.0.0.jar、jakarta.servlet.jsp.jstl-api-2.0.0.jar
问题成功解决。
if标签 if标签用于做if判断。
test属性表示判断的条件(使用EL表达式输出)
1 2 3 <c:if test="${12==12}" > <h1>if 语句判断成功了!</h1> </c:if >
多路判断标签 作用:多路判断,与switch,case,default相似。
choose标签开始选择判断,when标签表示每一种判断情况,tset属性表示当前这种判断情况的值,otherwise标签表示剩余的情况。
注意点:不能在标签之间使用html注释,要使用jsp注释;when标签的父标签一定要是choose标签。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <% request.setAttribute("height" , 128 ); %> <c:choose> <c:when test="${height>190}" > <h2>高个子</h2> </c:when> <c:when test="${height>170}" > <h2>正常</h2> </c:when> <c:when test="${height>130}" > <h2>很低</h2> </c:when> <c:otherwise> <h2>身高低于130 </h2> </c:otherwise> </c:choose>
forEach标签 作用:遍历输出使用。
begin属性设置开始的索引,end属性设置结束的索引,var属性表示循环的变量,step是步长,默认为1(即i++)。
forEach遍历1-10
1 2 3 <c:forEach begin="1" end="100" var ="i" > ${i} </c:forEach>
forEach遍历对象数组
1 2 3 4 5 6 <% request.setAttribute("arr" , new String []{"123" , "456" , "789" }); %> <c:forEach items="${requestScope.arr}" var ="i" > ${i} </c:forEach>
forEach遍历Map、List集合
1 2 3 4 5 6 7 8 9 10 11 12 <% Map<String, Object> map = new HashMap <>(); map.put("key1" , "value1" ); map.put("key2" , "value2" ); map.put("key3" , "value3" ); request.setAttribute("arr" , map); %> <c:forEach items="${requestScope.arr}" var ="entry" > ${entry.key}=${entry.value} </c:forEach>
下方这些方法可以去掉get之后直接在EL表达式中使用。
接口方法
用法
getCurrent()
获取当前遍历到的数据
getIndex()
获取遍历的索引
getCount()
获取遍历的个数
isFirst()
当前遍历的数据是否是第一条
isLast()
当前遍历的数据是否是最后一条
getBegin()
获取begin的属性值
getEnd()
获取end的属性值
getStep()
获取step属性值
文件的上传和下载 文件的上传介绍
要有一个form标签,method=post请求。
form标签的encType属性值必须为multipart/form-data值。
在form标签中使用input type=file添加上传的文件。
编写服务器代码(Servlet)接收,处理上传的数据。
具体实现步骤:
首先创建一个JSP页面,构建一个表单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <%-- Created by IntelliJ IDEA. User: 13511 Date: 2024 /1 /25 Time: 10 :49 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <form action="http://localhost:8080/_09_EL_JSTL/uploadServlet" method="post" enctype="multipart/form-data" > 用户名:<input type="text" name="username" ><br> 头像:<input type="file" name="photo" ><br> <input type="submit" value="上传" > </form> </body> </html>
然后创建一个Servlet程序,先打印一句话,测试是否连通成功。同时在web.xml中配置好servlet的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.geo.servlet;import jakarta.servlet.ServletException;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;public class uploadServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("文件上传过来了" ); } }
encType = multipart/form-data表示提交的数据,以多段(每一个表单项就是一个数据段)的形式进行拼接,然后以二进制流的形式发送给服务器。
文件上传、HTTP协议的说明
在服务器端以流的方式接收传输过来的数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.geo.servlet;import jakarta.servlet.ServletException;import jakarta.servlet.ServletInputStream;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import jdk.jshell.spi.ExecutionControl;import java.io.File;import java.io.FileInputStream;import java.io.IOException;public class uploadServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("文件上传过来了" + req.getInputStream()); ServletInputStream inputStream = req.getInputStream(); byte [] buffer = new byte [1024 ]; int read = inputStream.read(buffer); System.out.println(new String (buffer, 0 , read)); } @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("文件上传过来了" + req.getParameter("username" )); } }
commons-fileupload.jar 常用API介绍说明 commons-fileupload.jar需要依赖commons-io.jar这个包,所以两个都需要引入。
导入的包中,常用的类:
ServletFileUpload类,用于解析上传中的数据。
FileItem类:表示每一个表单项。
判断当前上传的数据格式是否是多段的形式:
boolean ServletFileUpload.isMultipartContent(HttpServletRequest request);
解析上传的数据:
public List< FileItem > parseRequest(HttpServletRequest request)
判断这个表单项,是普通的表单项,还是上传的文件类型。
true表示普通的表单项,false表示上传的文件类型:
boolean FileItem.isFormField()
获取表单项的name属性值:
String FileItem.getFieldName()
获取表单项的值:
String FileItem.getString()
获取上传的文件名:
String FileItem.getName()
将上传的文件写到参数file所指向的磁盘目录:
void FileItem.write(file)
文件上传功能的实现 表单填写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <title>JSP - Hello World</title> </head> <body> <h1><%= "Hello World!" %> </h1> <br/> <a href="hello-servlet" >Hello Servlet</a> 一定要设置enctype的属性,否则无法上传文件 <form method="post" action="hello-servlet" enctype="multipart/form-data" > <input type="text" name="name" > <input type="file" name="file" > <input type="submit" value="提交" > </form> </body> </html>
上传servlet的设计:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 package org.example.demo;import com.sun.istack.internal.Nullable;import org.apache.commons.fileupload.FileItem;import org.apache.commons.fileupload.FileItemFactory;import org.apache.commons.fileupload.FileUploadException;import org.apache.commons.fileupload.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;import java.io.*;import java.util.List;import javax.servlet.ServletException;import javax.servlet.http.*;import javax.servlet.annotation.*;有webservelt这个注解就不用特意去web.xml中配置servlet的信息了 @WebServlet(name = "helloServlet", value = "/hello-servlet") public class HelloServlet extends HttpServlet { @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (ServletFileUpload.isMultipartContent(req)) { FileItemFactory fileItemFactory = new DiskFileItemFactory (); ServletFileUpload servletFileUpload = new ServletFileUpload (fileItemFactory); req.setCharacterEncoding("utf-8" ); try { List<FileItem> list = servletFileUpload.parseRequest(req); list.forEach(item -> { if (item.isFormField()) { System.out.println("表单项的name属性值是:" + item.getFieldName()); try { System.out.println("上传的表单项名是:" + item.getString("GBK" )); } catch (UnsupportedEncodingException e) { throw new RuntimeException (e); } } else { System.out.println("表单项的name属性值是:" + item.getFieldName()); servletFileUpload.setHeaderEncoding("GBK" ); System.out.println("上传的文件名是:" + item.getName()); try { item.write(new File ("d:\\" + item.getName())); } catch (Exception e) { throw new RuntimeException (e); } } }); } catch (FileUploadException e) { ... } } } }
文件下载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package org.example.demo;import org.apache.commons.io.IOUtils;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.URLEncoder;@WebServlet(name = "download", value = "/downloadServlet") public class DownloadServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String downloadFilename = "xiaoji.png" ; ServletContext servletContext = getServletContext(); String mimeType = servletContext.getMimeType("/file/" + downloadFilename); System.out.println("下载的文件的类型:" + mimeType); resp.setContentType(mimeType); resp.setHeader("Content-Disposition" , "attachment; filename=\"" + URLEncoder.encode(downloadFilename + "\"" , "utf-8" )); InputStream inputStream = servletContext.getResourceAsStream("/file/" + downloadFilename); byte [] bytes = new byte [1024 ]; OutputStream outputStream = resp.getOutputStream(); IOUtils.copy(inputStream, outputStream); } }
书城项目第三阶段 页面jsp动态化
在html页面添加page指令。
修改文件后缀名为:.jsp。
使用IDEA快捷键(ctrl+shift+r)查找替换,将.html全部替换为.jsp。
抽取jsp页面公共部分 将所有页面内容相同的代码块抽取出来,放到一个单独的jsp文件中,然后在对应位置使用静态引入该jsp页面。
动态的BASE标签值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <%-- Created by IntelliJ IDEA. User: YuHong Date: 2024 /4 /26 Time: 下午4 :19 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <% String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/" ; %> <% System.out.println(basePath); %> <!-- 写base标签,永远固定相对路径跳转的结果--> <base href="<%=basePath%>" > <link type="text/css" rel="stylesheet" href="static/css/style.css" > <script type="text/javascript" src="static/script/jquery.js" ></script>
表单提交失败的错误回显 先在处理业务的servlet中,将错误信息存入request域中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package com.geo.web;import com.geo.pojo.User;import com.geo.service.UserService;import com.geo.service.impl.UserServiceImpl;import jakarta.servlet.ServletException;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;public class LoginServlet extends HttpServlet { private UserService userService = new UserServiceImpl (); @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); String password = req.getParameter("password" ); User login = userService.login(new User (null , username, password, null )); if (login == null ) { req.setAttribute("msg" , "用户名或密码错误!" ); req.setAttribute("username" , username); req.getRequestDispatcher("/pages/user/login.jsp" ).forward(req, resp); } else { req.getRequestDispatcher("/pages/user/login_success.jsp" ).forward(req, resp); } } }
然后在对应的业务提交表单页面插入表达式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" > <title>尚硅谷会员登录页面</title> <%-- 静态包含,base标签,css样式,jquery--%> <%@include file="/pages/common/head.jsp" %> </head> <body> <div id="login_header" > <img class="logo_img" alt="" src="static/img/logo.gif" > </div> <div class="login_banner" > <div id="l_content" > <span class="login_word" >欢迎登录</span> </div> <div id="content" > <div class="login_form" > <div class="login_box" > <div class="tit" > <h1>尚硅谷会员</h1> <a href="regist.jsp" >立即注册</a> </div> <div class="msg_cont" > <b></b> <span class="errorMsg" ><%=request.getAttribute("msg" ) == null ? "请输入用户名和密码" : request.getAttribute("msg" )%></span> </div> <div class="form" > <form action="userServlet" , method="post" > <input type="hidden" name="action" value="login" /> <label>用户名称:</label> <input class="itxt" type="text" placeholder="请输入用户名" autocomplete="off" tabindex="1" name="username" <%-- 此value属性用于用户登录失败之后不用重新填写账号--%> value="<%=request.getAttribute(" username")==null?" ":request.getAttribute(" username")%>" /> <br/> <br/> <label>用户密码:</label> <input class="itxt" type="password" placeholder="请输入密码" autocomplete="off" tabindex="1" name="password" /> <br/> <br/> <input type="submit" value="登录" id="sub_btn" /> </form> </div> </div> </div> </div> </div> <%@include file="/pages/common/foot.jsp" %> </body> </html>
代码优化一:合并LoginServlet和RegistServlet为UserServlet 首先在表单提交处中多添加一个hidden属性的input标签:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <form action="userServlet" , method="post" > <input type="hidden" name="action" value="login" /> <label>用户名称:</label> <input class="itxt" type="text" placeholder="请输入用户名" autocomplete="off" tabindex="1" name="username" <%-- 此value属性用于用户登录失败之后不用重新填写账号--%> value="<%=request.getAttribute(" username")==null?" ":request.getAttribute(" username")%>" /> <br/> <br/> <label>用户密码:</label> <input class="itxt" type="password" placeholder="请输入密码" autocomplete="off" tabindex="1" name="password" /> <br/> <br/> <input type="submit" value="登录" id="sub_btn" /> </form>
然后在servlet中进行判断是登录业务还是注册业务,再分别进行处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 package com.geo.web;import com.geo.pojo.User;import com.geo.service.UserService;import com.geo.service.impl.UserServiceImpl;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet(name = "userServlet", value = "/userServlet") public class UserServlet extends HttpServlet { UserService userService = new UserServiceImpl (); @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action = req.getParameter("action" ); System.out.println(action); if (action.equals("login" )) { login(req, resp); } else if (action.equals("regist" )) { regist(req, resp); } } public void login (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("处理登录的需求" ); String username = req.getParameter("username" ); String password = req.getParameter("password" ); User login = userService.login(new User (null , username, password, null )); if (login == null ) { req.setAttribute("msg" , "用户名或密码错误!" ); req.setAttribute("username" , username); req.getRequestDispatcher("/pages/user/login.jsp" ).forward(req, resp); } else { req.getRequestDispatcher("/pages/user/login_success.jsp" ).forward(req, resp); } } public void regist (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("处理注册的需求" ); String username = req.getParameter("username" ); String password = req.getParameter("password" ); String email = req.getParameter("email" ); String code = req.getParameter("code" ); if ("abcde" .equalsIgnoreCase(code)) { if (userService.existsUsername(username)) { req.setAttribute("msg" , "用户名已存在!" ); req.setAttribute("email" , email); System.out.println("用户名" + username + "已存在" ); req.getRequestDispatcher("/pages/user/regist.jsp" ).forward(req, resp); } else { userService.registerUser(new User (null , username, password, email)); req.getRequestDispatcher("/pages/user/regist_success.jsp" ).forward(req, resp); } } else { req.setAttribute("msg" , "验证码错误!" ); req.setAttribute("username" , username); req.setAttribute("email" , email); System.out.println("验证码错误,验证码是" + code); req.getRequestDispatcher("/pages/user/regist.jsp" ).forward(req, resp); } } }
代码优化二:使用反射优化业务逻辑代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 package com.geo.web;import com.geo.pojo.User;import com.geo.service.UserService;import com.geo.service.impl.UserServiceImpl;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;import java.lang.reflect.Method;@WebServlet(name = "userServlet", value = "/userServlet") public class UserServlet extends HttpServlet { UserService userService = new UserServiceImpl (); @Override protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action = req.getParameter("action" ); try { Method method = this .getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class); method.invoke(this , req, resp); } catch (Exception e) { e.printStackTrace(); } } public void login (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("处理登录的需求" ); String username = req.getParameter("username" ); String password = req.getParameter("password" ); User login = userService.login(new User (null , username, password, null )); if (login == null ) { req.setAttribute("msg" , "用户名或密码错误!" ); req.setAttribute("username" , username); req.getRequestDispatcher("/pages/user/login.jsp" ).forward(req, resp); } else { req.getRequestDispatcher("/pages/user/login_success.jsp" ).forward(req, resp); } } public void regist (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("处理注册的需求" ); String username = req.getParameter("username" ); String password = req.getParameter("password" ); String email = req.getParameter("email" ); String code = req.getParameter("code" ); if ("abcde" .equalsIgnoreCase(code)) { if (userService.existsUsername(username)) { req.setAttribute("msg" , "用户名已存在!" ); req.setAttribute("email" , email); System.out.println("用户名" + username + "已存在" ); req.getRequestDispatcher("/pages/user/regist.jsp" ).forward(req, resp); } else { userService.registerUser(new User (null , username, password, email)); req.getRequestDispatcher("/pages/user/regist_success.jsp" ).forward(req, resp); } } else { req.setAttribute("msg" , "验证码错误!" ); req.setAttribute("username" , username); req.setAttribute("email" , email); System.out.println("验证码错误,验证码是" + code); req.getRequestDispatcher("/pages/user/regist.jsp" ).forward(req, resp); } } }
代码优化三:抽取模块业务处理doPost请求,继承自BaseServlet BaseServlet:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.geo.web;import jakarta.servlet.ServletException;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;import java.lang.reflect.Method;public abstract class BaseServlet extends HttpServlet { protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action = req.getParameter("action" ); try { Method method = this .getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class); method.invoke(this , req, resp); } catch (Exception e) { e.printStackTrace(); } } }
其他例如UserServlet程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 package com.geo.web;import com.geo.pojo.User;import com.geo.service.UserService;import com.geo.service.impl.UserServiceImpl;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;import java.lang.reflect.Method;@WebServlet(name = "userServlet", value = "/userServlet") public class UserServlet extends BaseServlet { UserService userService = new UserServiceImpl (); public void login (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("处理登录的需求" ); String username = req.getParameter("username" ); String password = req.getParameter("password" ); User login = userService.login(new User (null , username, password, null )); if (login == null ) { req.setAttribute("msg" , "用户名或密码错误!" ); req.setAttribute("username" , username); req.getRequestDispatcher("/pages/user/login.jsp" ).forward(req, resp); } else { req.getRequestDispatcher("/pages/user/login_success.jsp" ).forward(req, resp); } } public void regist (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("处理注册的需求" ); String username = req.getParameter("username" ); String password = req.getParameter("password" ); String email = req.getParameter("email" ); String code = req.getParameter("code" ); if ("abcde" .equalsIgnoreCase(code)) { if (userService.existsUsername(username)) { req.setAttribute("msg" , "用户名已存在!" ); req.setAttribute("email" , email); System.out.println("用户名" + username + "已存在" ); req.getRequestDispatcher("/pages/user/regist.jsp" ).forward(req, resp); } else { userService.registerUser(new User (null , username, password, email)); req.getRequestDispatcher("/pages/user/regist_success.jsp" ).forward(req, resp); } } else { req.setAttribute("msg" , "验证码错误!" ); req.setAttribute("username" , username); req.setAttribute("email" , email); System.out.println("验证码错误,验证码是" + code); req.getRequestDispatcher("/pages/user/regist.jsp" ).forward(req, resp); } } }
数据的封装和抽取以及BeanUtils的使用(由于使用JakartaEE,无法实现此步骤) BeanUtils工具类可以一次性把所有请求的参数注入到JavaBean中。
首先去Maven仓库中下载commons-beanutils.jar和commons-logging.jar,然后导入到lib文件夹中。
书城项目第四阶段:使用EL表达式修改回显值 修改login.jsp中相应的部分:
1 2 3 4 <span class="errorMsg" > <%--<%=request.getAttribute("msg" ) == null ? "请输入用户名和密码" : request.getAttribute("msg" )%>--%> ${empty requestScope.msg ? "请输入用户名和密码" : requestScope.msg} </span>
书城项目第五阶段 MVC MVC全称:Model 模型、View 视图、Controller 控制器。
View视图:只负责数据和界面的显示,不接受任何与显示数据无关的代码,便于程序员和美工的分工合作。
Controller控制器:只负责接受请求,调用业务层的代码处理请求,然后派发页面,是一个“调度者“角色。
Model模型:将与业务逻辑相关的数据封装为具体的JavaBean类,其中不掺杂任何与数据处理相关的代码。
MVC的理念是将软件代码拆分成为组件,单独开发,组合使用(目的还是为了解耦)。
图书模块 编写图书模块的数据库表 1 2 3 4 5 6 7 8 9 create table t_book( `id` int PRIMARY key auto_increment, `name` VARCHAR (100 ), `price` DECIMAL (11 ,2 ), `author` VARCHAR (100 ), `salse` INT , `stock` INT , `img_path` VARCHAR (200 ) )
编写图书模块的JavaBean 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 package com.geo.pojo;import java.math.BigDecimal;public class Book { private Integer id; private String name; private String author; private BigDecimal price; private Integer sales; private Integer stock; private String imgPath = "static/img/default.jpg" ; public Book () { } public Book (Integer id, String name, String author, BigDecimal price, Integer sales, Integer stock, String imgPath) { this .id = id; this .name = name; this .author = author; this .price = price; this .sales = sales; this .stock = stock; if (imgPath != null && !imgPath.equals("" )) { this .imgPath = imgPath; } } public Integer getId () { return id; } public void setId (Integer id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public String getAuthor () { return author; } public void setAuthor (String author) { this .author = author; } public BigDecimal getPrice () { return price; } public void setPrice (BigDecimal price) { this .price = price; } public Integer getSales () { return sales; } public void setSales (Integer sales) { this .sales = sales; } public Integer getStock () { return stock; } public void setStock (Integer stock) { this .stock = stock; } public String getImgPath () { return imgPath; } public void setImgPath (String imgPath) { this .imgPath = imgPath; } @Override public String toString () { return "Book{" + "id=" + id + ", name='" + name + '\'' + ", author='" + author + '\'' + ", price=" + price + ", sales=" + sales + ", stock=" + stock + ", imgPath='" + imgPath + '\'' + '}' ; } }
编写图书模块的DAO和测试 DAO:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.geo.dao;import com.geo.pojo.Book;import java.util.List;public interface BookDao { public int addBook (Book book) ; public int deleteBookById (Integer id) ; public int updateBook (Book book) ; public Book queryBookById (Integer id) ; public List<Book> queryAllBook () ; }
DAO实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package com.geo.dao.impl;import com.geo.dao.BookDao;import com.geo.pojo.Book;import java.util.List;public class BookDaoImpl extends BaseDao implements BookDao { @Override public int addBook (Book book) { String sql = "insert into t_book(`name`,`author`,`price`,`sales`,`stock`,`image_path`) values(?,?,?,?,?,?)" ; return update(sql, book.getName(), book.getAuthor(), book.getPrice(), book.getSales(), book.getStock(), book.getImgPath()); } @Override public int deleteBookById (Integer id) { String sql = "delete from t_book where id = ?" ; return update(sql, id); } @Override public int updateBook (Book book) { String sql = "update t_book set `name`=?,`author`=?,`price`=?,`sales`=?,`stock`=?,`image_path`=?" ; return update(sql, book.getName(), book.getAuthor(), book.getPrice(), book.getSales(), book.getStock(), book.getImgPath()); } @Override public Book queryBookById (Integer id) { String sql = "select `name`,`author`,`price`,`sales`,`stock`,`image_path` imgPath from t_book where id = ?" ; return queryForOne(Book.class, sql, id); } @Override public List<Book> queryAllBook () { String sql = "select `name`,`author`,`price`,`sales`,`stock`,`image_path` imgPath from t_book" ; return queryForList(Book.class, sql); } }
使用IDEA生成测试类 首先选中DAO文件,然后按下CTRL+SHIFT+T,点击创建新测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package com.geo.test;import com.geo.dao.BookDao;import com.geo.dao.impl.BookDaoImpl;import com.geo.pojo.Book;import org.junit.Test;import java.math.BigDecimal;import static org.junit.Assert.*;public class BookDaoTest { private BookDao bookDao = new BookDaoImpl (); @Test public void addBook () { bookDao.addBook(new Book (null , "海底两万里" , "李晓杰" , new BigDecimal (23 ), 400 , 20 , null )); } @Test public void deleteBookById () { bookDao.deleteBookById(1 ); } @Test public void updateBook () { bookDao.updateBook(new Book (2 , "海底两万里" , "李晓杰" , new BigDecimal (23 ), 800 , 20 , null ), 2 ); } @Test public void queryBookById () { System.out.println(bookDao.queryBookById(3 )); } @Test public void queryAllBook () { System.out.println(bookDao.queryAllBook()); } }
编写图书模块的Service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.geo.service.impl;import com.geo.dao.BookDao;import com.geo.dao.impl.BookDaoImpl;import com.geo.pojo.Book;import com.geo.service.BookService;import java.util.List;public class BookServiceImpl implements BookService { private BookDao bookDao = new BookDaoImpl (); @Override public void addBook (Book book) { bookDao.addBook(book); } @Override public void deleteBook (Integer id) { bookDao.deleteBookById(id); } @Override public void updateBook (Book book, Integer id) { bookDao.updateBook(book, id); } @Override public Book queryBook (Integer id) { return bookDao.queryBookById(id); } @Override public List<Book> queryBooks () { return bookDao.queryAllBook(); } }
图书列表功能的实现 因为要使用到JSTL,所以需要导包jakarta.servlet.jsp.jstl-2.0.0.jar、jakarta.servlet.jsp.jstl-api-2.0.0.jar(Tomcat10.0)
BookServlet:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package com.geo.web;import com.geo.pojo.Book;import com.geo.service.BookService;import com.geo.service.impl.BookServiceImpl;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.List;@WebServlet(name = "bookServlet", value = "/manager/bookServlet") public class BookServlet extends BaseServlet { private BookService bookService = new BookServiceImpl (); protected void add (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } protected void delete (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } protected void update (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } protected void list (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { List<Book> books = bookService.queryBooks(); req.setAttribute("books" , books); req.getRequestDispatcher("/pages/manager/book_manager.jsp" ).forward(req, resp); } }
因为将图书的数据存在了request域中,所以使用JSTL循环遍历,将图书的信息循环显示在页面上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" > <title>图书管理</title> <%-- 静态包含,base标签,css样式,jquery--%> <%@include file="/pages/common/head.jsp" %> </head> <body> <div id="header" > <img class="logo_img" alt="" src="../../static/img/logo.gif" > <span class="wel_word" >图书管理系统</span> <%-- 静态包含manager--%> <%@include file="/pages/common/manager_menu.jsp" %> </div> <div id="main" > <table> <tr> <td>名称</td> <td>价格</td> <td>作者</td> <td>销量</td> <td>库存</td> <td colspan="2" >操作</td> </tr> <c:forEach items="${requestScope.books}" var ="book" > <tr> <td>${book.name}</td> <td>${book.price}</td> <td>${book.author}</td> <td>${book.sales}</td> <td>${book.stock}</td> <td colspan="2" >操作</td> </tr> </c:forEach> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td><a href="book_edit.jsp" >添加图书</a></td> </tr> </table> </div> <%@include file="/pages/common/foot.jsp" %> </body> </html>
然后在点击图书管理跳转的标签的链接进行改动:
1 2 3 4 5 6 7 8 9 10 11 12 13 <%-- Created by IntelliJ IDEA. User: YuHong Date: 2024 /4 /26 Time: 下午4 :26 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <div> <a href="manager/bookServlet?action=list" >图书管理</a> <a href="/pages/manager/order_manager.jsp" >订单管理</a> <a href=../index.jsp">返回商城</a> </div>
action后的参数意味着跳转过去之后调用list方法,但因为点击a标签是get请求,所以需要在baseServlet中添加一个get操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.geo.web;import jakarta.servlet.ServletException;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;import java.lang.reflect.Method;public abstract class BaseServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } protected void doPost (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action = req.getParameter("action" ); try { Method method = this .getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class); method.invoke(this , req, resp); } catch (Exception e) { e.printStackTrace(); } } }
只需要在doget方法中调用dopost就可以了。
所有功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 package com.geo.web;import com.geo.pojo.Book;import com.geo.service.BookService;import com.geo.service.impl.BookServiceImpl;import com.oracle.wls.shaded.org.apache.bcel.generic.SWAP;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import org.apache.commons.beanutils.BeanUtils;import java.io.IOException;import java.lang.reflect.InvocationTargetException;import java.math.BigDecimal;import java.util.List;@WebServlet(name = "bookServlet", value = "/manager/bookServlet") public class BookServlet extends BaseServlet { private BookService bookService = new BookServiceImpl (); protected void add (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, InvocationTargetException, IllegalAccessException { String name = req.getParameter("name" ); String author = req.getParameter("author" ); BigDecimal price = new BigDecimal (req.getParameter("price" )); Integer sales = Integer.valueOf(req.getParameter("sales" )); Integer stock = Integer.valueOf(req.getParameter("stock" )); Book book = new Book (null , name, author, price, sales, stock, null ); bookService.addBook(book); resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=list" ); } protected void delete (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String id = req.getParameter("id" ); bookService.deleteBook(Integer.valueOf(id)); resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=list" ); } protected void update (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String id = req.getParameter("id" ); String name = req.getParameter("name" ); String author = req.getParameter("author" ); BigDecimal price = new BigDecimal (req.getParameter("price" )); Integer sales = Integer.valueOf(req.getParameter("sales" )); Integer stock = Integer.valueOf(req.getParameter("stock" )); Book book = new Book (null , name, author, price, sales, stock, null ); bookService.updateBook(book, Integer.valueOf(id)); resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=list" ); } protected void list (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { List<Book> books = bookService.queryBooks(); req.setAttribute("books" , books); req.getRequestDispatcher("/pages/manager/book_manager.jsp" ).forward(req, resp); } protected void getBook (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int id = Integer.parseInt(req.getParameter("id" )); Book book = bookService.queryBook(id); req.setAttribute("book" , book); req.getRequestDispatcher("/pages/manager/book_edit.jsp" ).forward(req, resp); } }
相应jsp页面修改:
bookManager.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" > <title>图书管理</title> <%-- 静态包含,base标签,css样式,jquery--%> <%@include file="/pages/common/head.jsp" %> <script> $(function () { $("a.deleteBook" ).click(function () { return confirm("你确定要删除【" + $(this ).parent().parent().find("td:first" ).text() + "】吗?" ); }) }); </script> </head> <body> <div id="header" > <img class="logo_img" alt="" src="../../static/img/logo.gif" > <span class="wel_word" >图书管理系统</span> <%-- 静态包含manager--%> <%@include file="/pages/common/manager_menu.jsp" %> </div> <div id="main" > <table> <tr> <td>名称</td> <td>价格</td> <td>作者</td> <td>销量</td> <td>库存</td> <td colspan="2" >操作</td> </tr> <c:forEach items="${requestScope.books}" var ="book" > <tr> <td>${book.name}</td> <td>${book.price}</td> <td>${book.author}</td> <td>${book.sales}</td> <td>${book.stock}</td> 跳转链接附带上图书的参数,方便于更新或删除 <td><a href="manager/bookServlet?action=getBook&id=${book.id}&method=update" >修改</a></td> <td><a class="deleteBook" href="manager/bookServlet?action=delete&id=${book.id}&method=add" >删除</a> </td> </tr> </c:forEach> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td><a href="pages/manager/book_edit.jsp" >添加图书</a></td> </tr> </table> </div> <%@include file="/pages/common/foot.jsp" %> </body> </html>
book_edit.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" > <title>编辑图书</title> <%-- 静态包含,base标签,css样式,jquery--%> <%@include file="/pages/common/head.jsp" %> <style type="text/css" > h1 { text-align: center; margin-top: 200px; } h1 a { color: red; } input { text-align: center; } </style> </head> <body> <div id="header" > <img class="logo_img" alt="" src="../../static/img/logo.gif" > <span class="wel_word" >编辑图书</span> <%-- 静态包含manager--%> <%@include file="/pages/common/manager_menu.jsp" %> </div> <div id="main" > <form action="manager/bookServlet" method="get" > 隐藏域的值通过传参的参数进行决定调用servlet中的哪个方法 <input type="hidden" name="action" value="${param.method}" > 这个隐藏域用于传递id,方便修改图书时通过id查找到对应的图书 <input type="hidden" name="id" value="${param.id}" > <table> <tr> <td>名称</td> <td>价格</td> <td>作者</td> <td>销量</td> <td>库存</td> <td colspan="2" >操作</td> </tr> <tr> <td><input name="name" type="text" value="${requestScope.book.name}" /></td> <td><input name="price" type="text" value="${requestScope.book.price}" /></td> <td><input name="author" type="text" value="${requestScope.book.author}" /></td> <td><input name="sales" type="text" value="${requestScope.book.sales}" /></td> <td><input name="stock" type="text" value="${requestScope.book.stock}" /></td> <td><input type="submit" value="提交" /></td> </tr> </table> </form> </div> <%@include file="/pages/common/foot.jsp" %> </body> </html>
图书的分页处理 整体概述:
先创建分页模型Page对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package com.geo.pojo;import java.util.List;public class Page <T> { public static final Integer PAGE_SIZE = 4 ; private Integer pageNo; private Integer pageSize = PAGE_SIZE; private Integer pageTotal; private Integer pageTotalCount; private List<T> items; public Integer getPageNo () { return pageNo; } public void setPageNo (Integer pageNo) { this .pageNo = pageNo; } public Integer getPageTotal () { return pageTotal; } public void setPageTotal (Integer pageTotal) { this .pageTotal = pageTotal; } public List<T> getItems () { return items; } public void setItems (List<T> items) { this .items = items; } public Integer getPageTotalCount () { return pageTotalCount; } public void setPageTotalCount (Integer pageTotalCount) { this .pageTotalCount = pageTotalCount; } @Override public String toString () { return "Page{" + "pageNo=" + pageNo + ", pageSize=" + pageSize + ", pageTotal=" + pageTotal + ", pageTotalCount=" + pageTotalCount + ", items=" + items + '}' ; } }
然后在BookServiceImpl中添加实现方法(BookService也要写对应方法体)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Override public Page<Book> page (int pageNo, int pageSize) { Page<Book> page = new Page <Book>(); Integer pageTotalCount = bookDao.queryForPageTotalCount(); page.setPageTotalCount(pageTotalCount); int pageTotal = pageTotalCount / pageSize; if (pageTotalCount % pageSize > 0 ) { pageTotal += 1 ; } page.setPageTotal(pageTotal); if (pageNo < 1 ) { pageNo = 1 ; } if (pageNo > pageTotal) { pageNo = pageTotal; } page.setPageNo(pageNo); int begin = (page.getPageNo() - 1 ) * pageSize; List<Book> items = bookDao.queryForPageItems(begin, pageSize); page.setItems(items); return page; }
增加分页条:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" > <title>图书管理</title> <%-- 静态包含,base标签,css样式,jquery--%> <%@include file="/pages/common/head.jsp" %> <script> $(function () { $("a.deleteBook" ).click(function () { return confirm("你确定要删除【" + $(this ).parent().parent().find("td:first" ).text() + "】吗?" ); }) }); </script> </head> <body> <div id="header" > <img class="logo_img" alt="" src="../../static/img/logo.gif" > <span class="wel_word" >图书管理系统</span> <%-- 静态包含manager--%> <%@include file="/pages/common/manager_menu.jsp" %> </div> <div id="main" > <table> <tr> <td>名称</td> <td>价格</td> <td>作者</td> <td>销量</td> <td>库存</td> <td colspan="2" >操作</td> </tr> <c:forEach items="${requestScope.page.items}" var ="book" > <tr> <td>${book.name}</td> <td>${book.price}</td> <td>${book.author}</td> <td>${book.sales}</td> <td>${book.stock}</td> <td><a href="manager/bookServlet?action=getBook&id=${book.id}&method=update" >修改</a></td> <td><a class="deleteBook" href="manager/bookServlet?action=delete&id=${book.id}&method=add" >删除</a> </td> </tr> </c:forEach> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td><a href="pages/manager/book_edit.jsp" >添加图书</a></td> </tr> </table> <div id="page_nav" > <c:if test="${requestScope.page.pageNo>1}" > <a href="manager/bookServlet?action=page&pageNo=1" >首页</a> <a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo-1}" >上一页</a> <a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo-1}" >${requestScope.page.pageNo-1 }</a> </c:if > 【${requestScope.page.pageNo}】 <c:if test="${requestScope.page.pageNo<requestScope.page.pageTotal}" > <a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo+1}" >${requestScope.page.pageNo+1 }</a> <a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo+1}" >下一页</a> <a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageTotal}" >末页</a> </c:if > 共${requestScope.page.pageTotal}页,${requestScope.page.pageTotalCount}条记录 到第<input value="${requestScope.page.pageNo}" name="pn" id="pn_input" />页 <input id="searchPageBtn" type="button" value="确定" > <script> $(function () { $("#searchPageBtn" ).click(function () { let pageNo = $("#pn_input" ).val(); location.href = "${pageScope.basePath}manager/bookServlet?action=page&pageNo=" + pageNo; }) }) </script> </div> </div> <%@include file="/pages/common/foot.jsp" %> </body> </html>
存服务器地址的位置:
head.jsp
1 2 3 4 5 6 7 8 <% String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/" ; pageContext.setAttribute("basePath" , basePath); %>
对原本功能进行优化:
添加图书之后跳转到最后一页(新添加图书的那页)
删除图书之后跳转到当前页
首先在添加、删除、更新跳转链接处增加参数(总页数\当前页数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" > <title>图书管理</title> <%-- 静态包含,base标签,css样式,jquery--%> <%@include file="/pages/common/head.jsp" %> <script> $(function () { $("a.deleteBook" ).click(function () { return confirm("你确定要删除【" + $(this ).parent().parent().find("td:first" ).text() + "】吗?" ); }) }); </script> </head> <body> <div id="header" > <img class="logo_img" alt="" src="../../static/img/logo.gif" > <span class="wel_word" >图书管理系统</span> <%-- 静态包含manager--%> <%@include file="/pages/common/manager_menu.jsp" %> </div> <div id="main" > <table> <tr> <td>名称</td> <td>价格</td> <td>作者</td> <td>销量</td> <td>库存</td> <td colspan="2" >操作</td> </tr> <c:forEach items="${requestScope.page.items}" var ="book" > <tr> <td>${book.name}</td> <td>${book.price}</td> <td>${book.author}</td> <td>${book.sales}</td> <td>${book.stock}</td> <td> <a href="manager/bookServlet?action=getBook&id=${book.id}&method=update&pageNo=${requestScope.page.pageNo}" >修改</a> </td> <td><a class="deleteBook" href="manager/bookServlet?action=delete&id=${book.id}&method=delete&pageNo=${requestScope.page.pageNo}" >删除</a> </td> </tr> </c:forEach> <tr> <td></td> <td></td> <td></td> <td></td> <td></td> <td></td> <td><a href="pages/manager/book_edit.jsp?method=add&pageNo=${requestScope.page.pageTotal}" >添加图书</a></td> </tr> </table> <div id="page_nav" > <c:if test="${requestScope.page.pageNo>1}" > <a href="manager/bookServlet?action=page&pageNo=1" >首页</a> <a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo-1}" >上一页</a> <a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo-1}" >${requestScope.page.pageNo-1 }</a> </c:if > 【${requestScope.page.pageNo}】 <c:if test="${requestScope.page.pageNo<requestScope.page.pageTotal}" > <a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo+1}" >${requestScope.page.pageNo+1 }</a> <a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageNo+1}" >下一页</a> <a href="manager/bookServlet?action=page&pageNo=${requestScope.page.pageTotal}" >末页</a> </c:if > 共${requestScope.page.pageTotal}页,${requestScope.page.pageTotalCount}条记录 到第<input value="${requestScope.page.pageNo}" name="pn" id="pn_input" />页 <input id="searchPageBtn" type="button" value="确定" > <script> $(function () { $("#searchPageBtn" ).click(function () { let pageNo = $("#pn_input" ).val(); location.href = "${pageScope.basePath}manager/bookServlet?action=page&pageNo=" + pageNo; }) }) </script> </div> </div> <%@include file="/pages/common/foot.jsp" %> </body> </html>
然后在BookServlet中修改添加、删除、更新后的重定向页面(同时将所有的list方法均改为跳转到page方法)
为了防止增加图书之后溢出到下一页,但是跳转到原本的最后一页,可以在add方法中传过来的页数+1,这样就永远不会小于最后一页。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 package com.geo.web;import com.geo.pojo.Book;import com.geo.pojo.Page;import com.geo.service.BookService;import com.geo.service.impl.BookServiceImpl;import com.oracle.wls.shaded.org.apache.bcel.generic.SWAP;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import org.apache.commons.beanutils.BeanUtils;import java.io.IOException;import java.lang.reflect.InvocationTargetException;import java.math.BigDecimal;import java.util.List;@WebServlet(name = "bookServlet", value = "/manager/bookServlet") public class BookServlet extends BaseServlet { private BookService bookService = new BookServiceImpl (); protected void page (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, InvocationTargetException, IllegalAccessException { int pageNo = req.getParameter("pageNo" ) != null ? Integer.parseInt(req.getParameter("pageNo" )) : 1 ; int pageSize = req.getParameter("pageSize" ) != null ? Integer.parseInt(req.getParameter("pageSize" )) : Page.PAGE_SIZE; Page<Book> page = bookService.page(pageNo, pageSize); req.setAttribute("page" , page); req.getRequestDispatcher("/pages/manager/book_manager.jsp" ).forward(req, resp); } protected void add (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, InvocationTargetException, IllegalAccessException { int pageNo = Integer.parseInt(req.getParameter("pageNo" )) + 1 ; String name = req.getParameter("name" ); String author = req.getParameter("author" ); BigDecimal price = new BigDecimal (req.getParameter("price" )); Integer sales = Integer.valueOf(req.getParameter("sales" )); Integer stock = Integer.valueOf(req.getParameter("stock" )); Book book = new Book (null , name, author, price, sales, stock, null ); bookService.addBook(book); resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=page&pageNo=" + pageNo); } protected void delete (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String id = req.getParameter("id" ); bookService.deleteBook(Integer.valueOf(id)); resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=page&pageNo=" + req.getParameter("pageNo" )); } protected void update (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String id = req.getParameter("id" ); String name = req.getParameter("name" ); String author = req.getParameter("author" ); BigDecimal price = new BigDecimal (req.getParameter("price" )); Integer sales = Integer.valueOf(req.getParameter("sales" )); Integer stock = Integer.valueOf(req.getParameter("stock" )); Book book = new Book (null , name, author, price, sales, stock, null ); bookService.updateBook(book, Integer.valueOf(id)); resp.sendRedirect(req.getContextPath() + "/manager/bookServlet?action=page&pageNo=" + req.getParameter("pageNo" )); } protected void list (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { List<Book> books = bookService.queryBooks(); req.setAttribute("books" , books); req.getRequestDispatcher("/pages/manager/book_manager.jsp" ).forward(req, resp); } protected void getBook (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int id = Integer.parseInt(req.getParameter("id" )); Book book = bookService.queryBook(id); req.setAttribute("book" , book); req.getRequestDispatcher("/pages/manager/book_edit.jsp" ).forward(req, resp); } }
首页价格区间筛选符合条件的图书
首先在接口BookService中添加对应方法:pageByPrice
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.geo.service;import com.geo.pojo.Book;import com.geo.pojo.Page;import java.util.List;public interface BookService { public void addBook (Book book) ; public void deleteBook (Integer id) ; public void updateBook (Book book, Integer id) ; public Book queryBook (Integer id) ; public List<Book> queryBooks () ; public Page<Book> page (int pageNo, int pageSize) ; Page<Book> pageByPrice (int pageNo, int pageSize, int min, int max) ; }
然后在BookServiceImpl实现方法体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Override public Page<Book> pageByPrice (int pageNo, int pageSize, int min, int max) { Page<Book> page = new Page <Book>(); Integer pageTotalCount = bookDao.queryForPageTotalCountByPrice(min, max); page.setPageTotalCount(pageTotalCount); int pageTotal = pageTotalCount / pageSize; if (pageTotalCount % pageSize > 0 ) { pageTotal += 1 ; } page.setPageTotal(pageTotal); if (pageNo < 1 ) { pageNo = 1 ; } if (pageNo > pageTotal) { pageNo = pageTotal; } page.setPageNo(pageNo); int begin = (page.getPageNo() - 1 ) * pageSize; List<Book> items = bookDao.queryForPageItemsByPrice(begin, pageSize, min, max); page.setItems(items); return page; }
在BookDaoImpl中实现数据库查询的操作
1 2 3 4 5 6 7 8 9 10 11 12 @Override public Integer queryForPageTotalCountByPrice (int min, int max) { String sql = "select count(*) from t_book where `price` between ? and ?" ; Number count = (Number) queryForSingleValue(sql, min, max); return count.intValue(); } @Override public List<Book> queryForPageItemsByPrice (int begin, int pageSize, int min, int max) { String sql = "select `id`,`name`,`author`,`price`,`sales`,`stock`,`image_path` imgPath from t_book where `price` between ? and ? limit ?,?" ; return queryForList(Book.class, sql, min, max, begin, pageSize); }
然后在点击搜索的按钮上绑定参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" > <title>书城首页</title> <%-- 静态包含,base标签,css样式,jquery--%> <%@include file="/pages/common/head.jsp" %> </head> <body> <div id="header" > <img class="logo_img" alt="" src="static/img/logo.gif" > <span class="wel_word" >网上书城</span> <div> <a href="pages/user/login.jsp" >登录</a> | <a href="pages/user/regist.jsp" >注册</a> <a href="pages/cart/cart.jsp" >购物车</a> <a href="pages/manager/manager.jsp" >后台管理</a> </div> </div> <div id="main" > <div id="book" > <div class="book_cond" > <form action="client/bookServlet" method="get" > <!--在隐藏域中添加参数--> <input type="hidden" name="action" value="pageByPrice" > <!--param参数用于搜索完成之后对价格区间进行回显操作--> 价格:<input id="min" type="text" name="min" value="${param.min}" > 元 - <input id="max" type="text" name="max" value="${param.max}" > 元 <input type="submit" value="查询" /> </form> </div> <div style="text-align: center" > <span>您的购物车中有3 件商品</span> <div> 您刚刚将<span style="color: red" >时间简史</span>加入到了购物车中 </div> <c:forEach items="${requestScope.page.items}" var ="book" > <div class="b_list" > <div class="img_div" > <img class="book_img" alt="" src="${book.imgPath}" /> </div> <div class="book_info" > <div class="book_name" > <span class="sp1" >书名:</span> <span class="sp2" >${book.name}</span> </div> <div class="book_author" > <span class="sp1" >作者:</span> <span class="sp2" >${book.author}</span> </div> <div class="book_price" > <span class="sp1" >价格:</span> <span class="sp2" >¥ ${book.price}</span> </div> <div class="book_sales" > <span class="sp1" >销量:</span> <span class="sp2" >${book.sales}</span> </div> <div class="book_amount" > <span class="sp1" >库存:</span> <span class="sp2" >${book.stock}</span> </div> <div class="book_add" > <button>加入购物车</button> </div> </div> </div> </c:forEach> </div> <div id="page_nav" style="clear: both" > <c:if test="${requestScope.page.pageNo > 1}" > <a href="client/bookServlet?action=page&pageNo=1" >首页</a> <a href="client/bookServlet?action=page&pageNo=${requestScope.page.pageNo-1}" >上一页</a> <a href="client/bookServlet?action=page&pageNo=${requestScope.page.pageNo-1}" >${requestScope.page.pageNo-1 }</a> </c:if > 【${requestScope.page.pageNo}】 <c:if test="${requestScope.page.pageNo < requestScope.page.pageTotal}" > <a href="client/bookServlet?action=page&pageNo=${requestScope.page.pageNo+1}" >${requestScope.page.pageNo+1 }</a> <a href="client/bookServlet?action=page&pageNo=${requestScope.page.pageNo+1}" >下一页</a> <a href="client/bookServlet?action=page&pageNo=${requestScope.page.pageTotal}" >末页</a> </c:if > 共${requestScope.page.pageTotal}页,${requestScope.page.pageTotalCount}条记录 到第<input value="${requestScope.page.pageNo}" name="pn" id="pn_input" />页 <input id="searchPageBtn" type="button" value="确定" > <script> $(function () { $("#searchPageBtn" ).click(function () { let pageNo = $("#pn_input" ).val(); location.href = "${pageScope.basePath}client/bookServlet?action=page&pageNo=" + pageNo; }) }) </script> </div> </div> </div> <%@include file="/pages/common/foot.jsp" %> </body> </html>
Cookie 什么是Cookie
Cookie是由服务器通知客户端保存键值对的一种技术。
客户端有了Cookie后,每次请求都发送给服务器。
每个Cookie的大小不能超过4kb。
如何创建Cookie 在CookieServlet程序中写好方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.geo.servlet;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.Cookie;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet(name = "CookieServlet", value = "/cookieServlet") public class CookieServlet extends BaseServlet { protected void createCookie (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = new Cookie ("key1" , "value1" ); resp.addCookie(cookie); resp.getWriter().println("Cookie创建成功!" ); } }
创建好Cookie之后,可以在浏览器控制台中的Application选项中查看:
服务器如何获取Cookie 服务器获取客户端的Cookie只需要一行代码:req.getCookies():Cookie[],会返回一个Cookie对象数组。
如果要寻找一个特定名字的Cookie,则可以封装成为一个工具类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.geo.util;import jakarta.servlet.http.Cookie;public class CookieUtils { public static Cookie findCookie (String name, Cookie[] cookies) { if (name == null || cookies == null || cookies.length == 0 ) { return null ; } for (Cookie cookie : cookies) { if (name.equals(cookie.getName())) { System.out.println("找到了需要的cookie" ); return cookie; } } return null ; } }
如何修改Cookie值 方案一
先创建一个要修改的同名的Cookie对象。
在构造器,同时赋予新的Cookie值。
调用response.addCookie(Cookie);
1 2 3 4 5 6 7 protected void updateCookie (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = new Cookie ("key1" , "value2" ); resp.addCookie(cookie); System.out.println("key1的值已经修改了" ); }
方案二
先查找需要修改的Cookie对象。
调用setValue方法赋予新的Cookie值。
调用response.addCookie通知客户端保存数据。
1 2 3 4 5 6 7 8 9 10 protected void updateCookie (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = CookieUtils.findCookie("key1" , req.getCookies()); if (cookie != null ) { cookie.setValue("newValue" ); resp.addCookie(cookie); } }
Cookie的生命控制 Cookie的生命控制指的是如何管理Cookie什么时候被销毁。
setMaxAge():正数表示在指定的秒数后过期,负值表示浏览器关闭Cookie就会被删除,0表示马上删除Cookie。默认值是-1.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 protected void life3600 (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = new Cookie ("default" , "defaultLife" ); cookie.setMaxAge(3600 ); resp.addCookie(cookie); } protected void defaultLife (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = new Cookie ("default" , "defaultLife" ); cookie.setMaxAge(-1 ); resp.addCookie(cookie); } protected void deleteNow (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = new Cookie ("default" , "defaultLife" ); cookie.setMaxAge(0 ); resp.addCookie(cookie); }
Cookie有效路径Path的设置 举例:
CookieA: path=/工程路径
CookieB:path=/工程路径/abc
请求地址如下:http://ip:port/工程路径/a.html
CookieA发送
CookieB不发送
请求地址如下:http://ip:root/工程路径/abc/a.html
CookieA发送
CookieB发送
1 2 3 4 5 protected void testPath (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Cookie cookie = new Cookie ("default" , "defaultLife" ); cookie.setPath(req.getContextPath() + "/abc" ); resp.addCookie(cookie); }
Cookie练习-免输入用户名登录 首先创建一个用于获取输入的表单:
1 2 3 4 5 6 7 8 9 10 11 12 13 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <form action="http://localhost:8080/cs/loginServlet" method="get" > 用户名:<input type="text" name="username" value="${cookie.username.value}" ><br> 密码:<input type="password" name="password" value="${cookie.password.value}" ><br> <input type="submit" value="提交" > </form> </body> </html>
然后在LoginServlet中处理数据,发送Cookie:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.geo.servlet;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.Cookie;import jakarta.servlet.http.HttpServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet(name = "LoginServlet", value = "/loginServlet") public class LoginServlet extends HttpServlet { @Override protected void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getParameter("username" ); String password = req.getParameter("password" ); if (username.equals("admin" ) && password.equals("admin" )) { Cookie cookie = new Cookie ("username" , username); cookie.setMaxAge(7 * 60 * 60 ); Cookie cookie1 = new Cookie ("password" , password); cookie1.setMaxAge(7 * 60 * 60 ); resp.addCookie(cookie); resp.addCookie(cookie1); System.out.println("登录成功" ); } else { System.out.println("登录失败" ); } } }
只要登录成功,而且不超时,下次就会自动填充账号和密码。
Session 什么是Session会话?
Session就是一个接口(HttpSession)
Session就是会话。它是用来维护客户端和服务器之间关联的一种技术。
每个客户端都有自己的一个Session会话。
Session会话中经常用来保存用户登录的信息。
如何创建Session和获取 request.getSession()
第一次调用是创建Session会话,之后每次调用都是获取Session会话。
isNew()判断Session是不是刚创建出来的。
每个会话都有一个身份证号,也就是ID值,是唯一的。
getId()得到Session的id值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @WebServlet(name = "SessionServlet", value = "/sessionServlet") public class SessionServlet extends BaseServlet { protected void createOrGetSession (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(); boolean aNew = session.isNew(); System.out.println(aNew); String id = session.getId(); System.out.println(id); resp.getWriter().write("得到的Session的id是" + id + "<br>" ); resp.getWriter().write("这个Session是否是新创建的" + aNew + "<br>" ); } }
Session域数据的存取 1 2 3 4 5 6 7 8 9 10 protected void setAttribute (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getSession().setAttribute("key1" , "value1" ); } protected void getAttribute (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Object key1 = req.getSession().getAttribute("key1" ); resp.getWriter().write("" + key1); }
Session生命周期 值为正数的时候设置超时时长,值为负数的时候,永不超时。
public void setMaxIactiveInterval(int interval):设置Session的超时时间(以秒为单位),超过指定的时长,Session就会销毁。
public int getMaxIactiveInterval():获取Session的超时时间。默认时长为30分钟。
public void invalidate():让当前Session会话立即销毁。
1 2 3 4 protected void defaultLife (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int maxInactiveInterval = req.getSession().getMaxInactiveInterval(); resp.getWriter().write("Session会话的默认超时时长为" + maxInactiveInterval + "秒" ); }
如果要在web工程中,将默认的超时时长为其他时长,可以在web.xml中进行配置,这样就变成了20分钟。如下:
1 2 3 <session-config > <session-timeout > 20</session-timeout > </session-config >
如果要改动单独的Session会话,则使用上方的方法。
[!IMPORTANT]
Session超时的时长的意思是指,客户端两次请求服务端最大的时间间隔。
浏览器和Session之间关联的技术内幕 底层其实是由cookie技术来实现的。
项目第六阶段 登录-显示用户名 首先,用户在登录之后将 用户信息保存到Session域中。
在UserServlet处理login业务中,修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public void login (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("处理登录的需求" ); String username = req.getParameter("username" ); String password = req.getParameter("password" ); User login = userService.login(new User (null , username, password, null )); if (login == null ) { req.setAttribute("msg" , "用户名或密码错误!" ); req.setAttribute("username" , username); req.getRequestDispatcher("/pages/user/login.jsp" ).forward(req, resp); } else { req.getSession().setAttribute("user" , login); req.getRequestDispatcher("/pages/user/login_success.jsp" ).forward(req, resp); } }
然后在页面显示的jsp中,将显示位置改为${sessionScope.user.username}。
同时在页头使用JSTL加上if判断,如果登录了就显示用户名,如果未登录就显示登录、注册两个链接。
核心代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 <div> <c:if test="${empty sessionScope.user}" > <%-- 如果用户未登录显示登录和注册--%> <a href="pages/user/login.jsp" >登录</a> | <a href="pages/user/regist.jsp" >注册</a> </c:if > <c:if test="${not empty sessionScope.user}" > <%-- 如果用户已经登录则显示用户名--%> <span>欢迎<span class="um_span" >${sessionScope.user.username}</span>光临尚硅谷书城</span> </c:if > <a href="pages/cart/cart.jsp" >购物车</a> <a href="pages/manager/manager.jsp" >后台管理</a> </div>
登出-注销用户 1、销毁Session中用户的登录信息,或者销毁session。
2、重定向到首页或者登录页面。
1 2 3 4 public void logOut (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getSession().invalidate(); resp.sendRedirect(req.getContextPath()); }
在jsp页面中进行相应的修改。
1 2 3 4 5 6 <div> <span>欢迎<span class="um_span" >${sessionScope.user.username}</span>光临尚硅谷书城</span> <a href="pages/order/order.jsp" >我的订单</a> <a href="userServlet?action=logOut" >注销</a> <a href="index.jsp" >返回</a> </div>
表单重复提交之验证码 表单重复提交有三种情况:
提交完表单,服务器使用请求转发的方式进行页面的跳转。这个时候,用户按下F5功能键,就会发起最后一次请求,造成表单重复提交问题。解决办法:使用重定向而不是请求转发来进行跳转。
用户正常提交服务器,但是由于网络延迟等原因,迟迟未收到服务器的响应,这个时候用户以为提交失败,然后多点了几次提交,也会造成表单重复提交。
用户正常提交服务器,服务器也没有延迟,正常提交之后,用户回退浏览器,重新提交也在造成表单重复提交。
第一种情况可以使用重定向来解决问题。
后两种就只能使用验证码来解决。
验证码的底层原理
谷歌kaptcha图片验证码的使用 使用步骤:
导入谷歌验证码的jar包:Kaptcha.jar
在web.xml中配置用于生成验证码的servlet程序。
1 2 3 4 5 6 7 8 <servlet > <servlet-name > KaptchaServlet</servlet-name > <servlet-class > com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > KaptchaServlet</servlet-name > <url-pattern > /kaptcha</url-pattern > </servlet-mapping >
在表单中使用img标签去显示验证码的图片并使用
1 2 3 4 5 6 7 <form> 用户名:<input type="text" ><br> 密码:<input type="text" ><br> 验证码:<input type="text" name="code" ><img src="http://localhost:8080/tmp/kaptcha" > </form>
从Session域中获取验证码的值:KAPTCHA_SESSION_KEY
Filter过滤器 什么是过滤器
Filter过滤器是JavaWeb三大组件之一:Servlet程序、Listener监听器、Filter过滤器。
过滤器是JavaEE的规范,也就是接口。
作用是拦截请求,过滤响应。
拦截请求常见的应用场景:权限检查、日记操作、事务管理。
Filter的初体验 要求:在工程下有一个admin目录,这个目录下的所有资源都必须用户登录之后才可以访问。
不使用Filter,可以在jsp文件中使用java代码控制:如果session域中没有用户的信息,就跳转到登录页面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <% Object user = session.getAttribute("user" ); if (user == null ) { request.getRequestDispatcher("/login.jsp" ).forward(request, response); } %> 我是a.jsp文件 </body> </html>
使用过滤器来实现:
首先新建一个Filter类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.geo;import jakarta.servlet.*;import jakarta.servlet.annotation.WebFilter;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpSession;import java.io.IOException;public class AdminFilter implements Filter { @Override public void doFilter (ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; HttpSession session = httpServletRequest.getSession(); Object user = session.getAttribute("user" ); if (user == null ) { servletRequest.getRequestDispatcher("/login.jsp" ).forward(servletRequest, servletResponse); } else { filterChain.doFilter(servletRequest, servletResponse); } } @Override public void init (FilterConfig filterConfig) throws ServletException { } @Override public void destroy () { } }
一定要实现init和destroy方法。
然后去web.xml中配置信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <filter > <filter-name > AdminFilter</filter-name > <filter-class > com.geo.AdminFilter</filter-class > </filter > <filter-mapping > <filter-name > AdminFilter</filter-name > <url-pattern > /index.jsp</url-pattern > </filter-mapping >
可能由于使用的JDK版本(JDK19)太高,导致每次都启动失败,所以使用了注解的方式注册Filter,成功启动。
先将xml中的信息删掉,然后在Filter类名上加上。
1 @WebFilter(urlPatterns = "/*")
其中的路径就是要进行权限判断的位置。
Filter的生命周期 Filter的生命周期包含以下方法:
构造器方法(web工程启动的时候执行)
init初始化方法(web工程启动的时候执行)
doFilter过滤方法(每次只要拦截到就会执行)
destroy销毁方法(web共工程停止的时候就会执行)
FilterConfig类 FilterConfig类是filter过滤器的配置文件类。
Tomcat每次创建Filter的时候就会创建一个FilterConfig类,这里包含了Filter配置文件的配置信息。
FilterConfig类的作用是为了获取Filter过滤器的配置内容:
获取Filter的别名filter-name
获取在Filter中配置的init-param初始化参数
获取ServletContext对象
Filter的拦截路径
精确匹配
1 2 <url-pattern > /target.jsp</url-pattern > 以上路径必须在:http://ip:port/工程路径/target.jsp
目录匹配
1 <url-pattern > /admin/*</url-pattern >
类型匹配(不能斜杠开头)
1 <url-pattern > *.html</url-pattern >
Filter过滤器只关心地址是否匹配,而不关心请求的资源是否存在,所以即使资源不存在也会照样拦截。
JSON 什么是JSON? JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人的阅读和编写。同时也易于机器解析和生成。
JSON在JavaScript中的使用。 JSON的定义 json是由键值对组成,由{}包围,每个键由引号引起来,键和值之间使用冒号进行分割,多组键值对之间使用逗号进行分割。
JSON的访问 json本身是一个对象。
json中的key可以理解为对象中的一个属性。
json中的key访问就跟访问对象的属性一样:json对象.key
JSON的两个常用方法 json的存在有两种形式。
一种是对象的形式存在,称为json对象。
另一种是字符串的形式存在,称为json字符串。
JSON.stringify()把json对象转换为json字符串。
JSON.parse()把json字符串转换为json对象。
JavaBean和JSON的相互转换 首先导入包:gson.jar。
然后创建好一个JavaBean类。
然后创建好一个对象:
1 2 3 4 5 6 7 8 9 @Test public void test () { Person person = new Person (1 , "ihi" ); Gson gson = new Gson (); String json = gson.toJson(person); System.out.println(json); Person person1 = gson.fromJson(json, Person.class); System.out.println(person1); }
List和JSON的互转 1 2 3 4 5 6 7 8 9 10 11 @Test public void test2 () { List<Person> persons = new ArrayList <Person>(); persons.add(new Person (1 , "ihi" )); persons.add(new Person (2 , "ihi" )); Gson gson = new Gson (); String json = gson.toJson(persons); System.out.println(json); List<Person> persons1 = gson.fromJson(json, persons.getClass()); System.out.println(persons1); }
Map和JSON的互转 1 2 3 4 5 6 7 8 9 10 11 12 @Test public void test3 () { Map<Integer, Person> persons = new HashMap <Integer, Person>(); persons.put(1 , new Person (1 , "ihi" )); persons.put(2 , new Person (2 , "ihi" )); Gson gson = new Gson (); String json = gson.toJson(persons); System.out.println(json); Map<Integer, Person> persons1 = gson.fromJson(json, persons.getClass()); System.out.println(persons1); }
AJAX请求 什么是AJAX请求 AJAX即“Asynchronous JavaScript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术。
AXAX是一种浏览器通过js异步发起请求。局部更新页面的技术。
原生AJAX请求 在html页面进行请求的发送:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" > <html > <head > <meta http-equiv ="pragma" content ="no-cache" /> <meta http-equiv ="cache-control" content ="no-cache" /> <meta http-equiv ="Expires" content ="0" /> <meta http-equiv ="Content-Type" content ="text/html; charset=UTF-8" > <title > Insert title here</title > <script type ="text/javascript" > function ajaxRequest ( ) { var xmlhttprequest = new XMLHttpRequest (); xmlhttprequest.open ("GET" , "http://localhost:8080/json/ajaxServlet?action=javaScriptAjax" , true ); xmlhttprequest.onreadystatechange = function ( ) { if (xmlhttprequest.readyState == 4 && xmlhttprequest.status == 200 ) { var jsonObj = JSON .parse (xmlhttprequest.responseText ); document .getElementById ("div01" ).innerHTML = "编号:" + jsonObj.id + " , 姓名:" + jsonObj.name ; } } xmlhttprequest.send (); } </script > </head > <body > <button onclick ="ajaxRequest()" > ajax request</button > <button onclick ="ajaxRequest()" > ajax request</button > <button onclick ="ajaxRequest()" > ajax request</button > <button onclick ="ajaxRequest()" > ajax request</button > <button onclick ="ajaxRequest()" > ajax request</button > <div id ="div01" > </div > <table border ="1" > <tr > <td > 1.1</td > <td > 1.2</td > </tr > <tr > <td > 2.1</td > <td > 2.2</td > </tr > </table > </body > </html >
服务端servlet返回请求的数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.geo;import com.google.gson.Gson;import jakarta.servlet.ServletException;import jakarta.servlet.annotation.WebServlet;import jakarta.servlet.http.HttpServletRequest;import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;@WebServlet(name = "AjaxServlet", value = "/ajaxServlet") public class AjaxServlet extends BaseServlet { protected void javaScriptAjax (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("Ajax请求过来了" ); Person person = new Person (2 , "小杰" ); Gson gson = new Gson (); String json = gson.toJson(person); resp.getWriter().write(json); } }
JQuery中的AJAX请求 $.ajax方法
参数
功能
url
表示请求的地址
type
表示请求的类型是GET还是POST
data
表示发送给服务器的数据
success
请求响应,响应的回调函数
dataType
响应的数据类型,text、xml、json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $(function ( ) { $("#ajaxBtn" ).click (function ( ) { $.ajax ({ url : "http://localhost:8080/json/ajaxServlet" , data : {action : "JQueryAjax" }, type : "GET" , success : function (data ) { $("#msg" ).html (" ajax 编号:" + data.id + " , 姓名:" + data.name ); }, dataType : "json" }); }); )
$.get,这是一个简单的get请求功能,用于取代$.ajax。
参数
功能
url
请求的url地址
data
发送的数据
callback
成功的回调函数
type
返回的数据类型
1 2 3 4 5 6 7 $("#getBtn" ).click (function ( ) { $.get ("http://localhost:8080/16_json_ajax_i18n/ajaxServlet" , "action=JQueryGet" , function (data ) { $("#msg" ).html (" get 编号:" + data.id + " , 姓名:" + data.name ); }, "json" ); });
1 2 3 4 5 6 7 $("#postBtn" ).click (function ( ) { $.post ("http://localhost:8080/16_json_ajax_i18n/ajaxServlet" , "action=JQueryPost" , function (data ) { $("#msg" ).html (" post 编号:" + data.id + " , 姓名:" + data.name ); }, "json" ); });
$.getJson
参数
功能
url
请求的url地址
data
发送给服务器的数据
callback
成功的回调函数
1 2 3 4 5 6 $("#getJSONBtn" ).click (function ( ) { $.getJSON ("http://localhost:8080/16_json_ajax_i18n/ajaxServlet" , "action=jQueryGetJSON" , function (data ) { $("#msg" ).html (" getJSON 编号:" + data.id + " , 姓名:" + data.name ); }); });
表单序列化serialize()
serialize()可以把表单中所有的的表单项的内容都获取到,并以name=value&name=value的形式进行拼接。
1 2 3 4 5 6 7 $("#submit" ).click (function ( ) { $.getJSON ("http://localhost:8080/16_json_ajax_i18n/ajaxServlet" , "action=jQuerySerialize&" + $("#form01" ).serialize (), function (data ) { $("#msg" ).html (" Serialize 编号:" + data.id + " , 姓名:" + data.name ); }); });
i18n国际化
..
...
...