Skip to content

JavaWeb

约 7769 个字 303 行代码 预计阅读时间 30 分钟

在 Idea 中的一些配置:

  • 修改在浏览器中项目的名字:将 http://localhost:8080/demo2_war_exploded/ 修改为 http://localhost:8080/day09/ image-20240603153716083
  • 不用改 html 还要重启 server。image-20240603153923620

Html

查看标签作用:HTML Cheat Sheet & Quick Reference (cheatsheets.zip)

常用标签

  • 表格跨行效果: rowspancolspan
  • 加粗:b,删除线:s,下划线:s,斜体:i
  • 原样输出:pre
  • 在一行显示:span

表单

  • id 属性用于为 HTML 元素提供一个唯一的标识符。它在 CSS 和 JavaScript 中非常有用。
  • id 也用于 <label> 标签的 for 属性,以建立标签和对应输入框之间的关联。这样,当用户点击标签时,与之关联的输入控件会获得焦点。
  • name 属性的主要作用是在表单提交时,标识表单控件的数据,并成为数据的一部分发送到服务器。
  • action 提交的地址
<form method="POST" action="api/login">
  <label for="mail">Email: </label>
  <input type="email" id="mail" name="mail">
  <br/>
  <label for="pw">Password: </label>
  <input type="password" id="pw" name="pw">
  <br/>
  <label for="city">City:</label>
  <select name="city" id="city">
    <option value="1">Sydney</option>
    <option value="2">Melbourne</option>
    <option value="3">Cromwell</option>
  </select>
  <br/>
  <input type="submit" value="Login">
  <br/>
  <input type="checkbox" id="ck" name="ck">
  <label for="ck">Remember me</label>
</form>

效果:





CSS

优先级

  • 由上到下,由外到内,优先级从低到高。
  • style(直接在标签列表写)> id 选择器 > class 选择器 > 标签名选择器

选择器

  1. 使用 标签名 作为选择器的名称

    div {
        /* css 代码 */
    }
    
  2. class 选择器 : <div class="abc></div>"

    div.abc {
        /* css 代码 */
    }
    或者
    .abc {
    
    }
    
  3. id 选择器: <div id="efg"></div>

    div#efg {
        /* css 代码 */
    }
    或者
    #efg {
    
    }
    
  4. 关联选择器:<div><p> 123 </p></div>

    div p {
        /* css 代码 */
    }
    
  5. 组合选择器

    div,p {
        /* css 代码 */
    }
    
  6. 伪元素选择器, 例如超链接的状态 <a href="https://www.bing.com/"> 超链接 </a>

    a:link {
        /* 原始状态 */
    }
    a:hover {
        /* 悬停状态 */
    }
    a:active {
        /* 点击状态 */
    }
    a:visited {
        /* 点击之后 */
    }
    

盒子模型

边框:border, 内边距:padding,外边距:margin

image-20240521213625712

布局的定位

布局的漂浮:float

  • left : 后面的 div 到右边
  • right : 后面的 div 到左边

布局的定位:position

  1. absolute
    • 从文档流中脱出:元素完全脱离文档流,不占据空间。
    • 定位方式:通过 topbottomleftright 属性进行定位。其位置是相对于最近的已定位祖先元素(relativeabsolutefixed)。
  2. relative
    • 不脱离文档流:元素仍然占据空间,但可以通过偏移属性移动。
    • 定位方式:通过 topbottomleftright 属性进行偏移。其位置是相对于其正常位置进行偏移。

JavaScript

语法

  • JS 的特点:交互性,安全性(不能访问本地的文件),跨平台性

  • JS 的类型:string,number,boolean,null,undefined

  • 声明变量:var a = 10,定义数组:var arr = [1,"4",true] (是 Array 数组,类型可以不同)

  • == 比较的只是值,=== 比较的是类型和值。

  • JS 的函数 ,不支持重载。(如果有同名函数,则实际执行的是最后一个(顺序上)

    function fun() {
        // code   
    }
    
  • 全局变量:在函数外面定义的变量; 局部变量:函数内部定义的变量。

  • 嵌入 js 文件放在 </body> 后面。

BOM

浏览器对象模型:browser object model

  • navigator 对象:获取浏览器的信息
  • screen 对象:获取屏幕的信息
  • location 对象:请求和设置 URL 地址:location.href
  • history 对象:请求的 URL 的历史记录:
    • 回退:history.back();histroy(-1);
    • 前进:history.forward();history(1);
  • window 对象:
    • 页面弹框:window.alert() ,简写 alert()
    • 确认框:window.confirm()
    • 打开新窗口:window.open()
    • 关闭窗口:window.close()
    • 做定时器:setInterval("js代码", 毫秒数) 每几秒执行一次
    • 倒计时:setTimeout() 只会执行一次。

DOM

文档对象模型:document object model

<span id="spanid">hahaha</span>
  • Element 对象:即标签对象:span
  • 属性对象:标签下的属性,即 id
  • 文本对象:即 hahaha

DOM 解析 HTML

<html>
    <head>
        <title>aaaa</title>
    </head>
    <body>
        <span id="spanid">bbb</span>
    </body>
</html>

根据 HTML 的层级结构,在内存中分配一个树形结构。

  • 根结点为 html
  • document 对象:整个 html 文件
  • element 对象:标签对象
  • 属性对象
  • 文本对象
  • Node 节点对象:是所有对象的父对象

image-20240523193929516

document 对象

查找节点:

  • 指定 id 的第一个对象:getElementById()
  • 指定名称的对象 集合(数组)getElementsByName()
  • 指定标签的对象 集合(数组)getElementsByTagName()

节点的方法

  • 创建指定标签的元素:creatElement("tagname")
  • 创建文本:creatTextNode("message")
  • 添加子节点到末尾:appendChild() 可以实现剪贴的效果(把上个文本的内容弄到别处)
  • 在节点之前插入节点:insertBefore(Node,NewNode)
  • 替换节点:replaceChild(newNode, oldNode) 通过父节点替换
  • 复制节点:cloneNode(true)
  • 删除节点:removeChild() 通过父节点删除。

新创建一个对象过程:

  1. 使用 document.createElement() 方法创建元素。
  2. 使用 setAttribute() 方法给元素添加属性。
  3. 使用 document.createTextNode() 创建文本节点。
  4. 使用 appendChild() 方法将文本节点添加到元素中。

image-20240524094913089

element 对象

element 对象:标签对象。

常用方法

  • 获取属性的值:getAttribute()

  • 设置属性的值:setAttribute()

  • 删除属性:removeAttribute(),但不能删除 value

Node 对象

任何元素都可以看作是一个 Node,比如document对象,element 对象,属性对象,文本对象。

属性:

  • 类型: nodeType
  • 名称: nodeName
  • 值: nodeValue
  • 父节点:parentNode
  • 子节点:childNodes
  • 同辈节点:nextSiblingpreviousSibling

interHTML

作用

  • 获取文本内容
  • 想标签里面设置内容
  • 常和 divspan 搭配来用

XML

可扩展标记型语言:eXtensible Markup Language

经常用在文件配置,标签可以自己定义。

文档声明

文档声明必须放在第一行。

<?xml version="1.0" encoding="utf-8"?>

属性:

  • version:xml 的版本。1.01.1
  • encoding:xml 的编码。gbkutf-8
  • standalone: 是否需要其他依赖文件

中文乱码问题

  • encoding 中的编码和保存文件的编码格式不同。(读和写的格式不同)

xml文件中空格和换行都会当成内容和来解析。

xml 中 注释不能嵌套

特殊符号如:<,>,& 需要特殊字符转义才能正常使用。

  • < 对应的是:&lt;
  • > 对应的是:&gt;
  • & 对应的是: &amp;

xml 的约束

引入方式

  • 引入外部的本机dtd文件:<!DOCTYPE xx SYSTEM "dtd文件的路径">
  • 引入网络上的dtd文件:<!DOCTYPE 根元素 PUBLIC "DTD名称" "URL">

XML 的解析方式:dom 和 sax 。

  • sax解析过程:采用事件驱动,边读边解析,从上到下,一行一行的解析,解析到某一个对象,返回对象名称,但不能实现增删改操作
  • dom解析过程:根据xml的树形结构,把xml的标签,属性和文本豆封装成对象,可以实现增删改操作。可能产生内存溢出。

dom4j 解析 xml

拿到 document (dom4j 包下的)

SAXReader reader = new SAXReader();
Document document = reader.read(url);

SAXReader creates a DOM4J tree from SAX parsing events.

document 的父接口是 Node

  • 获取根节点:getRootElement(),返回 element 类型

获取 element 的子标签

  • 获取所有一层子标签:elements()
  • 获取制定标签的子标签:elements("qname")
  • 获取制定标签的第一个子标签:element("qname")

查询

public static void selsctName() throws DocumentException {
    SAXReader saxReader = new SAXReader();
    Document document = saxReader.read("/Users/selfknow/program/Heima/code/web0/com/src/p1.xml");

    // 得到根节点
    Element root = document.getRootElement();

    // 得到 p1
    List<Element> list = root.elements("p1");

    for(Element ele: list) {
        Element name1 = ele.element("name");
        System.out.println(name1.getText());
    }
}

添加

在第一个 p1 标签末尾添加一个元素

public static void addSex() throws Exception {
    SAXReader saxReader = new SAXReader();
    Document document = saxReader.read("/Users/selfknow/program/Heima/code/web0/com/src/p1.xml");

    // 得到根节点
    Element root = document.getRootElement();

    // 得到第一个p1标签
    Element p1 = root.element("p1");

    Element p11 = p1.addElement("Sex");
    p11.setText("man");

    // 写回 xml
    FileOutputStream sc = new FileOutputStream("/Users/selfknow/program/Heima/code/web0/com/src/p1.xml");
    OutputFormat format = OutputFormat.createPrettyPrint();
    XMLWriter xmlWriter = new XMLWriter(sc,format);
    xmlWriter.write(document);
    xmlWriter.close();
}

创建一个元素

Element school = DocumentHelper.createElement("school");

删除

得到该元素,然后获取该元素的父节点(getParent() 方法),通过父节点删除该元素。

xpath

可以直接获取到某个元素

  • /AAA/DDD/BBB 一层一层的找,找到 BBB
  • //BBB 找到所有元素为 BBB 的
  • /* 所有元素
  • //BBB[@id='b1'] BBB上有id属性,且id属性为b1

Tomcat

Web 资源

  • 静态资源:html

  • 动态资源:JSP/Servlet

找到 tomcat 路径的bin 文件夹

  • 启动:sh startup.sh
  • 关闭:sh shutdown.sh

image-20240529225510613

默认端口:8080

tomcat路径\conf\server.xml 中修改端口号

webapps 中写项目。

conf/server.xml

在 Tomcat 中,conf/server.xml 文件是一个非常重要的配置文件,它用于配置整个 Tomcat 服务器的工作环境。这个文件包含了定义 Tomcat 服务器各个组件如何运行的各种设置。以下是一些 server.xml 中常见配置项的说明:

  1. Server<Server> 元素是顶层元素,代表整个 Tomcat 服务器。这个元素通常包括属性如端口号(用于接受关闭命令)和关闭命令的字符串。

  2. Service<Service> 元素用来定义一个服务,它包括一个或多个连接器(Connector)和一个引擎(Engine)。服务代表可以处理进来的请求的一组连接器和引擎。

  3. Connector<Connector> 元素配置如何接收网络连接。常见的配置包括端口号、使用的协议(比如 HTTP/1.1、AJP)、以及针对性能和安全的各种设置(如连接超时、TLS/SSL 配置等)。

  4. Engine<Engine> 元素代表请求处理的中央组件,它处理通过连接器接收的所有请求。一个引擎可以包含多个主机(Host),即虚拟主机。

  5. Host<Host> 元素定义一个虚拟主机,即一个域名对应的网站容器。常见的配置包括主机名(如 localhost)、应用部署的目录(appBase)以及日志配置。

  6. Context<Context> 元素定义一个 Web 应用的运行环境,可以设置如应用的路径、文档根目录和与特定 Web 应用相关的参数。

  7. Realm<Realm> 元素用于配置用户认证和授权的环境。Tomcat 支持多种类型的 Realm 实现,如基于内存的用户列表、基于数据库的用户验证等。

  8. Executor<Executor> 元素用于定义线程池,可以被多个 Connector 共享。这有助于优化线程资源的使用和管理。

  9. Valve<Valve> 元素用于在特定的管道(pipeline)点插入特定功能,例如访问日志的记录、IP 过滤等。

这些配置项的组合定义了 Tomcat 服务器的工作方式,包括它如何处理入站连接、如何部署和运行 Web 应用程序、以及如何管理安全和日志记录。正确地配置这些参数对于优化服务器性能和保证运行安全至关重要。

http 协议

Referer:请求来自那个页面,可以用来做防盗链。

http-equiv 属性常见的用途包括:

  1. 内容类型(Content-Type)

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    
    这设置了文档的字符集,告诉浏览器使用 UTF-8 编码来解析页面。

  2. 缓存控制(Cache-Control)

    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
    
    这告诉浏览器不要缓存页面的内容,每次访问时都需要从服务器重新加载。

  3. 过期控制(Expires)

    <meta http-equiv="Expires" content="Wed, 21 Oct 2015 07:28:00 GMT">
    
    设置页面的过期时间,过了这个时间点后,页面内容需要从服务器重新加载。

  4. 自动刷新(Refresh)

    <meta http-equiv="refresh" content="30;url=http://example.com/">
    
    这会在 30 秒后自动将用户重定向到指定的 URL 地址。

  5. X-UA-Compatible(主要用于控制 Internet Explorer 的兼容模式):

    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    
    这告诉 Internet Explorer 使用最新的渲染引擎。

通过使用 http-equiv 属性,网页开发者可以在不更改服务器配置的情况下,控制页面的某些行为和设置,提高网页的灵活性和响应性。

响应码

  • 200: 请求成功
  • 404: 请求的资源没有找到
  • 500: 服务器内部出现了错误
  • 302: 重定向

Servlet

Servlet 的作用是处理请求,服务器会把接收到的请求交给 Servlet 处理。

有三种实现方式

  • 实现 javax.servlet.Servlet 接口
  • 继承 javax.servlet.GenericServlet
  • 继承 javax.servlet.http.HttpServlet

Servlet 接口

Servlet 由我们来写,但对象有服务器来实现,并由服务器来调用相应的方法。

生命周期方法

  • init(ServletConfig);
  • service(ServletRequest, ServletResponse) :每次处理请求时都会被调用
  • destroy();

特性:

  • 单例,一个类只有一个对象
  • 线程不安全

如何让浏览器访问 Servlet

  1. 给 Servlet 指定一个 Servlet 路径
  2. 浏览器访问路径

在 web.xml 中配置路径

<servlet>
    <servlet-name>myServlet</servlet-name>
    <servlet-class>org.example.AServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>myServlet</servlet-name>
    <url-pattern>/AServlet</url-pattern>
</servlet-mapping>

新版的 Idea 可以直接利用注解来配置 Servlet

image-20240603180148024

ServletConfig

image-20240601220040984

HttpServlet

重写 doGet()doPost() 方法。不重写会报 405 错误:http.method_get_not_supported.

image-20240602174346615

ServletContext

一个项目只有一个 ServletContext 对象。

  • 在服务器启动时创建对象,服务器关闭时销毁对象

对象的作用是 在整个 Web 应用的动态资源之间共享数据。例如 AServlet 向 ServletContext 对象中保存了一个值,然后在 BServlet 中就可以获取这个值。

操作数据的方法:

  • void setAttribute(String name,Object value)
  • Object getAttribute(String name)
  • void removeAttribute(String name)
  • Enumeration getAttributeNames() 获取所有域属性的名称
  • String getInitParameter(String name) 得到初始化参数

获取资源

  • getRealPath() 获取真实路径
  • getResourceAsStream() 获取资源的路径后,在创建出输入流对象

Response

每次请求服务器时,服务器会把客户端的请求数据封装到 request 对象中,request 就是请求数据的载体。

服务器还会创建 response 对象,这个对象与客户端链接在一起,他可以用来向客户端发送相应。

状态码:

  • 200表示成功,302表示重定向,404表示客户端错,500表示服务器端错

相应头:Content-Type,Refresh,Location

  • void setHeader(String name, String value)

  • void setIntHeader(String name, int value)

  • 发送 302:设置 Location 头,完成重定向

    response.setHeader("Location","/day10/AServlet");
    response.setStatus(302);
    

    image-20240603180650880

  • 定时刷新:设置 Refresh 头

  • 禁止浏览器缓存:Cache-Control、pragma、expires

相应体:

  • response 的两个流

    • 两个流不能同时使用
    • ServletOutputStream,用来发送字节数据
    • PrintWriter,用来发送字符数据
  • 快捷重定向: response.sendRedirect(String location)

Request

获取客户端ip:resquest.getRemoteAddr()

获取请求方式:request.getMethod()

获取请求头

  • String getHeader(String name)
  • int getIntHeader(String name)

获取请求URL

http://localhost:8080/day10/AServlet?username=xxx&password=yyy
  • 获取请求 URL,不包含参数的整个请求路径:String getRequestURL(). 例:http://localhost:8080/day10/AServlet
  • 获取请求 URI,项目名 + Servlet 路径:StringRequestURI(). 例:/day10/AServlet
  • 获取参数部分,问号后面的部分:String getQueryString(). 例:username=xxx&password=yyy
  • 获取 Servlet 路径:String getServletPath(). 例:/AServlet
  • 获取项目名String getContextPath(). 例:/day10
  • 获取服务器端口:String getServerPort(). 例:8080
  • 获取服务器名:String getServerName(). 例:localhost
  • 获取协议:String getScheme(). 例:http

获取请求参数

  • String getParameter(String name) 单值
  • String[] getParameterValues(String name) 多值
  • Map<String,String[]> getParameterMap() 获取所有请求参数

请求转发和请求包含

RequestDispatcher 的主要作用有两种:

  1. 转发(Forward):通过 forward 方法将请求从一个 Servlet 转发到服务器上的另一个资源(如另一个 Servlet、JSP 文件等)。当前 Servlet 可以设置相应头,但不能设置相应体。 留头不留体
  2. 包含(Include):通过 include 方法在响应中包含另一个资源的内容。这样可以在当前响应中组合多个资源的输出,实现资源的复用。 留头又留体
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/CServlet");
// 请求转发
requestDispatcher.forward(req,resp);
// 请求包含
requestDispatcher.include(req,resp);

image-20240605114400102

request 域

Servlet 中三大域对象:request,session,application。都有如下三个方法

  • void setAttribute(String name,Object value)
  • Object getAttribute(String name)
  • void removeAttribute(String name)

属性:Attribute 是 Servlet 和 Servlet 在转发或包含时用来传递值。

请求转发和重定向的区别

  1. 请求转发时一次请求一次响应,而重定向时两次请求两次响应
  2. 请求转发地址栏不变化,而重定向会变化
  3. 请求转发只能转发到本项目其他Servlet(即只能在本服务器),重定向无所谓。
  4. 请求转发时服务器端的行为,只需给出转发的 Servlet 路径。

转发:req.getRequestDispatcher(path).forward(req,resp);

重定向:resp.sendRedirect(req.getContextPath() + path);

重定向不能携带 req 中的信息

URL 编码

在客户端和服务器之间传递中文时,转换成合适的方式:把中文转化成 % 后面跟随两位的 16进制(utf-8编码)

  • URL 编码:URLEncoder.encoder(String s,String encoding)
  • URL 解码:URLDecoder.decode(String s,String encoding)

JSP

内置对象

  • out : 等同于 response.getWriter() ,用来向客户端发送文本数据。
  • request: 即 HttpServletRequest 类的对象
  • response: 即 HttpServletResponse 类的对象
  • application: 即 ServletContext 类的对象
  • session: 即 HttpSession 类的对象
  • pageContext :页面上下文对象。

JSP 的四大域

  • 整个应用程序:application
  • 整个会话:session
  • 一个请求:request
  • 一个 jsp 页面:pageContext

pageContextfindAttribute(String name) 方法在 JSP 开发中非常有用,它提供了一种便捷的方式来查找属性,无需开发者显式指定属性应该在哪个作用域中查找。这个方法会按照一定的顺序在不同的作用域中搜索指定的属性:

  1. Page Scope:首先在页面作用域中搜索属性。
  2. Request Scope:如果在页面作用域中找不到,则继续在请求作用域中搜索。
  3. Session Scope:如果请求作用域中也找不到,且存在会话(Session),则在会话作用域中搜索。
  4. Application Scope:最后在应用程序作用域中搜索。

如果属性在上述任何作用域中被找到,findAttribute 方法会返回该属性的值。如果在所有作用域中都找不到,它将返回 null

Jsp 的作用

jsp:Java server pages。

  • 在原有 html 的基础上添加 java 脚本,构成 jsp 页面。
  • 作为请求发起页面,例如显示表单、超链接。
  • 作为请求结束页面,例如显示数据
  • Servlet:作为请求中处理数据的环节

jsp 的组成

jsp 包含 html,java脚本,Java标签

jsp 中无需创建即可使用的对象一共有九个,被称为 9 大内置对象。例如:request 对象、out 对象

三种java脚本

  • <% ... %> Java 代码片段
  • <%= ... %> Java 表达式,常用于输出
  • <%! ... %> 声明,用来创建类的成员变量和成员方法

还有一种输出方式

<%
    out.print("hello world");
%>

jsp 的注释:<%-- ... --%>

<%@include ... %> : 静态包含

<%taglib ... %>: 导入标签库

动作标签:

  • <jsp:forward>:请求转发
  • <jsp:include xxx> :动态包含,会调用 xxx 的输出结果(形成 xxx.class),区别于 静态包含(<%@include>),静态包含最终只会形成一个 class 文件。
  • <jsp:param>: 用来传递参数。

示例

image-20240607174844952

Servlet 端

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String s1 = req.getParameter("num1");
    String s2 = req.getParameter("num2");

    int num1 = Integer.parseInt(s1);
    int num2 = Integer.parseInt(s2);

    int sum = num1 + num2;

    req.setAttribute("sum",sum);

    // 转发到 result.jsp
    req.getRequestDispatcher("/add/result.jsp").forward(req,resp);
}

form 页面:

<form action="/day10/CServlet" method="post">
    整数1: <input type="text" name="num1"> <br>
    整数2: <input type="text" name="num2"> <br>
    <input type="submit" value="submit">
</form>

result 页面:

<%
    Integer sum = (Integer) request.getAttribute("sum");
    out.print(String.valueOf(sum));
%>

jsp 原理

jsp 其实是一种特殊的 Servlet

  • 当 jsp 页面第一次被访问时,服务器会把 jsp 编译成 java 文件
  • 然后再把 Java 文件编译成 class 文件
  • 然后创建该类对象
  • 最后调用对象的 service() 方法
  • 第二次请求同一 jsp 时,直接调用 service() 方法

image-20240607223406722

先有服务器保存 Cookie 到浏览器,下次浏览器请求服务器时把上一次请求得到的 Cookie 再归还给服务器。

向浏览器添加Cookie:response.addCookit() ,设置的属性(Set-Cookie)

获得浏览器归还的Cookie:resquest.getCookie(), 注意是从 request 拿到的。

Request: 浏览器发给服务器的

response: 服务器发给浏览器的

maxAge

Cookie 的最大生命,即 Cookie 可保存在硬盘中的最大时长,单位为秒。cookie.setMaxAge(60)

  • maxAge > 0: 浏览器会把 cookie 保存到硬盘上。
  • maxAge < 0: cookie 只会在浏览器内存中存在,关闭浏览器是,cookie 消失
  • maxAge = 0: 浏览器会马上删除这个 cookie

path

cookie 的 path 默认值:当前访问路径的父路径。

  • 例如当访问 /day11/jsps/a.jsp 时,path 默认值为 /day11/jsps

当访问路径包含 cookie 路径时,request 中会有对应的 cookie 值。

image-20240609171505350

Session

会话(Session),会话范围是某个用户从首次访问服务器开始,到该用户关闭浏览器结束。

session 是服务器端对象,保存在服务器端。

HttpSession 是 Servlet 三大域之一(request,seesion,application(ServletContext) ).

  • void setAttribute(String name, Object value)
  • Object getAttribute(String name)
  • void removeAttribute(String name)

服务器会为 每个用户当前浏览器 创建一个 session 对象,直到关闭浏览器结束

  • Servlet:request.getSession()
  • Jsp: session 是内置对象,可以直接使用。

Session 原理

当访问 jsp 文件(内置 Session 对象)或者在 Servlet 中调用 response.getSession() 方法,会在 Cookie 中创建 JSESSIONID , JSESSIONID 是会话的唯一标识

其他方法

  • String getId(): 获取 sessionId
  • void invalidate(): 让 seesion 失效。
  • int getMaxInactiveInterval(): 获取 session 的最大不活动时间,默认为 30 分钟。

session 依赖 Cookie,目的是让客户端发送请求时归还 sessionId,这样才能找到对应的 session。

response.encodeURL(String url)

  • 该方法会对 URL 进行智能的重写
  • 如果没有 cookie 会重写 url
  • 有 cookie 中的 sessionId 则不重写
  • 示例:/day10/AServlet;jsessionid=E9E51CDF937140AD0A8B231622C493FB
  • 使得每个 URL 都有当前的 sessionId

MVC

MVC

  • M:model 模型
  • V:view 视图
  • C:controller 控制器

JavaWeb 三层框架

Web 层:与 Web 相关的内容(Servlet,JSP,Servlet相关API:request、response、session、ServletContext)

业务逻辑层:业务对象(Service)

数据层:操作数据库(DAO,Data Access Object)

实体类:JavaBean

image-20240613144535460

登陆注册项目

功能:

  • 注册
  • 登陆

分析

JSP:

  • login.jsp:登陆表单
  • regist.jsp:注册表单
  • index.jsp:主页(只有登陆成功才能看到)

Servlet:

  • LoginServlet
  • RegistServlet

Service:

  • UserService:与用户相关的业务类

Dao:

  • UserDao:与用户相关的数据类。

Domain:

  • User (对应数据库,还要对应所有表单)
    • username
    • password
    • verifyCode

数据库设计(暂时用 xml 顶替)

user.xml

<users>
    <user username="xxx" password="yyy" verifyCode="abcd"/>
</users>

注册

regist.jsp

  • 第一步:提交表单

RegistServlet(作为服务员,拿到前端的数据,调用service层中的方法)

  • 封装表单数据,封装到 User 对象
  • 调用 UserService 中的注册方法
    • 注册成功
    • 注册失败,则抛出异常,错误信息保存到 request 域中,并转发到注册页面。

UserService#regist()

  • 注册失败会抛出一个自定义的异常,添加异常信息。
  • 校验用户名是否已被注册(通过用户名查询用户)
    • 已被注册,抛出异常(“用户名已被注册”)
  • 添加用户

UserDao:通过业务分析,得到结果:需要实现两个方法

  • 按用户名查询用户对象
  • 插入一个用户到数据库中

分析上是正着分析:表单,Servlet,Service,Dao

实际开发:表单,Dao,Service,Servlet

EL 表达式:有就显示,没有就不显示,按照 scope 逐层搜索。

修改1:实现 Dao 工厂

  1. 把 UserDao 修改为接口,然后把原来的 UserDao 修改类名为 UserDaoImpl
  2. 修改 UserService 中对 UserDao 的实例化:private UserDao userDao = DaoFactory.getUserDao();
  3. 创建 DaoFactory,提供 getUserDao() 方法

修改2: 实现数据库连接

实现 JdbcUserDaoImpl 继承 UserDao 接口。

采用 ORM 对象关系映射,把查询到数据封装为 User 对象,再返回。

在尝试调用一个对象的方法之前,必须确保该对象不是 null。如果你试图关闭一个未初始化或已经设置为 null 的对象,如 pstmt 或 con,直接调用 close() 方法会抛出 NullPointerException。通过检查对象是否为 null 可以避免这种异常的发生。

JDBC

Jdbc 协议格式:jdbc:mysql://localhost:3306/数据库名称

创建连接:Connection con = DriverManager.getConnection(url,username,password);

语句:Statement stmt = con.createStatement()

  • 增删改:stmt.excuteUpdate(sql) ,返回影响的行数
  • 查询:stmt.excuteQuery(sql),返回查询结果 ResultSet

事务

事务的四大特性:原子性是一个动作,一致性是一个结果。

  • 原子性:事务中所有操作是不可分割的原子单位。事务中所有操作要么全部执行成功,要么全部执行失败。
  • 一致性:事务执行前后,数据库的状态必须从一个一致性状态变到另一个一致性状态。
  • 隔离性:并发操作中,不同事务之间应该隔离开来,使得每个事务不会相互干扰。
  • 持久性:一旦事务提交成功,事务中的所有数据操作都会被持久化到数据库中。

并发事务问题

读问题:

  • 读脏数据:读到另一个事务的尚未提交的修改。
  • 不可重复读:在一个事务内,多次读取同一数据不一样。(因为另一个事务对该数据做了修改)
  • 幻读:对同一张表的两次查询不一样。(因为另一事务插入了一条记录)

事务的四大隔离级别

  • 串行化:三种读问题都能解决,性能最差。
  • 可重复读:处理了读脏数据,不可重复读
  • 读已提交数据:处理了读脏数据
  • 读未提交数据:啥也不处理(

事务的应用

mysql中的事务

开始事务:start transaction

持久化事务:commit

回滚事务:rollback

jdbc中的事务

同一事务中所有的操作,都必须在用 同一个 Connection 对象。

用这样的操作保证事务的 原子性: 要么全做,要么全不做。

try{
    con.setAutoCommit(false); // 开启事务
    ...
    con.commit(); // 持久化事务
} catch() {
    con.rollback(); // 回滚事务
}

用这样的操作保证事务的 隔离性 : 通过确保只有当前线程的事务连接(con)才会被关闭,而其他连接(connection)如果不是当前事务的连接,则不会被关闭。这种处理方式确保了在同一个事务上下文中,操作的是同一个数据库连接,避免了多线程环境下的连接混淆,从而保证了事务的隔离性。

public static void releaseConnection(Connection connection) throws SQLException {
    Connection con = tl.get();//获取当前线程的事务连接
    if(connection != con) {//如果参数连接,与当前事务连接不同,说明这个连接不是当前事务,可以关闭!
        if(connection != null &&!connection.isClosed()) {//如果参数连接没有关闭,关闭之!
            connection.close();
        }
    }
}

BaseServlet

我们希望在一个 Servlet 中可以有多个请求处理方法

  • 客户端发送请求时,必须多给出一个参数,来说明调用的方法
  • 请求处理方法的签名必须与 service 相同,即返回值和参数,以及声明的异常都相同。

拿到方法名之后,我们可以通过反射的方法调用 service 中的方法。

  • 反射调用:method.invoke(this,req,resp)
  • 正常调用:this.addUser(req,resp)

ThreadLocal

ThreadLocal 内部是一个 Map

class TL<T> {
    private Map<Thread,T> map = new HashMap<Thread,T>();

    // 用当前线程做 key
    public void set(T data) {
        map.put(Thread.currentThread(),data);
    }

    public T get() {
        return map.get(Thread.currentThread());
    }

    public void remove() {
        map.remove(Thread.currentThread());
    }
}

DbUtils

Commons-dbutils 中 QueryRunner : 两个主要的方法是update和query,它们分别用于执行更新操作和查询操作。

update 方法

update方法用于执行数据库更新操作,例如INSERTUPDATEDELETE语句。它的主要作用是执行不返回结果集的SQL语句,并返回受影响的行数。

query 方法

query方法用于执行数据库查询操作,并将结果集转换为相应的Java对象。它需要传入一个结果集处理器(ResultSetHandler),用来定义如何将结果集中的数据映射到Java对象中。

  • sql:要执行的SQL查询语句。
  • handler:结果集处理器,用于将结果集转换为所需的Java对象。常用的处理器有BeanListHandlerScalarHandlerMapListHandler等。
  • params(可选):SQL语句中的参数。

返回值

  • 返回值是泛型类型,由结果集处理器决定。例如,BeanListHandler会返回一个List,其中每个元素是一个映射到JavaBean的对象。

常用的结果集处理器

  • BeanListHandler:将结果集中的每一行映射到一个JavaBean对象,并返回一个包含这些对象的列表。
  • ScalarHandler:用于处理单行单列的结果,例如聚合函数的结果。
  • MapListHandler:将结果集中的每一行映射到一个Map对象,并返回一个包含这些映射的列表。

顾客管理系统项目

添加用户

CustomerServlet#addCustomer()

  • 封装表单数据到 Customer 对象
  • 补:uuid
  • 传递给 Service 层
  • 保存成功信息,转发到 msg

用这种方法传递 method 参数,使得用户不可见:

<form action="${pageContext.request.contextPath}/CustomerServlet" method="post">
    <input type="hidden" name="method" value="addCustomer">

编辑用户

先拿到顾客的信息(拿到 cid),然后做修改(做回显),再提交表单修改信息。

如何从前端拿到参数:(无论是从 URL 还是从表单中)

  • req.getParameter(String name)

获取当前Web应用程序的上下文路径的表达式。

  • ${pageContext.request.contextPath}

要将 List<Customer> params 转化成 Object[]类型的,QueryRunner 下的 query() 方法才能接受,具体方法如下:

  • params.toArray();

监听器

在 IDEA 中实现一个监听类

  • 写一个监听器类:实现某个监听器接口;
  • 注册:直接在类上进行标注 @WebListener 注解

事件源:三大域。

  • ServletContext
    • 生死监听
    • 属性监听
  • HttpSession
    • 生死监听
    • 属性监听
  • ServletRequest
    • 生死监听
    • 属性监听

生命周期:

  • ServletContext:应用程序启动时创建,关闭时销毁。
  • HttpSession:调用 request.getSession() 时创建,超时/关闭浏览器 会话 结束。
  • ServletRequest:动态请求时创建,响应后销毁。

过滤器

在 IDEA 中实现一个过滤器

  • 写一个过滤器类:实现 Filter 接口
  • 注册:直接在 类上 (而不是方法上)进行标注 @WebFilter(urlPatterns = "/*", dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD}) 注解

Java Servlet API 中的 Filter 接口用于在请求达到 Servlet 或响应发送给客户端之前,对它们进行预处理和后处理。Filter 可以改变请求和响应,并且能够阻止请求被进一步处理。

过滤器的 doFilter() 方法是 Filter 接口中最关键的方法,它负责实现过滤功能。

void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    // 做一下预处理操作 ...
    System.out.println("预处理");

    // 将请求和响应传递给下一个过滤器或最终的目标资源
    chain.doFilter(request,response);

    // 后处理操作 ...
    System.out.println("后处理");

}
  • FilterChain chain:过滤器链,负责管理一个或多个过滤器的执行,以及过滤器后的资源访问。

四种拦截方式:

  • REQUEST(默认)
  • FORWARD
  • INCLUDE
  • ERROR

filterDemo

分ip统计网站的访问次数

在过滤器中做一些预处理,用 Map<String,Interger> map 来记录 ip 和访问次数。

  1. map 的创建:在服务器启动时完成创建 map,并存放到 ServletContext 中
  2. 来一个 ip ,记录一下。
  3. 写一个页面展示 map 的数据。

用监听器来写创建 map:它允许你在应用程序启动时进行初始化操作,并且确保在任何 Servlet 或 Filter 使用 map 之前它已经被创建并存放在 ServletContext 中。

  • 实现:ServletContextListener 接口。

Ajax

AJAX(Asynchronous JavaScript and XML):异步的 js 和 xml

  • 发一个请求后,无需等待服务器的响应,然后就可以发第二个请求。
  • 可以使用 js 接受服务器的响应,然后使用 js 来局部刷新。

发送异步请求

GET请求

在前端可以这样写:

<script type = "text/javascripts">
// 文档加载完毕后执行
window.onload = function() { 
    // 给按钮你的点击时间注册监听
    var btn = document.getElementById("btn");
    btn.onclick = function() {

        // ajax 异步 get 请求
        var xhr = new XMLHttpRequest();
        xhr.open("GET", "https://api.example.com/data", true);
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4 && xhr.status === 200) {
                var response = JSON.parse(xhr.responseText);
                console.log(response);
            }
        };
        xhr.send();
    }
}
</script>

POST 请求

要设置 Content-Type 请求头

  • xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");

参数:xhr.send(参数)

XStream

XStream 可以 JavaBean 序列化为 XML,或将 XML 反序列化为 Java 对象。

  • 设置根节点别名:xstream.alias("root", RootClass.class);
  • 设置类别名:xstream.alias("user", User.class);
  • 设置字段别名:xstream.aliasField("name", User.class, "username");
  • 忽略特定成员:例:xstream.omitField(User.class, "password");

JSON

语法

属性名必须使用 双引号 扩起来。

属性值:

  • null
  • 数值
  • 字符串
  • 布尔值:true 或 flase
  • 数组:使用 [ ] 扩起来
  • 对象:使用 { } 扩起来

核心类:

  • JSONObject:相当于 Map
  • JSONArray:相当于 List

Ajax 执行过程

image-20240701204623457

省市联动

dao:提供两个方法

  • 查询所有省
  • 通过省名称查询指定的市

servlet:两个方法

  • 把所有省转化成 json,发送给客户端
  • 获取省名称,查询所有市,转化为json,发送给客户端

web:

  • 访问 servlet,得到所有省,显示在 <select id="province">
  • <select id="province"> 添加 onchange 事件监听,获取选择的省名称,访问 servlet ,得到所有市,显示在 <select id="city">

使用 outerHTML 可以获取或设置整个元素(包括其标签和内容)的 HTML。

// 查询所有省
fetch('<c:url value='/ProvinceServlet'/>')
    .then(response => response.json())
    .then(allProvince => {
        for (const province of allProvince) {
            // 创建 <option> 元素
            var optionEle = document.createElement("option");
            optionEle.value = province.pid; // 实际值
            optionEle.innerHTML = province.name; // 显示值

            // 添加到下拉框中
            document.getElementById("province").appendChild(optionEle);
        }
    });

// 给 province 添加 onchange 监听
document.getElementById("province").onchange = function () {

    fetch('<c:url value='/CityServlet'/>', {
            method: "POST",
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded;'
            },
            body: "pid=" + this.value,
        })
        .then(response => response.json())
        .then(citys => {
            // 清空原有的 city
            var cityList = document.getElementById("city");
            cityList.innerHTML = cityList.options[0].outerHTML;

            for (const city of citys) {
                // 创建 <option> 元素
                var optionEle = document.createElement("option");
                optionEle.value = city.cid; // 实际值
                optionEle.innerHTML = city.name; // 显示值

                // 添加到下拉框中
                cityList.appendChild(optionEle);
            }
        });
};

图书商城

用户

用户模块

  • 注册
  • 邮箱激活
  • 登陆
  • 退出

分类模块

  • 查看所有分类

图书模块

  • 查询所有图书
  • 按分类查询图书
  • 查询图书详细(按 id 查询)

购物车模块

  • 添加购物车条目
  • 清空所有条目
  • 删除特定条目
  • 我的购物车(按用户查询购物车)

订单模块

  • 生成订单
  • 我的订单(按用户查询订单)
  • 按 id 查询订单
  • 确认收货
  • 付款功能(只是跳转到银行页面)
  • 付款回调功能(由银行来调用这个方法,表示付款成功)

管理员后台

管理员

  • 登录

分类管理

  • 添加分类
  • 查看所有分类
  • 删除分类
  • 按 id 查询
  • 修改分类

图书管理

  • 查看所有图书

  • 按 id 查询

  • 添加图书(上传图片)

  • 删除图书

  • 修改图书

订单模块

  • 查询所有订单
  • 按状态查询订单
  • 发货

注册 和 登录

Servlet

  • 用来传送 对象,参数
  • 然后调用 Service 的具体方法
  • 处理 Service 中的异常
  • 返回客户端

Service 用来处理具体的逻辑。

UserServlet

  1. 封装表单数据到 User 对象中
  2. 补全 uid,code(激活码)
  3. 输入校验(不访问数据库)
    • 保存错误信息到 request
    • 保存 form 到 request(回显)
    • 转发会到 regist.jsp
  4. 调用 service 的注册方法
    • 保存错误信息到 request
    • 保存 form 到 request(回显)
    • 转发会到 regist.jsp
  5. 发邮件
  6. 保存成功信息到 request
  7. 转发到 msg.jsp

UserService

  1. 校验 User 的 username 是否被注册
  2. 校验 User 的 email 是否被注册
  3. 把 User 存到数据库

激活

邮箱打开激活链接:

  • 拿到激活码
  • 通过激活码查到用户
  • 把用户的状态置为 true
  • 然后给用户链接,让用户跳转到登陆页面

登录

登录界面:

  • Servlet 拿到用户名和密码
  • 调用 service 中的登录方法
    • 查询用户名是否存在
    • 检验密码是否正确
    • 若错误,则爆出异常,返回错误信息到 msg
  • 页面跳转到 main.jsp

购物车

登录时创建购物车 cart ,存放在 session 中。

添加到购物车

  • 从 session 中拿到购物车
  • 获取参数:bid 和 count
  • 使用 BookService 通过 bid 得到 book 对象
  • 创建购物车条目
  • 返回 list.jsp

订单

我的订单

  • 获取参数 uid
  • 通过 uid 来查询订单,返回一个 List<Order>
  • 返回 order/list.jsp

因为在 orderitem 表中包含图书和数量

  • 图书:bid ,但在 javabean 中存取的是 Book 对象,所以只用 bid 是无法填充 orderitem 的

我们采用多表查询,然后用 MapHandler 拿到对应的数据,然后使用 toBean 方法,进行对 Book 和 orderitem 对象进行封装。

具体的:

private void loadOrderItem(Order order) {
    String sql = "select * from orderitem,book where orderitem.bid = book.bid and oid = ?";
    try {
        List<Map<String, Object>> mapList = qr.query(sql, new MapListHandler(), order.getOid());

        // 利用 mapList 生成两个对象 book 和 orderItemList
        List<OrderItem> orderItemList = new ArrayList<OrderItem>();
        for(Map<String,Object> map: mapList) {
            OrderItem orderItem = CommonUtils.toBean(map,OrderItem.class);
            Book book = CommonUtils.toBean(map,Book.class);
            orderItem.setBook(book);
            orderItemList.add(orderItem);
        }
        order.setOrderItemList(orderItemList);
    } catch (SQLException e) {
        throw new RuntimeException(e);
    }
}

后台

删除分类

  • list.jsp 传递 cid
  • 去 servlet 删除
    • 若分类下还有图书,则不能删除
    • 爆异常,返回 msg.jsp
  • 返回 list.jsp

修改分类名称

在 mod.jsp 中拿到当前修改的分类

  • list.jsp 页面拿到当前的 cid
  • 去 servlet ,通过 cid 查询分类
  • 保存分类到 req
  • 转发到 mod.jsp

修改名称

  • 从 req 中拿到 category
  • 回显
  • 提交表单 到 servlet
  • 修改:检查是否有重名
    • 爆出异常,返回 msg.jsp
  • 返回 list.jsp

添加图书

上传三步

  • 创建工厂
  • 创建解析器
  • 解析request 得到的表单字段

把表单字段封装到 Book 对象中

调用 service 的方法

返回 list.jsp

删除图书

book 表与 orderitem 有关联关系

删除图书不是真的在数据库表中删除记录,而是给 book 表添加一个 del 字段,表示是否删除。

  • 修改 bookDao 中相关的方法
  • 修改 Book 类,添加 del 属性

泛型

泛型类:

class A<T> {
    private T t;
    public T fun1() {
        return t;
    }
    public void fun2(T t) {
        this.t = t;
    }
}

泛型方法:

// 定义一个泛型方法
public <T> T fun(T t1) {
    // 简单返回传入的参数
    return t1;
}

在创建实例时,为泛型的类型变量赋值。

通配符 ?

  • ?:无边界通配符,表示可以是任何类型。
  • ? extends T:有上边界的通配符,表示类型必须是 T 或其子类型。
  • ? super T:有下边界的通配符,表示类型必须是 T 或其父类型。

注解

注解的作用目标:

  • 方法
  • 构造器
  • 参数
  • 局部变量

注解的属性

定义属性:

@interface MyAnno {
    int age();
    String name();
}

使用注解给属性赋值:

@MyAnno(age=100,name="ZhangSan")
public class Demo {

}

设置默认值:int age() default 100;

设置总用目标的限定(限定在成员变量、方法..上)

@Target(value={ElementType.TYPE,EElementType.METHOD,ElementType.FIELD})

反射注解

  1. 要求 * 注解的保留策略必须是RUNTIME

  2. 反射注解需要从作用目标上返回 * 类上的注解,需要使用Class来获取 * 方法上的注解,需要Method来获取 * 构造器上的注解,需要Construcator来获取 * 成员上的,需要使用Field来获取

Class

Method、Constructor、Field:AccessibleObject

它们都有一个方法: * Annotation getAnnotation(Class),返回目标上指定类型的注解! * Annotation[] getAnnotations(),返回目标上所有注解!


Last update: August 2, 2024
Created: June 3, 2024