JavaWeb

文章目录

JavaWeb 开发中的关键概念和技术

  1. Servlet:Servlet 是 Java EE 平台的一部分,用于处理客户端发来的请求并生成响应。Servlet 可以响应各种类型的请求,如 HTTP 请求。

  2. JSP(JavaServer Pages):JSP 是一种基于 Java 技术的服务器端动态网页技术,它允许开发人员将 Java 代码和特定标记(如 HTML 或 XML)混合在一起,以生成动态网页内容。

  3. JSTL(JavaServer Pages Standard Tag Library):JSTL 是一组 JSP 自定义标签,用于简化 JSP 页面的开发,提供了用于流程控制、条件判断、循环、格式化等常见任务的标准化标签。

  4. Java EE 容器:Java EE 容器提供了对 Java EE 应用程序的运行时环境和支持,包括 Servlet 容器(如 Apache Tomcat、Jetty)和 Java EE 应用服务器(如 WildFly、GlassFish)。

  5. MVC(Model-View-Controller):MVC 是一种软件架构模式,常用于构建 Web 应用程序。在 JavaWeb 开发中,Servlet 和 JSP 通常被用作控制器(Controller)和视图(View),而业务逻辑则放在模型(Model)中。

  6. ORM(Object-Relational Mapping):ORM 是一种将对象模型和关系数据库之间的数据映射的技术。在 JavaWeb 开发中,常用的 ORM 框架包括 Hibernate、JPA(Java Persistence API)等,用于简化数据持久化操作。

  7. RESTful Web 服务:RESTful 是一种 Web 服务架构风格,基于 HTTP 协议,使用标准的 HTTP 方法(如 GET、POST、PUT、DELETE)进行通信。在 JavaWeb 开发中,可以使用 JAX-RS(Java API for RESTful Web Services)实现 RESTful Web 服务。

  8. 安全性:JavaWeb 应用程序通常需要考虑安全性,包括身份验证(Authentication)、授权(Authorization)、数据加密、防止跨站脚本攻击(XSS)等。

Web 应用程序分为静态 Web 应用程序和动态 Web 应用程序

静态 Web 应用程序:

定义

静态 Web 应用程序是由服务器直接提供给客户端的 HTML、CSS、JavaScript 和其他静态文件的集合。这些文件在客户端请求时被发送到浏览器,然后由浏览器解析和呈现。

特点

  1. 内容固定不变:静态 Web 应用程序的内容在服务器端生成后不会改变,除非手动编辑并重新部署文件。
  2. 不需要服务器端处理:静态 Web 应用程序不需要服务器端处理或数据库查询。服务器仅负责向客户端提供文件。
  3. 速度快:由于没有服务器端处理,静态 Web 应用程序通常加载速度较快。

适用场景

  • 简单的网站或展示性网站。
  • 不需要与用户交互或根据用户输入动态生成内容的网站。

动态 Web 应用程序:

定义

动态 Web 应用程序使用服务器端处理和数据库查询等技术,根据用户请求生成动态内容并将其发送给客户端。通常使用服务器端脚本语言(如 PHP、Python、Ruby、Java 等)来处理请求,并与数据库交互以获取或修改数据。

特点

  1. 内容动态生成:动态 Web 应用程序可以根据用户请求或其他数据生成动态内容,并在服务器端进行处理。
  2. 与数据库交互:动态 Web 应用程序通常需要与数据库交互,以存储和检索数据。
  3. 交互性强:由于内容是动态生成的,动态 Web 应用程序可以根据用户的输入或行为做出响应,具有更强的交互性。

适用场景

  • 社交网络、电子商务平台等需要与用户交互并动态生成内容的网站。
  • 需要基于用户输入或其他数据进行复杂计算或处理的网站。

1.基本概念

1.前言

web开发:

  • web,网页的意思 , www.baidu.com

  • 静态web

    • html,css
    • 提供给所有人看的数据始终不会发生变化!
  • 动态web

    • 淘宝,几乎是所有的网站;
    • 提供给所有人看的数据始终会发生变化,每个人在不同的时间,不同的地点看到的信息各不相同!
    • 技术栈:Servlet/JSP,ASP,PHP。
  • 在Java中,动态web资源开发的技术统称为JavaWeb。

2.web应用程序

web应用程序:可以提供浏览器访问的程序。

  • a.html.b.html...多个web资源,这些web资源可以被外界访问,对外界提供服务。

  • 我们能访问到的任何一个页面或者资源,都存在于这个世界的某一个角落的计算机上。

  • URL

  • 这些统一的web资源会被放在同一个文件夹下,web应用程序-->Tomcat:服务器。

  • 一个web应用由多部分组成 (静态web,动态web)。

    • html,css,js
    • jsp,servlet
    • Java程序
    • jar包
    • 配置文件 (Properties)
  • web应用程序编写完毕后,若想提供给外界访问:需要一个服务器来统一管理;

3.静态web

  • *.htm, *.html,这些都是网页的后缀,如果服务器上一直存在这些东西,我们就可以直接进行读取。通络;
  • 静态web存在的缺点:
    • Web页面无法动态更新,所有用户看到都是同一个页面;
      • 轮播图,点击特效:伪动态;
      • JavaScript [实际开发中,它用的最多];
      • VBScript;
    • 它无法和数据库交互(数据无法持久化,用户无法交互)。

4.动态web

页面会动态展示: "Web的页面展示的效果因人而异";

缺点:

  • 加入服务器的动态web资源出现了错误,需要重新编写后台程序 ,重新发布;
    • 停机维护。

优点:

  • Web页面可以动态更新,所有用户看到都不是同一个页面;
  • 它可以与数据库交互 (数据持久化:注册,商品信息,用户信息...);

2.web服务器

1.技术讲解

ASP(Active Server Pages):

  • 微软:国内最早流行的就是ASP;

  • 在HTML中嵌入了VB的脚本, ASP + COM;

  • 在ASP开发中,基本一个页面都有几千行的业务代码,页面极其换乱,维护成本高!

  • C#

  • IIS

    html 复制代码
    <h1>
        <h1></h1>
            <h1>
                <h1>
                    <h1>
                    <h1>
                </h1>
                <%
                System.out.println("hello")
                %>
                <h1>
                </h1>
                    <h1></h1>
               <h1>
           </h1>

php(Hypertext Preprocessor):

  • PHP开发速度很快,功能很强大,跨平台,代码很简单 (70% , WP)
  • 无法承载大访问量的情况(局限性)

JSP (JavaServer Pages) / Servlet :

B/S:浏览和服务器

C/S: 客户端和服务器

  • sun公司主推的B/S架构

  • 基于Java语言的 (所有的大公司,或者一些开源的组件,都是用Java写的)

  • 可以承载三高问题带来的影响;

  • 语法像ASP , ASP-->JSP , 加强市场强度;

  • 工作原理

    • 当客户端发出请求时,Web 服务器将请求发送到 Servlet 容器。

    • Servlet 容器根据请求的 URL 找到相应的 Servlet,并调用其 service() 方法。

    • Servlet 的 service() 方法根据请求的类型(GET、POST 等)执行相应的逻辑,并生成响应。

    • 如果 Servlet 需要生成动态内容,它可以调用其他 Java 类或组件,并将生成的 HTML 或其他数据发送回客户端。

...

2.web服务器

  • 服务器是一种被动的操作,用来处理用户的一些请求和给用户一些响应信息;

  • IIS(Internet Information Services)

    • 微软开发的用于 Windows 服务器操作系统的 Web 服务器应用程序; ASP...,Windows中自带的
  • Tomcat

  • Apache Tomcat 是一个开源的 Java Servlet 容器,也可以作为一个 Web 服务器来使用。它由 Apache 软件基金会开发和维护,是一个流行的用于托管 Java Web 应用程序的服务器。

  • Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,因为Tomcat 技术先进.性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。

  • Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。对于一个Java初学web的人来说,它是最佳的选择。

  • Tomcat 实际上运行JSP 页面和Servlet。Tomcat最新版本为9.0。

...

工作3-5年之后,可以尝试手写Tomcat服务器;

  • 下载tomcat:
    1. 安装 or 解压;
    2. 了解配置文件及目录结构;
    3. 这个东西的作用。

3.Tomcat

1. 安装tomcat

2.Tomcat启动和配置

  • 文件夹作用:
  • 启动。关闭Tomcat
  • 访问测试:http://localhost:8080/

  • 可能遇到的问题:

    1. Java环境变量没有配置;
    2. 闪退问题:需要配置兼容性;
    3. 乱码问题:配置文件中设置。

3.配置

  • 可以配置启动的端口号
    • Tomcat的默认端口号为:8080
    • mysql:3306
    • http:80
    • https:443
xml 复制代码
<Connector port="8081" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
  • 可以配置主机的名称
    • 默认的主机名为:localhost->127.0.0.1
    • 默认网站应用存放的位置为:webapps
xml 复制代码
<Host name="www.subeily.com"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
高难度面试题 -- 请你谈谈网站是如何进行访问的!
  1. 输入一个域名;回车

  2. 检查本机的 C:\Windows\System32\drivers\etc\hosts配置文件下有没有这个域名映射;

  3. 有:直接返回对应的ip地址,这个地址中,有我们需要访问的web程序,可以直接访问

    java 复制代码
    127.0.0.1       www.subeily.com
  4. 没有:去DNS服务器找,找到的话就返回,找不到就返回找不到;

  1. 可以配置一下环境变量(可选性)。
  1. DNS 解析

  2. 建立 TCP 连接

  3. 发送 HTTP 请求

  4. 服务器处理请求

  5. 服务器发送响应

  6. 接收并渲染响应

  7. 关闭 TCP 连接

4.发布一个web网站

  • 不会就先模仿:

    • 将自己写的网站,放到服务器(Tomcat)中指定的web应用的文件夹(webapps)下,就可以访问了。
  • 网站应该有的结构

java 复制代码
--webapps :Tomcat服务器的web目录
    -ROOT
    -books :网站的目录名
        - WEB-INF
            -classes : java程序
            -lib:web应用所依赖的jar包
            -web.xml :网站配置文件
        - index.html 默认的首页
        - static 
            -css
                -style.css
            -js
            -img
         -.....
  • HTTP协议 : 面试

  • Maven:构建工具

    • Maven安装包
  • Servlet 入门

    • HelloWorld!
    • Servlet配置
    • 原理

4.Http

1.什么是HTTP

HTTP(超文本传输协议)是一个简单的请求-响应协议,它通常运行在TCP之上。

  • 文本:html,字符串 ....
  • 超文本:图片,音乐,视频,定位,地图.......
  • 80

HTTP工作流程

  1. 客户端发送请求:客户端(例如Web浏览器)向服务器发送HTTP请求。请求通常包括请求方法(如GET、POST等)、资源的URL、HTTP版本以及其他相关的元数据,例如请求头部。

  2. 服务器处理请求:服务器接收到请求后,根据请求的内容执行相应的操作。这可能包括读取文件、查询数据库或执行其他逻辑处理。

  3. 服务器发送响应:服务器生成一个HTTP响应,包括响应状态码、响应头部和响应体。响应状态码指示请求的结果(例如200表示成功,404表示未找到,500表示服务器错误等)。

  4. 客户端接收响应:客户端接收到服务器发送的HTTP响应。根据响应的内容,客户端可能会更新页面内容、显示错误信息或执行其他操作。

  5. 连接关闭:一旦完成响应,连接通常会被关闭,除非使用了持久连接(例如HTTP/1.1中的Keep-Alive)。

HTTP还支持其他功能,例如缓存、Cookie、认证等。HTTP协议的版本有多种,从最早的HTTP/0.9到当前的HTTP/2和HTTP/3,每个版本都有不同的特性和改进。

Https:安全的

  • 443

2.两个时代

  • http1.0

    • HTTP/1.0:客户端可以与web服务器连接后,只能获得一个web资源,断开连接
  • http2.0

    • HTTP/1.1:客户端可以与web服务器连接后,可以获得多个web资源。'
  • 区别:

    1. 持久连接(Keep-Alive)
    • HTTP/1.0:默认情况下,每个HTTP请求/响应都需要建立一个新的TCP连接。这导致了频繁的连接建立和关闭,增加了延迟和资源消耗。
    • HTTP/1.1:引入了持久连接的概念,即在单个TCP连接上可以发送多个HTTP请求和响应。这减少了连接建立和关闭的次数,提高了性能。
    1. 管道化(Pipeline)
    • HTTP/1.0:不支持管道化,每个请求必须等待上一个请求的响应完成后才能发送下一个请求。

    • HTTP/1.1:支持管道化,允许客户端在不等待响应的情况下发送多个请求。这减少了请求和响应之间的等待时间,提高了效率。

      • 存在主要的问题是"Head-of-Line Blocking",即如果服务器不能保证按照请求的顺序返回响应,那么就可能会出现某个响应被阻塞,导致整个管道化的请求-响应流程受阻。

        所以 HTTP/1.1的管道化并没有被广泛采用。后续的HTTP/2协议引入了更加成熟的多路复用(Multiplexing)机制,从根本上解决了HTTP/1.1管道化存在的问题。

    1. 缓存控制
    • HTTP/1.0 :缓存控制较为简单,通常使用Expires头字段指定过期时间。

    • HTTP/1.1 :引入了更灵活和精细的缓存控制机制,包括Cache-Control头字段,可以指定缓存的行为,如缓存有效时间、强制重新验证等。

      • HTTP/1.1中常用的缓存控制指令:

        1. public:表示响应可以被任何缓存(包括客户端和中间代理服务器)缓存。

        2. private:表示响应只能被单个用户的浏览器缓存,不应被共享缓存(如代理服务器)缓存。

        3. no-cache:表示缓存必须在使用之前重新验证其有效性,即缓存存储副本,但在使用之前必须通过服务器进行重新验证。

        4. no-store:表示不允许缓存响应的任何部分,即缓存不得存储任何有关该响应的信息。

        5. must-revalidate:表示缓存必须在使用之前重新验证其有效性,如果缓存的响应已过期,则必须向源服务器发送请求以获取新的响应。

        6. max-age=:表示缓存可以被重用的最大时间(以秒为单位)。

        7. s-maxage= :类似于max-age,但仅适用于共享缓存,而不适用于私有缓存。

        8. max-stale[=]:表示即使缓存的响应已过期,客户端也可以接受一个最多已过期指定秒数的响应。

        9. min-fresh=:表示客户端希望获取一个在指定秒数内仍保持新鲜的响应。

      • 协商缓存和强制缓存------用于确定是否从缓存中获取响应的机制。

        1. 强制缓存

        强制缓存通过Cache-ControlExpires头字段来实现。当客户端首次请求资源时,服务器会返回响应,并在响应头中设置缓存规则,告诉客户端在一段时间内可以直接从缓存中获取该资源,而不必再次请求服务器。如果缓存有效,客户端将直接使用缓存中的资源,而不发送请求到服务器。
        2. 协商缓存

        协商缓存通过验证资源的新鲜度来决定是否从缓存中获取响应。客户端发送带有条件的请求到服务器,服务器根据条件来判断资源是否发生了变化。如果资源未发生变化,服务器返回304 Not Modified状态码,告诉客户端可以使用缓存中的资源;如果资源已经发生了变化,服务器会返回新的资源内容。协商缓存通常使用If-Modified-SinceIf-None-Match等头字段来实现。

    1. 主机头部(Host Header)
    • HTTP/1.0:不支持主机头部字段,因此同一个服务器上的不同网站必须使用不同的IP地址。
    • HTTP/1.1 :引入了主机头部字段,允许在同一台服务器上托管多个域名,从而支持虚拟主机的概念。
      • 指示客户端想要访问的目标主机(服务器)的域名信息。在一个物理服务器上可能托管多个虚拟主机(多个网站),通过主机头部可以告诉服务器应该将请求发送到哪个具体的虚拟主机上。
      • 格式:Host: www.example.com
    1. 错误处理
    • HTTP/1.0:错误处理相对简单,主要通过状态码来指示错误类型。

    • HTTP/1.1:引入了更多的错误状态码和机制,以提供更详细的错误信息和处理方式。

      • 常见状态码

        • 2xx:成功。表示请求已成功被服务器接收、理解、并接受。

        • 3xx:重定向。表示需要进一步的操作以完成请求。

        • 4xx:客户端错误。表示服务器无法处理请求,因为客户端似乎出现了错误。

        • 5xx:服务器错误。表示服务器在处理请求的过程中发生了错误。

      • 例子

        • 200 OK:表示请求成功。通常用于GET和POST请求。

        • 404 Not Found:表示请求的资源未找到。

        • 500 Internal Server Error:表示服务器遇到了一个未曾预料的状况,导致无法完成对请求的处理。

    1. 传输编码(Transfer-Encoding)

    服务器在将消息发送给客户端之前对消息进行编码,并在客户端接收到消息后进行解码。对消息进行压缩或分块处理,以提高传输效率或允许流式传输。

    • HTTP/1.0 :仅支持固定长度的消息体,或者使用Content-Length字段指定消息体长度。

    • HTTP/1.1:引入了分块传输编码(Chunked Transfer Encoding),允许服务器动态生成内容并分块传输,而无需提前知道内容的长度。

      1. 分块传输编码(Chunked Transfer Encoding)
      • 分块传输编码允许服务器将消息分成一系列以十六进制数字开头的数据块。每个数据块包含一段消息内容以及该内容的长度。最后一个数据块的长度为0,表示消息传输结束。
      • 这种编码方式使得服务器可以在发送完整个消息之前,将消息分成多个块逐个发送,而不需要等待整个消息的全部内容就绪。
      1. 压缩传输编码(Compression Transfer Encoding)
      • 压缩传输编码允许服务器对消息体进行压缩,以减少消息体的大小,从而节省带宽并加快传输速度。常见的压缩算法包括gzip和deflate。
      • 客户端在接收到消息后需要解压缩以获取原始的消息内容。

3.Http请求

  • 客户端---发请求(Request)---服务器

百度:

java 复制代码
Request URL:https://www.baidu.com/   请求地址
Request Method:GET    get方法/post方法
Status Code:200 OK    状态码:200
Remote(远程) Address:14.215.177.39:443
java 复制代码
Accept:text/html  
Accept-Encoding:gzip, deflate, br
Accept-Language:zh-CN,zh;q=0.9    语言
Cache-Control:max-age=0
Connection:keep-alive

请求行

  • 请求行中的请求方式:GET
  • 请求方式:Get,Post ,HEAD,DELETE,PUT,TRACT...
    • get:请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效
    • post:请求能够携带的参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效。

消息头

java 复制代码
Accept:告诉浏览器,它所支持的数据类型
Accept-Encoding:支持哪种编码格式  GBK   UTF-8   GB2312  ISO8859-1
Accept-Language:告诉浏览器,它的语言环境
Cache-Control:缓存控制
Connection:告诉浏览器,请求完成是断开还是保持连接
HOST:主机..../.

4.Http响应

  • 服务器---响应-----客户端

百度:

java 复制代码
Cache-Control:private    缓存控制
Connection:Keep-Alive    连接
Content-Encoding:gzip    编码
Content-Type:text/html   类型

响应体

java 复制代码
Accept:告诉浏览器,它所支持的数据类型
Accept-Encoding:支持哪种编码格式  GBK   UTF-8   GB2312  ISO8859-1
Accept-Language:告诉浏览器,它的语言环境
Cache-Control:缓存控制
Connection:告诉浏览器,请求完成是断开还是保持连接
HOST:主机..../.
Refresh:告诉客户端,多久刷新一次;
Location:让网页重新定位;

响应状态码

200:请求响应成功 200

3xx:请求重定向

  • 重定向:你重新到我给你新位置去;

4xx:找不到资源 404

  • 资源不存在;

5xx:服务器代码错误 500 502:网关错误

常见面试题:

当你的浏览器中地址栏输入地址并回车的一瞬间到页面能够展示回来,经历了什么?

5.Maven

为什么要学习这个技术?

  1. 在Java Web开发中,需要使用大量的jar包,我们手动去导入,这种操作很麻烦,PASS!!!

  2. 如何能够让一个东西自动帮我导入和配置这个jar包。

由此,Maven诞生了!

1. Maven项目架构管理工具

在Javaweb的学习中,就是用来就是方便导入jar包的!
Maven的核心思想:约定大于配置

  • 有约束,不要去违反。

Maven会规定好你该如何去编写自己的Java代码,必须要按照具体规范来。

2.下载安装Maven

官网:https://maven.apache.org/

  • 下载完成后,解压即可;

  • 建议将电脑上的所有环境都放在一个文件夹下,方便后期管理

3.配置环境变量

环境不进行配置也可以使用,但建议配置环境!!!

在电脑的系统环境变量中,进行如下配置:

  • 变量名:M2_HOME;变量值:你的maven目录下的bin目录。
  • 变量名:MAVEN_HOME;变量值:你的maven的目录。
  • 在系统的path中配置:

    java 复制代码
    %MAVEN_HOME%\bin

测试Maven是否安装成功,在cmd命令行输入:mvn -version,如下图则表示成功。

4.阿里云镜像

  • 镜像:mirrors
    • 作用:加速我们的下载
  • 国内建议使用阿里云的镜像。
  • 在setttins.xml文件中找到标签对,进行修改:
xml 复制代码
<mirror>
    <id>nexus-aliyun</id>
    <mirrorOf>*</mirrorOf>
    <name>Nexus aliyun</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror> 

5.本地仓库

本地仓库:在本地的仓库。
建立一个本地仓库:localRepository

xml 复制代码
<localRepository>F:\java\Maven\apache-maven-3.8.1\maven-repo</localRepository>

6.在IDEA中使用Maven

  1. 启动IDEA;

  2. 创建一个Maven项目;

  1. 等待项目初始化完毕。最后运行结果如下图:
  1. 观察maven仓库中多了什么东西?
  1. IDEA中的Maven设置

注意:IDEA项目创建成功后,看一眼Maven的配置。

注意:在IDEA中配置,Maven经常在IDEA中会出现一个问题就是项目自动创建完成后,它这个Maven Home会使用IDEA默认,如果发先了这个问题,手动改为本地的。

  1. 到这里,Maven在IDEA中的配置和使用就OK了!

7.创建一个普通的Maven项目

这个只有在Web应用下才会有!


创建中报错:Cannot resolve plugin org.apache.maven.plugins:maven-jar-plugin

主要原因是本地maven的配置文件和仓库地址不一致。

修改后如果仍是不行,可修改一下项目的pom.xml文件,加入如下代码:

xml 复制代码
<repositories>
    <repository>
        <id>alimaven</id>
        <name>Maven Aliyun Mirror</name>
        <url>https://repo.maven.apache.org/maven2/</url>
        <releases>
            <enabled>true</enabled>
        </releases>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories> 

再次进行刷新。如果你有更好的方法,欢迎留言交流!!!

8.标记文件夹功能

9.在 IDEA中配置Tomcat

解决警告问题。必须要的配置:为什么会有这个问题:我们访问一个网站,需要指定一个文件夹名字;

  • WAR文件是一个压缩文件,包含了完整的Web应用程序所需的所有内容。这包括Servlet类、JSP文件、HTML、CSS、JavaScript文件、配置文件、库文件等。

  • Exploded部署是指将Web应用程序以解压的形式直接部署到服务器上。这意味着应用程序的所有文件会以文件夹的形式存在于服务器的指定目录中,而不是以单个的WAR文件存在。

  • WAR文件和Exploded部署都有各自的优势和用途。WAR文件适用于生产环境部署,而Exploded部署适用于开发和调试过程中

中间的地址,可以自定义。它是在浏览器中的访问地址。

10. pom文件

pom.xml是Maven的核心配置文件。

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>

<!--Maven版本和头文件-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <!--这里就是我们刚才配置的GAV-->
  <groupId>com.github</groupId>
  <artifactId>Javaweb-01-maven</artifactId>
  <version>1.0-SNAPSHOT</version>
  <!--Package:项目的打包方式  jar:java应用  war:JavaWeb应用  -->
  <packaging>war</packaging>

  <!--配置-->
  <properties>
    <!--项目的默认构建编码-->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!--编码版本-->
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <!--项目依赖-->
  <dependencies>
    <!--    具体依赖的jar包配置文件-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <!--项目构建用的东西-->
  <build>
    <finalName>Javaweb-01-maven</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.github</groupId>
    <artifactId>JavaWeb-01-Maven02</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--    具体依赖的jar包配置文件-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <!--    maven的高级之处在于可以自动导入这个jar包所依赖的其他jar包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
    </dependencies>
</project>

maven由于他的约定大于配置,我们之后可以能遇到我们写的配置文件,无法被导出或者生效的问题,解决方案:

xml 复制代码
<!--在build中配置resources,来防止资源导出失败的问题-->
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>
  • <filtering>标签设置为true,表示这些资源文件在复制到输出目录时需要进行过滤处理,通常用于替换文件中的占位符或变量。

  • 在Maven中,当<filtering>标签设置为true时,表示资源文件在复制到输出目录(如target/classes目录)时需要进行过滤处理。这种过滤处理通常用于替换文件中的占位符或变量。

    具体来说,Maven会在复制资源文件到输出目录之前,对这些资源文件进行处理。处理的过程包括查找文件中的占位符或变量,并将其替换为相应的值。这些占位符或变量可以是Maven项目的属性、系统属性、环境变量等。

    例如,在一个.properties文件中,可能会有类似${version}这样的占位符。当<filtering>标签设置为true时,Maven会将${version}替换为实际的版本号,然后将该文件复制到输出目录。

    这种过滤处理对于根据不同环境(如开发、测试、生产)自动配置文件、替换不同配置选项等非常有用。通过在Maven的pom.xml中配置<filtering>true,开发人员可以在构建过程中自动完成这些替换操作,使得项目更加灵活和易于维护。
    IDEA操作

  • 参考:IDEA 中如何查看maven项目中所有jar包的依赖关系图

11. 解决遇到的问题

  1. Tomcat闪退

  2. IDEA中每次都要重复配置Maven;在IDEA中的全局默认配置中去配置。

  1. maven默认web项目中的web.xml版本问题。
  1. 替换为webapp4.0版本和tomcat一致。
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
        version="4.0"
        metadata-complete="true">

</web-app>
  1. Maven仓库的使用。

地址:https://mvnrepository.com/

Apache Maven是一个项目管理工具,用于管理Java项目的构建、依赖管理和项目信息管理。它利用项目对象模型(Project Object Model,POM)来描述项目,通过一系列插件执行各种构建任务。

以下是Maven的一些主要功能和特点:

  1. 项目对象模型(Project Object Model,POM)
  • Maven使用POM文件来描述项目的结构和配置信息。POM是一个XML文件,包含了项目的元数据,如项目依赖、构建目标、插件配置等。
  1. 依赖管理
  • Maven通过中央仓库(Central Repository)和其他远程仓库管理项目的依赖。开发人员可以在POM文件中指定项目所需的依赖,Maven会自动下载并管理这些依赖。
  1. 标准化的项目结构
  • Maven鼓励使用标准化的项目结构,例如将源代码放在src/main/java目录下,资源文件放在src/main/resources目录下,测试代码放在src/test/java目录下等。
  1. 约定优于配置
  • Maven遵循"约定优于配置"的原则,通过约定来减少配置。例如,Maven会根据约定自动识别源代码和资源文件的位置,无需额外配置。
  1. 插件系统
  • Maven提供了丰富的插件系统,可以执行各种构建任务,如编译代码、运行单元测试、打包项目、发布项目等。开发人员可以根据需要配置和使用不同的插件。
  1. 生命周期和阶段
  • Maven定义了一组标准的构建生命周期和阶段,如cleancompiletestpackage等。每个生命周期包含一组阶段,开发人员可以在POM文件中配置需要执行的阶段。
  1. 易于扩展
  • Maven是一个开放的平台,允许开发人员编写自定义插件和扩展,以满足特定项目的需求。

6.Servlet

1.Servlet简介

  • Servlet就是sun公司开发动态web的一门技术。

  • Sun在这些API中提供一个接口叫做:Servlet,如果你想开发一个Servlet程序,只需要完成两个小步骤:

    • 编写一个类,实现Servlet接口。
    • 把开发好的Java类部署到web服务器中。
  • 把实现了Servlet接口的Java程序叫做,Servlet

Servlet是Java编写的服务器端程序,用于处理客户端请求并生成响应。它运行在支持Java Servlet规范的Web服务器上,如Apache Tomcat、Jetty等。Servlet通常用于开发Web应用程序,可以处理HTTP请求、响应和会话管理等任务。

以下是Servlet的一些主要特点和用法:

  1. HTTP请求处理
  • Servlet可以接收来自客户端的HTTP请求,包括GET、POST、PUT、DELETE等方法,并且可以读取请求参数、请求头等信息。
  1. HTTP响应生成
  • Servlet可以根据请求生成相应的HTTP响应,包括设置响应状态码、响应头、响应体等内容。
  1. 会话管理
  • Servlet提供了会话管理功能,可以在多个HTTP请求之间保持会话状态。通常使用HTTP会话(HTTP Session)来实现会话管理,可以存储和获取会话属性,跟踪用户状态等。
  1. Servlet生命周期
  • Servlet有自己的生命周期,包括初始化、服务请求、销毁等阶段。当Web服务器启动时,会初始化Servlet并调用其init()方法;每当有HTTP请求到达时,服务器会调用Servlet的service()方法处理请求;当服务器关闭或者需要释放资源时,会调用Servlet的destroy()方法。
  1. 多线程支持
  • Servlet是多线程的,可以处理多个并发请求。每个HTTP请求通常由一个新的线程处理,因此Servlet必须是线程安全的,或者采取相应的同步措施来确保线程安全。
  1. 部署方式
  • Servlet通常作为Java Web应用程序的一部分进行部署,可以将Servlet打包成WAR(Web Application Archive)文件,并部署到支持Servlet规范的Web服务器上。
  1. Servlet API
  • Servlet API提供了一组接口和类,用于编写Servlet程序。其中包括javax.servlet.Servlet接口、javax.servlet.http.HttpServlet类等,开发人员可以通过实现这些接口或继承这些类来编写自己的Servlet。
  1. clean
  • clean阶段用于清理项目,删除先前构建生成的文件。主要任务包括删除target目录及其内容,以确保在下一次构建时开始一个干净的状态。执行该阶段可以清除之前构建产生的临时文件和目录,以便重新开始构建。
  1. compile
  • compile阶段用于编译项目的源代码(Java源文件)。在这个阶段,Maven会编译src/main/java目录下的所有Java源文件,并将编译结果(字节码文件)输出到target/classes目录中。这个阶段通常会检查语法错误和类型错误,并生成编译后的.class文件。
  1. test
  • test阶段用于运行项目的单元测试。在这个阶段,Maven会执行src/test/java目录下的所有单元测试,并生成测试报告。通常,这些测试类会以TestTestCase结尾,并使用一些测试框架(如JUnit、TestNG)来编写测试用例。执行该阶段可以确保项目的功能按预期工作,并且不会破坏已有的功能。
  1. package
  • package阶段用于将项目打包成特定的分发格式,如JAR、WAR、EAR等。在这个阶段,Maven会根据项目类型和配置生成相应的分发包,并将其输出到target目录中。例如,对于Java项目,可以生成一个包含编译后的类文件和资源文件的JAR文件;对于Web项目,可以生成一个WAR文件用于部署到Servlet容器中。

2.HelloServlet

Serlvet接口Sun公司有两个默认的实现类:HttpServlet,GenericServlet。

  1. 构建一个普通的Maven项目(不要使用模板),删掉里面的src目录,后面的笔记就在这个项目里面建立Moudel;这个空的工程就是Maven主工程;之后在其中建立一个maven子项目。
  1. 关于Maven父子工程的理解:
  • 父项目中会有:
xml 复制代码
   <modules>
       <module>Servlet-01</module>
   </modules>
  • 子项目会有:
xml 复制代码
   <parent>
       <artifactId>JavaWeb-02-Servlet</artifactId>
       <groupId>com.github</groupId>
       <version>1.0-SNAPSHOT</version>
   </parent>
  • 父项目中的java子项目可以直接使用。
java 复制代码
son extends father
  1. Maven环境优化。

  2. 修改web.xml为最新的。

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
           version="4.0"
           metadata-complete="true">
    
    </web-app>
  3. 将maven的结构搭建完整。

  1. 编写一个Servlet程序。
  1. 编写一个普通类。

  2. 实现Servlet接口,这里我们直接继承HttpServlet

java 复制代码
package com.github.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HelloServlet extends HttpServlet {
   // 由于get或post只是请求实现的不同的方式,可以相互调用,业务逻辑都是一样;
   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        ServletOutputStream outputStream = resp.getOutputStream();
       PrintWriter writer = resp.getWriter();  // 响应流
       writer.print("Hello,Serlvet");
   }

   @Override
   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       super.doPost(req, resp);
   }
}
  1. 编写Servlet的映射
  • 为什么需要映射:我们写的是JAVA程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要再web服务中注册我们写的Servlet,还需给他一个浏览器能够访问的路径;
xml 复制代码
   <!--注册Servlet-->
   <servlet>
       <servlet-name>hello</servlet-name>
       <servlet-class>com.github.servlet.HelloServlet</servlet-class>
   </servlet>
   <!--Servlet的请求路径-->
   <servlet-mapping>
       <servlet-name>hello</servlet-name>
       <url-pattern>/hello</url-pattern>
   </servlet-mapping>
  1. 配置Tomcat
  • 注意:配置项目发布的路径就可以了。
  1. 启动测试,OK!

报错参考:idea常见问题(自用)_com.intellij.javaee.oss.admin.jmx.jmxadminexceptio-CSDN博客

3.Servlet原理

  • Servlet是由Web服务器调用,web服务器在收到浏览器请求之后,会:

Servlet 的工作原理:

  1. 客户端发送请求

    客户端(通常是 Web 浏览器)发送 HTTP 请求到 Web 服务器。这个请求可以是 GET 请求、POST 请求或其他 HTTP 请求方法。

  2. Web 服务器接收请求

    Web 服务器(如 Apache Tomcat、Jetty 等)接收到客户端发送的请求。Web 服务器会根据配置将请求路由到相应的 Servlet。

  3. Servlet 容器处理请求

    Servlet 容器负责管理和执行 Servlet。当请求到达 Servlet 容器时,它会查找匹配请求 URL 的 Servlet,并调用相应的方法来处理请求。

  4. Servlet 处理请求

    Servlet 接收到请求后,会执行 service() 方法来处理请求。service() 方法根据请求的类型(GET、POST 等)调用相应的 doGet()doPost() 等方法。

  5. 生成响应

    doGet()doPost() 等方法中,Servlet 可以通过读取请求参数、访问数据库或执行其他操作来生成响应。生成的响应通常是一个 HTML 页面、JSON 数据等。

  6. 发送响应给客户端

    Servlet 将生成的响应发送回客户端。响应经过 Web 服务器再传输给客户端,客户端浏览器将其渲染并显示给用户。

  7. 生命周期管理

    Servlet 容器负责管理 Servlet 的生命周期,包括初始化 Servlet、处理请求、销毁 Servlet 等。在 Servlet 的生命周期内,容器会调用相应的方法来执行这些操作,如 init()service()destroy() 等。

4.Mapping问题

  1. 一个Servlet可以指定一个映射路径。
xml 复制代码
   <servlet-mapping>
       <servlet-name>hello</servlet-name>
       <url-pattern>/hello</url-pattern>
   </servlet-mapping>
  1. 一个Servlet可以指定多个映射路径。
xml 复制代码
   <servlet-mapping>
       <servlet-name>hello</servlet-name>
       <url-pattern>/hello</url-pattern>
   </servlet-mapping>
   <servlet-mapping>
       <servlet-name>hello</servlet-name>
       <url-pattern>/hello2</url-pattern>
   </servlet-mapping>
   <servlet-mapping>
       <servlet-name>hello</servlet-name>
       <url-pattern>/hello3</url-pattern>
   </servlet-mapping>
   <servlet-mapping>
       <servlet-name>hello</servlet-name>
       <url-pattern>/hello4</url-pattern>
   </servlet-mapping>
   <servlet-mapping>
       <servlet-name>hello</servlet-name>
       <url-pattern>/hello5</url-pattern>
   </servlet-mapping>
  1. 一个Servlet可以指定通用映射路径。
xml 复制代码
   <servlet-mapping>
       <servlet-name>hello</servlet-name>
       <url-pattern>/hello/*</url-pattern>
   </servlet-mapping>
  1. 默认请求路径。
xml 复制代码
   <!--默认请求路径-->
   <servlet-mapping>
       <servlet-name>hello</servlet-name>
       <url-pattern>/*</url-pattern>
   </servlet-mapping>
  1. 指定一些后缀或者前缀等等....
xml 复制代码
   <!-- 可以自定义后缀实现请求映射         
   注意点,*前面不能加项目映射的路径         
   hello/subei.github        -->
   <servlet-mapping>
       <servlet-name>hello</servlet-name>
       <url-pattern>*.github</url-pattern>
   </servlet-mapping>
  1. 优先级问题。
  • 指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求;
java 复制代码
package com.github.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class ErrorServelt extends HelloServlet{
   @Override
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       resp.setContentType("text/html");
       resp.setCharacterEncoding("utf-8");

       PrintWriter writer = resp.getWriter();
       writer.println("<h1>404</h1>");
   }

   @Override
   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       super.doPost(req, resp);
   }
}
xml 复制代码
<!--  404  -->
   <servlet>
       <servlet-name>error</servlet-name>
       <servlet-class>com.github.servlet.ErrorServelt</servlet-class>
   </servlet>
   <servlet-mapping>
       <servlet-name>error</servlet-name>
       <url-pattern>/*</url-pattern>
   </servlet-mapping>

5.ServletContext

  • web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用;
1.共享数据
  • 在这个Servlet中保存的数据,可以在另外一个servlet中拿到;
java 复制代码
package com.github.servlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        this.getInitParameter(); 初始化参数
//        this.getServletConfig(); Servlet配置
//        this.getServletContext(); Servlet上下文
        ServletContext context = this.getServletContext();

        String name = "学习超好";   // 数据
        // 将一个数据保存在了ServletContext中,名字为:name ,值 name
        context.setAttribute("name",name);

        System.out.println("Hello");

    }
}
java 复制代码
package com.github.servlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class GetServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String name = (String)context.getAttribute("name");

        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        resp.getWriter().print("名字:"+name);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
xml 复制代码
<servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.github.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>getc</servlet-name>
        <servlet-class>com.github.servlet.GetServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>getc</servlet-name>
        <url-pattern>/getc</url-pattern>
    </servlet-mapping>
  • 测试访问结果:

Java-IDEA2020-IDEA或者启动Tomcat控制台中文乱码解决

  • 解决后如下:
2.获取初始化参数
xml 复制代码
<!-- 配置一些Web应用初始化参数 -->
    <context-param>
        <param-name>url</param-name>
        <param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
    </context-param>

    <servlet>
        <servlet-name>gp</servlet-name>
        <servlet-class>com.github.servlet.ServletDemo03</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>gp</servlet-name>
        <url-pattern>/gp</url-pattern>
    </servlet-mapping>
java 复制代码
package com.github.servlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ServletDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String url = context.getInitParameter("url");
        resp.getWriter().print(url);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
3.请求转发
java 复制代码
package com.github.servlet;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ServletDemo04 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        System.out.println("进入ServletDemo04文件!");
//        转发的请求路径
//        RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");
//        调用forward实现请求转发;
//        requestDispatcher.forward(req,resp);
        context.getRequestDispatcher("/gp").forward(req,resp);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
xml 复制代码
    <servlet>
        <servlet-name>gp02</servlet-name>
        <servlet-class>com.github.servlet.ServletDemo04</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>gp02</servlet-name>
        <url-pattern>/gp02</url-pattern>
    </servlet-mapping>
4.读取资源文件
  • Properties
    • 在java目录下新建properties
    • 在resources目录下新建properties
  • 发现:都被打包到了同一个路径下:classes,我们俗称这个路径为classpath:
  • 思路:需要一个文件流;
properties 复制代码
username=root365
password=123456
java 复制代码
package com.github.servlet;

import javax.servlet.ServletException;
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.util.Properties;

public class ServletDemo05 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/com/github/servlet/aa.properties");

        Properties prop = new Properties();
        prop.load(is);
        String user = prop.getProperty("username");
        String pwd = prop.getProperty("password");

        resp.getWriter().print(user+":"+pwd);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
xml 复制代码
    <servlet>
        <servlet-name>db</servlet-name>
        <servlet-class>com.github.servlet.ServletDemo05</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>db</servlet-name>
        <url-pattern>/db</url-pattern>
    </servlet-mapping>
  • 访问测试!

6.HttpServletResponse

  • web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的一个HttpServletResponse;
    • 如果要获取客户端请求过来的参数:找HttpServletRequest;
    • 如果要给客户端响应一些信息:找HttpServletResponse。
1.简单分类
  • 负责向浏览器发送数据的方法。
java 复制代码
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
  • 负责向浏览器发送响应头的方法。
java 复制代码
    void setCharacterEncoding(String var1);

    void setContentLength(int var1);

    void setContentLengthLong(long var1);

    void setContentType(String var1);

    void setDateHeader(String var1, long var2);

    void addDateHeader(String var1, long var2);

    void setHeader(String var1, String var2);

    void addHeader(String var1, String var2);

    void setIntHeader(String var1, int var2);

    void addIntHeader(String var1, int var2);
  • 响应的状态码。
java 复制代码
    int SC_CONTINUE = 100;
    int SC_SWITCHING_PROTOCOLS = 101;
    int SC_OK = 200;
    int SC_CREATED = 201;
    int SC_ACCEPTED = 202;
    int SC_NON_AUTHORITATIVE_INFORMATION = 203;
    int SC_NO_CONTENT = 204;
    int SC_RESET_CONTENT = 205;
    int SC_PARTIAL_CONTENT = 206;
    int SC_MULTIPLE_CHOICES = 300;
    int SC_MOVED_PERMANENTLY = 301;
    int SC_MOVED_TEMPORARILY = 302;
    int SC_FOUND = 302;
    int SC_SEE_OTHER = 303;
    int SC_NOT_MODIFIED = 304;
    int SC_USE_PROXY = 305;
    int SC_TEMPORARY_REDIRECT = 307;
    int SC_BAD_REQUEST = 400;
    int SC_UNAUTHORIZED = 401;
    int SC_PAYMENT_REQUIRED = 402;
    int SC_FORBIDDEN = 403;
    int SC_NOT_FOUND = 404;
    int SC_METHOD_NOT_ALLOWED = 405;
    int SC_NOT_ACCEPTABLE = 406;
    int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
    int SC_REQUEST_TIMEOUT = 408;
    int SC_CONFLICT = 409;
    int SC_GONE = 410;
    int SC_LENGTH_REQUIRED = 411;
    int SC_PRECONDITION_FAILED = 412;
    int SC_REQUEST_ENTITY_TOO_LARGE = 413;
    int SC_REQUEST_URI_TOO_LONG = 414;
    int SC_UNSUPPORTED_MEDIA_TYPE = 415;
    int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
    int SC_EXPECTATION_FAILED = 417;
    int SC_INTERNAL_SERVER_ERROR = 500;
    int SC_NOT_IMPLEMENTED = 501;
    int SC_BAD_GATEWAY = 502;
    int SC_SERVICE_UNAVAILABLE = 503;
    int SC_GATEWAY_TIMEOUT = 504;
    int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
2.下载文件
  1. 向浏览器输出消息;
  2. 下载文件:
  3. 要获取下载文件的路径;
  4. 下载的文件名是啥?
  5. 设置想办法让浏览器能够支持下载我们需要的东西;
  6. 获取下载文件的输入流;
  7. 创建缓冲区;
  8. 获取OutputStream对象;
  9. 将FileOutputStream流写入到buffer缓冲区;
  10. 使用OutputStream将缓冲区中的数据输出到客户端!
java 复制代码
package com.github.servlet;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;

public class FileServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
//        1. 要获取下载文件的路径;
        // F:\java\IDEA2020.2\JavaWeb-02-Servlet\response\target\classes\02.png
        String realPath = "F:\\java\\IDEA2020.2\\JavaWeb-02-Servlet\\response\\target\\classes\\02.png";
        System.out.println("下载文件的路径:" + realPath);
//        2. 下载的文件名是啥?
        String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
//        3. 设置想办法让浏览器能够支持下载我们需要的东西;
        resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));
//        4. 获取下载文件的输入流;
        FileInputStream in = new FileInputStream(realPath);
//        5. 创建缓冲区;
        int len = 0;
        byte[] buffer = new byte[1024];
//        6. 获取OutputStream对象;
        ServletOutputStream out = resp.getOutputStream();
//        7. 将FileOutputStream流写入到buffer缓冲区;
        while ((len=in.read(buffer)) > 0){
            out.write(buffer,0,len);
        }

        in.close();
        out.close();
//        8. 使用OutputStream将缓冲区中的数据输出到客户端!
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>/response</servlet-name>
        <servlet-class>com.github.servlet.FileServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>/response</servlet-name>
        <url-pattern>/down</url-pattern>
    </servlet-mapping>

</web-app>
3.验证码功能
  • 验证怎么来的?
    • 前端实现;
    • 后端实现,需要用到 Java 的图片类,生产一个图片。
java 复制代码
package com.github.servlet;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

public class ImageServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
//        如何让浏览器3秒自动刷新一次;
        resp.setHeader("refresh","3");

//        在内存中创建一个图片
        BufferedImage image = new BufferedImage(90,40,BufferedImage.TYPE_INT_RGB);
//        得到图片,笔
        Graphics2D g = (Graphics2D) image.getGraphics();
//        设置图片的背景颜色
        g.setColor(Color.white);
        g.fillRect(0,0,90,40);
//        给图片写数据
        g.setColor(Color.RED);
        g.setFont(new Font(null,Font.BOLD,20));
        g.drawString(makeNum(),8,30);

//        告诉浏览器,这个请求用图片的方式打开
        resp.setContentType("image/jpeg");
//        网站存在缓存,不让浏览器缓存
        resp.setDateHeader("expires",-1);
        resp.setHeader("Cache-Control","no-cache");
        resp.setHeader("Pragma","no-cache");

//        把图片写给浏览器
        ImageIO.write(image,"jpg", resp.getOutputStream());

    }

//    生成随机数
    private String makeNum(){
        Random random = new Random();
        String num = random.nextInt(9999999) + "";
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 7-num.length() ; i++) {
            sb.append("0");
        }
        num = sb.toString() + num;
        return num;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
xml 复制代码
    <servlet>
        <servlet-name>/imgs</servlet-name>
        <servlet-class>com.github.servlet.ImageServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>/imgs</servlet-name>
        <url-pattern>/img</url-pattern>
    </servlet-mapping>
4.实现重定向
  • B一个web资源收到客户端A请求后,B他会通知A客户端去访问另外一个web资源C,这个过程叫重定向

常见场景:

  • 用户登录
java 复制代码
void sendRedirect(String var1) throws IOException;
  • 测试:
java 复制代码
package com.github.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*        
resp.setHeader("Location","/response/img");        
resp.setStatus(302);         */
        resp.sendRedirect("/response/img");    // 重定向
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
xml 复制代码
    <servlet>
        <servlet-name>/redirectServlet</servlet-name>
        <servlet-class>com.github.servlet.RedirectServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>/redirectServlet</servlet-name>
        <url-pattern>/red</url-pattern>
    </servlet-mapping>

面试题:请你聊聊重定向和转发的区别?

  • 相同点:页面都会实现跳转;
  • 不同点:
    • 请求转发的时候,url不会产生变化;
    • 重定向时候,url地址栏会发生变化;

什么是重定向

重定向是指当客户端发出请求后,服务器返回一个特殊的响应,指示客户端去请求另一个 URL。这个过程常用于网页跳转、URL 地址更新等情况。在 Web 开发中,重定向通常通过 HTTP 响应头来实现。

在你的代码中,如果要进行重定向,可以使用以下方法之一:

  1. 使用 HttpServletResponse 的 sendRedirect 方法
java 复制代码
resp.sendRedirect("https://example.com/new-url");

这会将客户端重定向到指定的 URL。在这种情况下,不需要手动设置响应头,sendRedirect 方法会自动设置 Location 头。

  1. 手动设置 Location 头

java

java 复制代码
resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); 
resp.setHeader("Location", "https://example.com/new-url");

这种方法手动设置了 Location 头,指示客户端重定向到指定的 URL。需要注意的是,这种方法需要手动设置状态码(例如,SC_MOVED_TEMPORARILY 表示 302 临时重定向),并且要确保不会同时使用 sendRedirect 方法,否则可能会产生冲突。

重定向常用于以下情况:

  • 当用户请求一个需要登录才能访问的页面时,可以重定向到登录页面。
  • 当网站 URL 更新或资源位置变更时,可以将旧的 URL 重定向到新的 URL。
  • 当进行表单提交后,为了防止表单的重复提交,可以重定向到一个展示成功消息的页面。

无论使用哪种方法,重定向都是一种常用的网页导航技术,能够提供更好的用户体验和网站结构。

重定向(Redirect)和转发(Forward)

重定向(Redirect)和转发(Forward)是在 Web 开发中常见的两种页面跳转方式,它们有着一些关键的区别:

  1. 执行方式
  • 重定向:在客户端(浏览器)接收到服务器的响应后,会发起一个新的请求去请求重定向的 URL。这意味着客户端和服务器之间会有两次通信。重定向时,浏览器会看到两个不同的 URL。

    java 复制代码
    resp.sendRedirect("/captcha"); 
  • 转发:是在服务器内部完成的,服务器直接将请求转发给另一个资源,目标资源对客户端来说是透明的,客户端并不知道自己被发送到了另一个资源。

    java 复制代码
    servletContext.getRequestDispatcher("/get_init_param").forward(req, resp);
  1. URL 的变化
  • 重定向:会导致浏览器地址栏的 URL 发生改变,因为客户端实际上发起了一个新的请求。
  • 转发:不会改变浏览器地址栏中的 URL,因为是服务器内部进行的操作,客户端对此一无所知。
  1. 传递参数
  • 重定向:不能直接共享请求参数,因为它是两个独立的请求。但是可以通过 URL 参数、Session 等方式来传递信息。
  • 转发:能够共享请求参数,因为是在服务器内部进行的操作,可以在转发时将请求参数一并传递给目标资源。
  1. 适用场景
  • 重定向:适用于跳转到其他网站、跳转到其他 Web 应用、防止表单重复提交等场景。
  • 转发:适用于内部资源之间的跳转,如不同 Servlet、JSP 之间的跳转。
5.简单实现登录重定向

index.jsp:

js 复制代码
<%--
    这里提交的路径需要寻找到项目的路径
    ${pageContext.request.contextPath} : 代表当前项目
--%>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>登录</title>
  </head>
  <body>
  <form action="${pageContext.request.contextPath}/login" method="get">
    用户名:<input type="text" name="username"><br>
    密  码:<input type="password" name="password"><br>
    <input type="submit">
  </form>
  </body>
</html>
java 复制代码
package com.github.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class RequestTest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 处理请求
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        System.out.println(username+":"+password);

        // 重定向时需注意路径问题,否则为404;
        // /response/success.jsp
        resp.sendRedirect("/response/success.jsp");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
xml 复制代码
    <servlet>
        <servlet-name>/request</servlet-name>
        <servlet-class>com.github.servlet.RequestTest</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>/request</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>成功</title>
</head>
<body>

<h1>登录成功!!!</h1>

</body>
</html>

7.HttpServletRequest

  • HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest,通过这个HttpServletRequest的方法,获得客户端的所有信息;

获取参数,请求转发:

java 复制代码
package com.github.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;

public class HttpServletRequestTest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");

        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobbys = req.getParameterValues("hobbies");
        System.out.println("=============================");
        // 后台接收中文乱码问题
        System.out.println(username);
        System.out.println(password);
        System.out.println(Arrays.toString(hobbys));
        System.out.println("=============================");

        System.out.println(req.getContextPath());
        // 通过请求转发
        // 这里的 / 代表当前的web应用
        req.getRequestDispatcher("/success.jsp").forward(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
xml 复制代码
    <servlet>
        <servlet-name>/hsrt</servlet-name>
        <servlet-class>com.github.servlet.HttpServletRequestTest</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>/hsrt</servlet-name>
        <url-pattern>/hsrt</url-pattern>
    </servlet-mapping>

http://localhost:8080/http_servlet_req?username=zhangsan\&password=123456\&hobbies=reading,cooking,traveling

java 复制代码
=============================
zhangsan
123456
[reading,cooking,traveling]
=============================

7.Cookie.Session

1.会话

  • 会话:用户打开一个浏览器,点击了很多超链接,访问多个web资源,关闭浏览器,这个过程可以称之为会话;

  • 有状态会话:一个同学来过教室,下次再来教室,我们会知道这个同学,曾经来过,称之为有状态会话;

你能怎么证明你是西开的学生?

java 复制代码
你              西开

1. 发票                西开给你发票
2. 学校登记        西开标记你来过了

一个网站,怎么证明你来过?

java 复制代码
客户端              服务端

1. 服务端给客户端一个 信件,客户端下次访问服务端带上信件就可以了; cookie
2. 服务器登记你来过了,下次你来的时候我来匹配你; seesion

2.保存会话的两种技术

cookie

  • 客户端技术 (响应,请求)

session

  • 服务器技术,利用这个技术,可以保存用户的会话信息? 我们可以把信息或者数据放在Session中!

常见常见:网站登录之后,你下次不用再登录了,第二次访问直接就上去了!

3.Cookie

  1. 从请求中拿到cookie信息;
  2. 服务器响应给客户端cookie;
java 复制代码
Cookie[] cookies = req.getCookies(); // 获得Cookie
cookie.getName(); // 获得cookie中的key
cookie.getValue(); // 获得cookie中的vlaue
new Cookie("lastLoginTime", System.currentTimeMillis()+""); // 新建一个cookie
cookie.setMaxAge(24*60*60); // 设置cookie的有效期
resp.addCookie(cookie); // 响应给客户端一个cookie
  • cookie:一般会保存在本地的 用户目录下 appdata;
  • 案例:
java 复制代码
package com.github.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

/** * 保存用户上一次访问的时间 */
public class CookieDemo01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 服务器,告诉你,你来的时间,将这个时间封装为一个信件
        // 解决中文乱码
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html; charset=UTF-8");

        PrintWriter writer = resp.getWriter();

        // Cookie,服务端从客户端获取
        Cookie[] cookies = req.getCookies();
        // 这里返回数组,说明Cookie可能存在多个

        // 判断Cookie是否存在
        if(cookies!=null){
            // 如果存在,遍历数组
            writer.write("你上一次访问的时间是:");
            for (int i = 0; i < cookies.length; i++) {
                Cookie cookie = cookies[i];
                // 获取Cookie的名字
                if(cookie.getName().equals("lastLoginTime")) {
                    // 获得cookie中的value
                    long parseLong = Long.parseLong(cookie.getValue());
                    Date date = new Date(parseLong);
                    writer.write(date.toLocaleString());
                }
            }
        }else{
            writer.write("第一次访问本站!!!");
        }

        // 服务端给客户端响应一个Cookie;
        Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis()+"");

        resp.addCookie(cookie);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
xml 复制代码
    <servlet>
        <servlet-name>/cookieDemo01</servlet-name>
        <servlet-class>com.github.servlet.CookieDemo01</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>/cookieDemo01</servlet-name>
        <url-pattern>/cd01</url-pattern>
    </servlet-mapping>

一个网站cookie是否存在上限!聊聊细节问题

  • 一个Cookie只能保存一个信息;
  • 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie;
  • Cookie大小有限制4kb;
  • 300个cookie浏览器上限。

删除Cookie;

  • 不设置有效期,关闭浏览器,自动失效;
  • 设置有效期时间为 0 ;

编码解码:

java 复制代码
URLEncoder.encode("哇哈哈","utf-8")
URLDecoder.decode(cookie.getValue(),"UTF-8")
java 复制代码
package com.github.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Date;

/** * 中文数据传递 */
public class CookieDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决中文乱码
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html; charset=UTF-8");

        PrintWriter writer = resp.getWriter();
        Cookie[] cookies = req.getCookies();

        // 判断Cookie是否存在
        if(cookies!=null){
            // 如果存在,遍历数组
            writer.write("你上一次访问的用户是:");
            for (int i = 0; i < cookies.length; i++) {
                Cookie cookie = cookies[i];
                // 获取Cookie的名字
                if(cookie.getName().equals("name")) {
                    // 解码
                    writer.write(URLDecoder.decode(cookie.getValue(),"UTF-8"));
                }
            }
        }else{
            writer.write("第一次访问本站!!!");
        }

        // 编码
        Cookie cookie = new Cookie("name", URLEncoder.encode("哇哈哈","UTF-8"));
        resp.addCookie(cookie);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
xml 复制代码
   <servlet>
        <servlet-name>/cookieDemo03</servlet-name>
        <servlet-class>com.github.servlet.CookieDemo03</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>/cookieDemo03</servlet-name>
        <url-pattern>/cd03</url-pattern>
    </servlet-mapping>

Cookie 通常用于存储一些不敏感的状态信息,而 Session 则更适合用于存储敏感信息和管理用户的会话状态
Cookie 和 Session 是两种常用的会话管理机制,它们在实现上有一些区别:

  1. Cookie
  • Cookie 是一小段文本信息,由服务器发送给客户端(通常是浏览器),然后客户端将其存储在本地的文件中。
  • Cookie 通常包含了一些键值对,用于存储用户的状态信息,比如登录状态、偏好设置等。
  • 每次客户端与服务器通信时,会将相关的 Cookie 信息发送给服务器,从而实现状态的保持和共享。
  • Cookie 可以设置过期时间,可以是会话级的(关闭浏览器后失效)或长期的(在指定时间之后失效)。
  1. Session
  • Session 是服务器端的一种机制,用于跟踪用户的会话状态。
  • 当用户访问服务器时,服务器会为该用户创建一个唯一的会话标识符(Session ID),并将该标识符发送给客户端。
  • 客户端在后续的请求中会携带该 Session ID,服务器利用这个标识符来识别用户,并存储该用户的状态信息。
  • Session 数据通常存储在服务器的内存或数据库中,与客户端无关,因此相对安全。
  • 与 Cookie 不同,Session 的存储不依赖于客户端的支持,因此更加灵活和安全,但也会增加服务器的负担。

4.Session(重点)

什么是Session:

  • 服务器会给每一个用户(浏览器)创建一个Seesion对象;
  • 一个Seesion独占一个浏览器,只要浏览器没有关闭,这个Session就存在;
  • 用户登录之后,整个网站它都可以访问!--> 保存用户的信息;保存购物车的信息......

Session和cookie的区别:

  • Cookie是把用户的数据写给用户的浏览器,浏览器保存 (可以保存多个)
  • Session把用户的数据写到用户独占Session中,服务器端保存 (保存重要的信息,减少服务器资源的浪费)
  • Session对象由服务创建;

使用场景:

  • 保存一个登录用户的信息;
  • 购物车信息;
  • 在整个网站中经常会使用的数据,我们将它保存在Session中;

使用Session:

java 复制代码
package com.github.pojo;

public class Person {
    private int age;
    private String name;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Person() {
    }

    public Person(String name, int age) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
java 复制代码
package com.github.servlet;

import com.github.pojo.Person;

import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;

public class SessionDemo01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决乱码问题
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html; charset=UTF-8");

        // 得到Session
        HttpSession session = req.getSession();
        // 在session中存东西
        session.setAttribute("name",new Person("懒羊羊",1));
        // 获取Session中的ID
        String id = session.getId();

        // 判断Session是否为新创建的
        if(session.isNew()){
            resp.getWriter().write("session创建成功!ID:" + id);
        }else{
            resp.getWriter().write("session已经在服务器中存在了!ID:" + id);
        }

        // Session创建的时候做了什么事情:
//        Cookie cookie = new Cookie("JSESSIONID", id);
//        resp.addCookie(cookie);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}

 // 得到Session
//HttpSession session = req.getSession();
//
//Person person = (Person) session.getAttribute("name");
//
//System.out.println(person.toString());
//
//HttpSession session = req.getSession();
//session.removeAttribute("name");
 // 手动注销Session
//session.invalidate();
xml 复制代码
    <servlet>
        <servlet-name>/sessionDemo</servlet-name>
        <servlet-class>com.github.servlet.SessionDemo01</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>/sessionDemo</servlet-name>
        <url-pattern>/sd01</url-pattern>
    </servlet-mapping>

会话自动过期:web.xml配置

xml 复制代码
<!--设置Session默认的失效时间-->
<session-config>
    <!--15分钟后Session自动失效,以分钟为单位-->
    <session-timeout>15</session-timeout>
</session-config>

POJO

POJO 是 "Plain Old Java Object"(简单老式 Java 对象)的缩写。它是指一个普通的 Java 对象,没有遵循任何特定的约定、框架或接口。POJO 的概念源于简化 Java 开发的目标,即通过简单的对象模型和普通的 Java 类来编写应用程序,而不依赖于任何特定的技术或框架。

以下是一些典型的 POJO 特征:

  1. 无继承或实现特定接口: POJO 类通常不继承特定的父类,也不实现特定的接口。它们只是普通的 Java 类。

  2. 不依赖于特定框架: POJO 不依赖于任何特定的框架或技术,可以在任何 Java 环境中使用,使得代码更加灵活和可移植。

  3. 简单的属性和方法: POJO 类通常包含一些私有字段(属性),以及公共的 getter 和 setter 方法来访问和修改这些属性。它们可能还包含一些其他的方法来处理对象的行为。

  4. 可序列化: POJO 类可以实现 Serializable 接口,以便在网络传输或持久化存储时可以被序列化和反序列化。

  5. 与业务逻辑无关: POJO 类通常不包含与业务逻辑相关的代码,它们只是用于表示数据的简单容器。

POJO 的概念在 Java 开发中得到了广泛的应用,特别是在框架如 Spring 等中。使用 POJO 可以使代码更加简洁、可维护和可测试,降低了耦合性,提高了代码的可重用性和灵活性。因此,在 Java 开发中,编写和使用 POJO 是一种良好的编程实践。

8.JSP

1.什么是JSP

Java Server Pages : Java服务器端页面,也和Servlet一样,用于动态Web技术!

  • 最大的特点:
    • 写JSP就像在写HTML;
    • 区别:
      • HTML只给用户提供静态的数据;
      • JSP页面中可以嵌入JAVA代码,为用户提供动态数据;

JSP,即 Java Server Pages(Java 服务器页面),是一种用于开发动态 Web 内容的技术。它允许开发人员在 HTML 页面中嵌入 Java 代码,从而实现在网页上动态生成内容的目的。JSP 通常与 Servlet 结合使用,Servlet 用于处理业务逻辑和控制流程,而 JSP 则用于生成用户界面。

以下是一些关于 JSP 的关键特性和用法:

  1. 易于学习和使用: JSP 基于标准的 HTML 和 Java 语法,因此对于熟悉这两种语言的开发人员来说相对容易上手。

  2. 动态内容生成: JSP 允许在 HTML 页面中嵌入 Java 代码,这使得开发人员可以在页面上动态生成内容,比如从数据库中检索数据并显示在网页上。

  3. 模板化: JSP 允许创建模板文件,其中包含页面的结构和布局,然后在运行时根据需要插入动态内容。这种模板化的方式使得页面的维护和更新更加方便。

  4. 标签库: JSP 支持自定义标签库,开发人员可以编写自定义标签来简化页面上的重复性代码,提高代码的可重用性和可维护性。

  5. 易于集成: JSP 可以轻松地与其他 Java 技术集成,如 Servlet、JavaBean、EJB 等,从而实现更复杂的 Web 应用程序功能。

  6. 支持 MVC 架构: JSP 可以作为视图层与 Servlet 结合使用,实现 MVC(Model-View-Controller)架构中的视图层,使得代码的组织和管理更加清晰。

尽管 JSP 在过去被广泛使用,但随着现代的 Web 开发趋势和技术的发展,像 Spring MVC、React、Angular 等前端框架的兴起,以及基于 RESTful API 的服务端开发模式,JSP 的使用已经相对减少。然而,在一些传统的企业应用程序中,仍然可以看到 JSP 的身影,尤其是在需要快速开发和易于维护的场景下。

2.JSP原理

思路:JSP到底怎么执行的!

  • 代码层面没有任何问题;

  • 服务器内部工作:

    • tomcat中有一个work目录;
  • IDEA中使用Tomcat的会在IDEA的tomcat中生产一个work目录;

  • 我电脑的地址:
java 复制代码
C:\Users\subei\AppData\Local\JetBrains\IntelliJIdea2020.2\tomcat\Unnamed_JavaWeb-02-Servlet_4\work\Catalina\localhost\Session\org\apache\jsp
  • 发现页面转变成了Java程序!
  • 浏览器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet!

  • JSP最终也会被转换成为一个Java类!

  • JSP 本质上就是一个Servlet

    在 JSP 技术中,JSP 页面本质上确实会被转换为 Servlet 类。当服务器第一次请求 JSP 页面时,容器会将其转换为一个 Java Servlet 类,然后编译并执行该 Servlet。因此,JSP 页面最终也是以 Servlet 的形式在服务器上运行的。

java 复制代码
// 初始化
  public void _jspInit() {
  }
// 销毁
  public void _jspDestroy() {
  }
// JSPService
  public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
      throws java.io.IOException, javax.servlet.ServletException {   
  1. 判断请求;

  2. 内置一些对象;

java 复制代码
final javax.servlet.jsp.PageContext pageContext;  // 页面上下文
javax.servlet.http.HttpSession session = null;    // session
final javax.servlet.ServletContext application;   // applicationContext
final javax.servlet.ServletConfig config;         // config
javax.servlet.jsp.JspWriter out = null;           // out
final java.lang.Object page = this;               // page:当前
HttpServletRequest request                        // 请求
HttpServletResponse response                      // 响应

pagepageContextapplication 是三个不同的对象,它们分别代表着不同的上下文和作用域。

  1. pagepage 是 JSP 页面本身的一个隐含对象,它表示当前的 JSP 页面实例。可以将其视为对当前 JSP 页面的引用。page 对象是一个 Java 对象,可以调用其方法,访问其属性等。通过 page 对象,可以实现在 JSP 页面中的 Java 代码中对当前页面进行操作和访问。

  2. pageContextpageContext 是一个 JSP 页面的上下文对象,它提供了访问 JSP 页面的各种信息和功能的接口。pageContext 对象可以用于获取请求、响应、会话等信息,以及在页面和请求之间传递数据,执行页面转发和包含等操作。它是在 JSP 页面中可用的隐含对象,无需额外声明即可直接使用。

  3. applicationapplication 是一个 Servlet 上下文对象,它表示整个 Web 应用程序的上下文。application 对象可以用于在整个应用程序范围内共享数据、访问初始化参数、获取资源等。与 pageContextpage 不同,application 对象在整个应用程序中都是唯一的,而不是针对单个 JSP 页面的。因此,通过 application 对象设置的数据在整个应用程序中是可见的。

总的来说,page 对象是对当前 JSP 页面的引用,pageContext 对象提供了访问 JSP 页面相关信息和功能的接口,而 application 对象提供了访问整个 Web 应用程序上下文的接口。

  1. 输出页面前增加的代码;
java 复制代码
response.setContentType("text/html");       // 设置响应的页面类型
pageContext = _jspxFactory.getPageContext(this, request, response,
                                         null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
  1. 以上的这些个对象我们可以在JSP页面中直接使用!
  • 在JSP页面中:
    • 只要是 JAVA代码就会原封不动的输出;
    • 如果是HTML代码,就会被转换为:
java 复制代码
      out.write("<html>\n");
      out.write("  <head>\n");
      out.write("    <title>$Title$</title>\n");
      out.write("  </head>\n");
      out.write("  <body>\n");
      out.write("  $END$\n");
      out.write("  </body>\n");
      out.write("</html>\n");
  • 这样的格式,输出到前端!

在 JSP 技术中,JSP 页面本质上确实会被转换为 Servlet 类。当服务器第一次请求 JSP 页面时,容器会将其转换为一个 Java Servlet 类,然后编译并执行该 Servlet。因此,JSP 页面最终也是以 Servlet 的形式在服务器上运行的。

关于 JSP 页面中的 Java 代码和 HTML 代码的处理方式,有一些需要注意的地方:

  1. Java 代码的处理 :在 JSP 页面中,任何位于 <% %> 标签中的 Java 代码块都会被原封不动地输出到生成的 Servlet 类中。这意味着,Java 代码会被编译和执行,输出的结果将直接包含在生成的 Servlet 中。例如:
jsonp 复制代码
<%
    String message = "Hello, world!";
    out.println(message);
%>

以上代码会被转换成 Servlet 类中的 Java 代码,类似于:

java 复制代码
out.println("Hello, world!");
  1. HTML 代码的处理 :在 JSP 页面中,HTML 代码会被直接放置在生成的 Servlet 类中的 service 方法中。这些 HTML 代码不会被直接输出到客户端,而是在 Servlet 运行时生成响应内容。例如:

html

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
</head>
<body>
    <h1>Welcome to our website!</h1>
    <p>This is a paragraph.</p>
</body>
</html>

以上 HTML 代码会被直接放置在生成的 Servlet 的 service 方法中,类似于:

java 复制代码
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<title>Example</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Welcome to our website!</h1>");
out.println("<p>This is a paragraph.</p>");
out.println("</body>");
out.println("</html>");

因此,JSP 技术使得开发者可以在同一个文件中编写 Java 代码和 HTML 代码,并且它们在最终生成的 Servlet 中被正确地处理和组合。

3.JSP基础语法

  • 任何语言都有自己的语法,JAVA中有,JSP 作为java技术的一种应用,它拥有一些自己扩充的语法(了解,知道即可!),Java所有语法都支持!
  • 配置必需的maven环境:
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>JavaWeb-02-Servlet</artifactId>
        <groupId>com.github</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>Jsp</artifactId>

    <dependencies>
<!--   Servlet 依赖   -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
<!--        JSP 依赖   -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.3</version>
        </dependency>
<!--        JSTL表达式的依赖-->
        <dependency>
            <groupId>javax.servlet.jsp.jstl</groupId>
            <artifactId>jstl-api</artifactId>
            <version>1.2</version>
        </dependency>
<!--        standard标签库-->
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
        </dependency>

    </dependencies>

</project>
  1. <dependency> 标签中的 jstl-api 依赖声明了 JSTL API,它提供了一组标签和函数,用于简化在 JSP 页面中的 Java 代码和常见任务的处理。jstl-api 包含了 JSTL 核心功能的接口和类。通常情况下,这个依赖是需要的,因为它定义了 JSTL 的 API,但不包含实现。

  2. <dependency> 标签中的 standard 依赖声明了 JSTL 的标准标签库的实现,它提供了 JSP 页面中可用的标签。这些标签可以用于控制流程、迭代、条件逻辑等常见任务,使得 JSP 页面的开发更加简单和模块化。standard 包含了 JSTL 标准标签库的实现。

JSP表达式
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>

  <%--JSP表达式
  作用:用来将程序的输出,输出到客户端
  <%= 变量或者表达式%>
  --%>
  <%= new java.util.Date()%>

  </body>
</html>
jsp脚本片段
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>

  <%--jsp脚本片段--%>
  <%
    int sum = 0;
    for (int i = 1; i <=100 ; i++) {
      sum+=i;
    }
    out.println("<h1>Sum="+sum+"</h1>");
  %>

  </body>
</html>
  • 脚本片段的再实现
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>

  <%
    int x = 10;
    out.println(x);
  %>
  <p>这是一个JSP文档</p>
  <%
    int num = 2;
    out.println(num);
  %>

  <hr>

  <%--在代码嵌入HTML元素--%>
  <%
    for (int i = 0; i < 5; i++) {
  %>
  <h1>Hello,World  <%=i%> </h1>
  <%
    }
  %>

  </body>
</html>
JSP声明
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>

  <%!
    static {
      System.out.println("Loading Servlet!");
    }

    private int globalVar = 0;

    public void guo(){
      System.out.println("进入了方法guo!");
    }
  %>

  </body>
</html>
  • JSP声明:会被编译到JSP生成Java的类中!其他的,就会被生成到_jspService方法中!
  • 在JSP,嵌入Java代码即可!
jsp 复制代码
<%%>
<%=%>
<%!%>

<%--注释--%>
  • JSP的注释,不会在客户端显示,HTML就会!

4.JSP指令

404与500页面实现

  • jsp2.jsp
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<%--定制错误页面--%>
<%--<%@ page errorPage="error/500.jsp" %>--%>
<html>

<head>
    <title>Title</title>
</head>
<body>

<%
    int x = 1/0;
%>

</body>
</html>
  • 404.jsp
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <img src="${pageContext.request.contextPath}/img/2-404.png" alt="404">
</body>
</html>
  • 500.jsp
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h2>自定义500错误的界面</h2>
<img src="${pageContext.request.contextPath}/img/1-500.png" alt="500">
</body>
</html>
  • web.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <error-page>
        <error-code>404</error-code>
        <location>/error/404.jsp</location>
    </error-page>
    <error-page>
        <error-code>500</error-code>
        <location>/error/500.jsp</location>
    </error-page>

</web-app>

头部和尾部页面拼接

  • footer.jsp
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>footer</title>
</head>
<body>

<h1>我是footer</h1>

</body>
</html>
  • header.jsp
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>head</title>
</head>
<body>

<h1>我是header</h1>

</body>
</html>
  • jsp3.jsp
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>jsp3</title>
</head>
<body>
    <%-- @include会将两个页面合二为一 --%>

    <%@include file="common/header.jsp"%>
    <h1>网页主体</h1>
    <%@include file="common/footer.jsp"%>

    <hr>

    <%--
        jsp标签
        jsp:include:拼接页面,本质还是三个
    --%>
    <jsp:include page="/common/header.jsp"/>
    <h1>网页主体</h1>
    <jsp:include page="/common/footer.jsp"/>

</body>
</html>

5. 9大内置对象

  • PageContext 存东西
  • Request 存东西
  • Response
  • Session 存东西
  • Application 【SerlvetContext】 存东西
  • config 【SerlvetConfig】
  • out
  • page ,不用了解
  • exception
  1. pageContextPageContext 对象用于存储和访问有关当前 JSP 页面的信息,如请求和响应对象,以及其他内置对象。它提供了各种方法来获取请求参数、会话对象等,并用于在页面之间传递数据。

  2. requestHttpServletRequest 对象代表客户端的请求,包含了客户端发送给服务器的请求信息。通过 request 对象,可以获取请求参数、请求头信息等。

  3. responseHttpServletResponse 对象代表服务器对客户端的响应,用于向客户端发送响应数据。通过 response 对象,可以设置响应头、写入响应体等。

  4. sessionHttpSession 对象代表客户端与服务器之间的会话,用于存储客户端的状态信息。通过 session 对象,可以存储和获取会话属性,实现用户状态的跟踪和管理。

  5. application(也称为 ServletContext):ServletContext 对象代表整个 Web 应用程序的上下文,提供了在整个应用程序范围内共享数据的功能。通过 application 对象,可以存储和获取应用程序范围的属性,访问初始化参数等。

  6. config(也称为 ServletConfig):ServletConfig 对象代表 Servlet 的配置信息,用于获取 Servlet 的初始化参数等。通过 config 对象,可以获取 Servlet 的配置信息,实现对 Servlet 的灵活配置。

  7. outJspWriter 对象用于向客户端输出内容,通常用于将动态生成的 HTML 或文本发送到浏览器端。

  8. pageObject 类型,表示当前 JSP 页面的实例。可以使用 page 对象来调用当前页面的方法,访问页面的属性等。

  9. exceptionThrowable 类型,表示在页面处理期间发生的异常。当 JSP 页面发生异常时,容器会将异常对象绑定到 exception 内置对象,开发者可以使用它来获取异常信息并进行处理。

java 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%--内置对象--%>
<%
    pageContext.setAttribute("name1","天启1号"); // 保存的数据只在一个页面中有效
    request.setAttribute("name2","天启2号"); // 保存的数据只在一次请求中有效,请求转发会携带这个数据
    session.setAttribute("name3","天启3号"); // 保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
    application.setAttribute("name4","天启4号");  // 保存的数据只在服务器中有效,从打开服务器到关闭服务器
%>

<%--
    脚本片段中的代码,会被原封不动生成到.jsp.java
    要求:这里面的代码,必须保证Java语法的正确性
--%>

<%
    // 从pageContent取出,我们通过寻找的方式来
    // 从底层到高层(作用域):
    String name1 = (String) pageContext.findAttribute("name1");
    String name2 = (String) pageContext.findAttribute("name2");
    String name3 = (String) pageContext.findAttribute("name3");
    String name4 = (String) pageContext.findAttribute("name4");
    String name5 = (String) pageContext.findAttribute("name5"); // 作用域

%>

<%--使用EL表达式输出 ${} --%>
<h1>取出的值:</h1>
<h3>${name1}</h3>
<h3>${name2}</h3>
<h3>${name3}</h3>
<h3>${name4}</h3>
<h3> <%=name5%> </h3>

</body>
</html>
  1. pageContext.setAttribute("name1","天启1号");:这行代码使用了 pageContext 对象,将一个名为 "name1",值为 "天启1号" 的属性保存在当前页面的范围内。这意味着这个属性只在当前 JSP 页面中有效,对其他页面不可见。

  2. request.setAttribute("name2","天启2号");:这行代码使用了 request 对象,将一个名为 "name2",值为 "天启2号" 的属性保存在当前请求的范围内。这意味着这个属性只在当前请求中有效,在请求转发时会携带这个数据,但对其他请求不可见。

  3. session.setAttribute("name3","天启3号");:这行代码使用了 session 对象,将一个名为 "name3",值为 "天启3号" 的属性保存在当前会话的范围内。这意味着这个属性只在当前会话中有效,从客户端打开浏览器到关闭浏览器期间保持有效。

  4. application.setAttribute("name4","天启4号");:这行代码使用了 application 对象,将一个名为 "name4",值为 "天启4号" 的属性保存在整个应用程序的范围内。这意味着这个属性在整个服务器中有效,从服务器启动到关闭期间保持有效,对所有用户和会话都可见。

如果EL表达式不生效,请在JSP页面最上面加上:<%@page isELIgnored="false" %>

  • request:客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻,用户看完没用的!
  • session:客户端向服务器发送请求,产生的数据,用户用完一会还有用,比如:购物车;
  • application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用,比如:聊天数据;

6.JSP标签.JSTL标签.EL表达式

JSP(JavaServer Pages)标签,JSTL(JSP Standard Tag Library)标签,和 EL(Expression Language,表达式语言)是用于在 JSP 页面中编写动态内容的三种重要技术。

  1. JSP 标签(JavaServer Pages Tags)
    JSP 标签是一种用于在 JSP 页面中嵌入 Java 代码的机制。JSP 标签可以分为两种类型:指令标签和动作标签。
  • 指令标签 :用于在 JSP 页面中设置全局配置信息,如页面编码、导入 Java 类等。常见的指令标签包括 <%@ page %><%@ include %><%@ taglib %> 等。
  • 动作标签 :用于执行特定的动作或操作,比如向页面输出内容、循环处理数据、条件判断等。动作标签以 <jsp:...> 开头,常见的动作标签有 <jsp:include><jsp:forward><jsp:useBean> 等。
  1. JSTL 标签(JSP Standard Tag Library)
    JSTL 是一组自定义标签库,提供了更丰富、更高层次的标签和功能,以简化 JSP 页面的开发。JSTL 包括四个主要的标签库:
  • 核心标签库(Core Tag Library):提供了常用的循环、条件判断、输出等功能。
  • 格式化标签库(Formatting Tag Library):用于格式化日期、数字等内容。
  • SQL 标签库(SQL Tag Library):提供了用于执行 SQL 查询和操作数据库的标签。
  • XML 标签库(XML Tag Library):用于处理 XML 数据。
  1. EL 表达式(Expression Language)
    EL 是一种用于在 JSP 页面中访问 Java 对象和数据的简洁语言。EL 表达式以 ${...} 的形式嵌入到 JSP 页面中,用于获取和操作变量、属性、集合等数据。EL 表达式可以访问各种作用域中的数据,如页面作用域、请求作用域、会话作用域等,还可以调用 JavaBean 的属性和方法。
  • EL表达式: ${ }
    • 获取数据
    • 执行运算
    • 获取web开发的常用对象
xml 复制代码
<!-- JSTL表达式的依赖 -->
<dependency>
    <groupId>javax.servlet.jsp.jstl</groupId>
    <artifactId>jstl-api</artifactId>
    <version>1.2</version>
</dependency>
<!-- standard标签库 -->
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>
  • JSP标签
  • jspTag.jsp
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>jspTag</title>
</head>
<body>

<h1>Tag1</h1>

<%--jsp:include--%>

<%--
http://localhost:8080/Jsp/jspTag.jsp?name=subeily&age=18
--%>

<jsp:forward page="/jspTag2.jsp">
    <jsp:param name="name" value="subeiLY"/>
    <jsp:param name="age" value="18"/>
</jsp:forward>


</body>
</html>
  • jspTag2.jsp
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>jspTag2</title>
</head>
<body>

<h1>Tag2</h1>

<%--取出参数--%>

名字:<%=request.getParameter("name")%>
年龄:<%=request.getParameter("age")%>

</body>
</html>
  • JSTL表达式
    • JSTL标签库的使用就是为了弥补HTML标签的不足;它自定义许多标签,可以供我们使用,标签的功能和Java代码一样!
  • 格式化标签
  • SQL标签
  • XML 标签
  • 核心标签 (掌握部分)
  • JSTL标签库使用步骤

    • 引入对应的 taglib;
    • 使用其中的方法;
    • 在Tomcat 的lib目录下也需要引入 jstl-api-1.2.jar、standard-1.1.2.jar的包,否则会报错:JSTL解析错误
  • c:if

js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<%--引入jstl核心标签库--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>core-if</title>
</head>
<body>

<h4>if测试</h4>

<hr>

<form action="core-if.jsp" method="get">
    <%--
        EL表达式获取表单中的数据
        ${param.参数名}
    --%>
    <input type="text" name="username" value="${param.username}">
    <input type="submit" value="登录">
</form>

<%--判断如果提交的用户名是管理员,则登录成功--%>
<c:if test="${param.username=='admin'}" var="isAdmin">
    <c:out value="管理员欢迎您!"/>
</c:if>

<%--自闭合标签--%>
<c:out value="${isAdmin}"/>

</body>
</html>
  • c:choose c:when
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<%--引入jstl核心标签库--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>core-for</title>
</head>
<body>

<%--定义一个变量score,值为85--%>
<c:set var="score" value="65"/>

<c:choose>
    <c:when test="${score>=90}">
        你的成绩为优秀
    </c:when>
    <c:when test="${score>=80}">
        你的成绩为一般
    </c:when>
    <c:when test="${score>=70}">
        你的成绩为良好
    </c:when>
    <c:when test="${score<=60}">
        你的成绩为不及格
    </c:when>
</c:choose>

</body>
</html>
  • c:forEach
js 复制代码
<%@ page import="java.util.ArrayList" %>
<%@ page contentType="text/html;charset=UTF-8" %>
<%--引入jstl核心标签库--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>core-foreach</title>
</head>
<body>

<%

    ArrayList<String> people = new ArrayList<>();
    people.add(0,"张三");
    people.add(1,"李四");
    people.add(2,"王五");
    people.add(3,"赵六");
    people.add(4,"田六");
    request.setAttribute("list",people);
%>

<%--
var , 每一次遍历出来的变量
items, 要遍历的对象
begin,   哪里开始
end,     到哪里
step,   步长
--%>
<c:forEach var="people" items="${list}">
    <c:out value="${people}"/> <br>
</c:forEach>

<hr>

<c:forEach var="people" items="${list}" begin="1" end="3" step="1" >
    <c:out value="${people}"/> <br>
</c:forEach>

</body>
</html>

9.JavaBean

实体类,JavaBean有特定的写法:

  • 必须要有一个无参构造
  • 属性必须私有化
  • 必须有对应的get/set方法;

一般用来和数据库的字段做映射 ORM;

ORM :对象关系映射

  • 表--->类
  • 字段-->属性
  • 行记录---->对象

people表

id name age address
1 饺子1号 3 成都
2 饺子2号 18 成都
3 饺子3号 85 成都
java 复制代码
class People{
    private int id;
    private String name;
    private int age;
    private String address;
}
java 复制代码
class A{
    new People(1,"饺子1号",3,"成都");
    new People(2,"饺子1号",18,"成都");
    new People(3,"饺子1号",85,"成都");
}
  • javaBean.jsp
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<%
//    People people = new People();
//    people.setAddress();
//    people.setId();
//    people.getAge()
//    people.setName();

%>

<jsp:useBean id="people" class="com.github.pojo.People" />

<jsp:setProperty name="people" property="address" value="成都"/>
<jsp:setProperty name="people" property="id" value="1"/>
<jsp:setProperty name="people" property="age" value="2"/>
<jsp:setProperty name="people" property="name" value="哇哈哈AD钙"/>

姓名:<jsp:getProperty name="people" property="name"/>
 ID:<jsp:getProperty name="people" property="id"/>
年龄:<jsp:getProperty name="people" property="age"/>
地址:<jsp:getProperty name="people" property="address"/>

</body>
</html>
  • 过滤器
  • 文件上传
  • 邮件发送
  • JDBC 复习 : 如何使用JDBC , JDBC crud, jdbc 事务

10.MVC三层架构

  • 什么是MVC: Model view Controller 模型.视图.控制器

1.早些年

  • 用户直接访问控制层,控制层就可以直接操作数据库;
java 复制代码
servlet--CRUD-->数据库
弊端:程序十分臃肿,不利于维护  
servlet的代码中:处理请求.响应.视图跳转.处理JDBC.处理业务代码.处理逻辑代码

架构:没有什么是加一层解决不了的!
程序猿调用
|
JDBC
|
Mysql Oracle SqlServer ....

2.MVC三层架构

Model

  • 业务处理 :业务逻辑(Service)
  • 数据持久层:CRUD (Dao)

View

  • 展示数据
  • 提供链接发起Servlet请求 (a,form,img...)

Controller (Servlet)

  • 接收用户的请求 :(req:请求参数.Session信息....)

  • 交给业务层处理对应的代码

  • 控制视图的跳转

    java 复制代码
    登录(View)
    --->接收用户的登录请求(Controller)
    --->处理用户的请求(获取用户登录的参数,username,password)(Controller)
    ---->交给业务层处理登录业务(判断用户名密码是否正确:事务)(Model)
    --->Dao层查询用户名和密码是否正确(Model)
    -->数据库

MVC(Model-View-Controller)是一种常用的软件架构模式,通常用于构建用户界面(UI)和 Web 应用程序。它将应用程序分为三个主要组件:模型(Model)、视图(View)、和控制器(Controller)。这三个组件各自负责不同的责任,并通过定义清晰的界限来促进代码的模块化、可维护性和可扩展性。

  1. 模型(Model)
  • 模型代表应用程序中的数据和业务逻辑。
  • 它负责管理应用程序的状态,处理数据逻辑,并与数据库、文件系统或其他数据源进行交互。
  • 模型通常包含实体类、数据访问对象(DAO)、服务类等。
  • 在一个典型的应用程序中,模型层通常是最底层的,不依赖于其他层,但其他层可以依赖于模型层。
  1. 视图(View)
  • 视图是用户界面的呈现层,负责将模型中的数据渲染成用户可以理解和操作的形式。
  • 视图通常是用户界面、HTML 页面、图表等。
  • 视图是模型的被动观察者,它通常不会直接操作模型,而是通过控制器来获取或更新模型的数据。
  1. 控制器(Controller)
  • 控制器是模型和视图之间的协调者,负责处理用户的输入并作出相应的响应。
  • 控制器接收用户的请求,调用适当的模型来处理请求,并选择合适的视图来显示处理结果。
  • 控制器通常包含了业务逻辑的一部分,但它的主要责任是将用户请求路由到正确的模型和视图。

MVC 三层架构通过将应用程序分为三个独立的组件,有助于降低代码的耦合度,提高代码的可维护性和可扩展性。它也使团队能够更轻松地分工合作,例如设计团队可以专注于视图的设计,开发团队可以专注于模型和控制器的实现。

11.Filter 过滤器 (重点)

Filter:过滤器 ,用来过滤网站的数据;

  • 处理中文乱码
  • 登录验证....

Filter开发步骤:

  1. 导包

  2. 编写过滤器

  3. 导包不要错;

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <parent>
          <artifactId>JavaWeb-02-Servlet</artifactId>
          <groupId>com.github</groupId>
          <version>1.0-SNAPSHOT</version>
      </parent>
      <modelVersion>4.0.0</modelVersion>
    
      <artifactId>Filter</artifactId>
    
      <dependencies>
          <!--   Servlet 依赖   -->
          <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>servlet-api</artifactId>
              <version>2.5</version>
          </dependency>
          <!--        JSP 依赖   -->
          <dependency>
              <groupId>javax.servlet.jsp</groupId>
              <artifactId>javax.servlet.jsp-api</artifactId>
              <version>2.3.3</version>
          </dependency>
          <!--        JSTL表达式的依赖-->
          <dependency>
              <groupId>javax.servlet.jsp.jstl</groupId>
              <artifactId>jstl-api</artifactId>
              <version>1.2</version>
          </dependency>
          <!--        standard标签库-->
          <dependency>
              <groupId>taglibs</groupId>
              <artifactId>standard</artifactId>
              <version>1.1.2</version>
          </dependency>
          <!--    连接数据库-->
          <dependency>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
              <version>5.1.47</version>
          </dependency>
      </dependencies>
    
    </project>
  1. 实现Filter接口,重写对应的方法即可;
java 复制代码
package com.github.filter;

import javax.servlet.*;
import java.io.IOException;

public class CharacterEncodingFilter implements Filter {

   /**     * 初始化:web服务器启动,就以及初始化了,随时等待过滤对象出现!     */
   public void init(FilterConfig filterConfig) {
       System.out.println("CharacterEncodingFilter初始化");
   }

   /**     * Chain : 链     *      * 1. 过滤中的所有代码,在过滤特定请求的时候都会执行     * 2. 必须要让过滤器继续同行     *    chain.doFilter(request,response);     */
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
       request.setCharacterEncoding("utf-8");
       response.setCharacterEncoding("utf-8");
       response.setContentType("text/html;charset=UTF-8");

       System.out.println("CharacterEncodingFilter执行前....");
       // 让我们的请求继续走,如果不写,程序到这里就被拦截停止!
       chain.doFilter(request,response);
       System.out.println("CharacterEncodingFilter执行后....");
   }

   /**     * 销毁:web服务器关闭的时候,过滤会销毁     */
   public void destroy() {
       System.out.println("CharacterEncodingFilter销毁");
   }
}
  1. 在web.xml中配置 Filter;
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
        version="4.0">

   <filter>
       <filter-name>CharacterEncodingFilter</filter-name>
       <filter-class>com.github.filter.CharacterEncodingFilter</filter-class>
   </filter>
   <filter-mapping>
       <filter-name>CharacterEncodingFilter</filter-name>
       <!--只要是 /servlet的任何请求,会经过这个过滤器-->
       <url-pattern>/servlet/*</url-pattern>
       <!--<url-pattern>/*</url-pattern>-->
   </filter-mapping>

</web-app>

12.监听器 Listener

实现一个监听器的接口;(有N种)

  1. 编写一个监听器;

实现监听器的接口...

java 复制代码
package com.github.listener;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/** * @author: subei * @Description: 统计网站在线人数 : 统计session */
public class OnlineCountListener implements HttpSessionListener {
   /**
     * 创建session监听: 看你的一举一动
     * 一旦创建Session就会触发一次这个事件!
     * @param se
     */
   public void sessionCreated(HttpSessionEvent se) {
       ServletContext ctx = se.getSession().getServletContext();

       System.out.println(se.getSession().getId());

       Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");

       if (onlineCount==null){
           onlineCount = new Integer(1);
       }else {
           int count = onlineCount.intValue();
           onlineCount = new Integer(count+1);
       }

       ctx.setAttribute("OnlineCount",onlineCount);
   }

   /**
     * 销毁session监听
     * 一旦销毁Session就会触发一次这个事件!
     * @param se     */
   public void sessionDestroyed(HttpSessionEvent se) {
       ServletContext ctx = se.getSession().getServletContext();

       Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");

       if (onlineCount==null){
           onlineCount = new Integer(0);
       }else {
           int count = onlineCount.intValue();
           onlineCount = new Integer(count-1);
       }

       ctx.setAttribute("OnlineCount",onlineCount);
   }
   /**
     * Session销毁:
     * 1. 手动销毁  getSession().invalidate();
     * 2. 自动销毁     */
}
  1. web.xml中注册监听器;
xml 复制代码
<!--注册监听器-->
<listener>
   <listener-class>com.github.listener.OnlineCountListener</listener-class>
</listener>
  1. 看情况是否使用!

Filter和Listener是在Java Web应用程序中常用的两种组件,它们用于在应用程序的生命周期中执行特定的任务或操作。

  1. Filter过滤器
  • 过滤器是用于在请求到达Servlet之前或响应离开Servlet之前拦截、过滤HTTP请求和响应的组件。
  • 过滤器可以用于执行诸如身份验证、日志记录、字符编码转换等任务,而无需修改Servlet本身的代码。
  • 过滤器可以被配置为拦截特定的URL模式,以便只对符合条件的请求进行处理。
  • 过滤器实现javax.servlet.Filter接口,并通过doFilter()方法来实现过滤逻辑。
  • 典型的应用包括字符编码过滤器、身份验证过滤器、日志记录过滤器等。
  1. Listener监听器
  • 监听器是用于监视Web应用程序中的事件并采取相应行动的组件。
  • 监听器可以监听到ServletContext、HttpSession和ServletRequest等不同范围的事件。
  • 监听器通常用于执行与应用程序生命周期相关的任务,例如在应用程序启动或关闭时执行某些操作,或者在会话创建或销毁时执行某些操作。
  • 监听器实现javax.servlet.ServletContextListener、javax.servlet.http.HttpSessionListener等接口,并通过相应的回调方法(如contextInitialized()、contextDestroyed()等)来响应事件。
  • 典型的应用包括应用程序启动时的初始化、会话管理、属性变化监听等。

虽然Filter和Listener在Java Web应用程序中都用于处理请求和响应,但它们的用途和功能略有不同。Filter主要用于拦截和处理HTTP请求和响应,而Listener主要用于监听和响应应用程序中的事件。在实际应用中,它们经常一起使用,以实现更复杂的功能和逻辑。

13.过滤器.监听器常见应用

  • 监听器:GUI编程中经常使用;
java 复制代码
package com.github.listener;

import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class TestPanel {
    public static void main(String[] args) {
        // 新建一个窗体
        Frame frame = new Frame("建军节快乐");  
        // 面板
        Panel panel = new Panel(null);
        // 设置窗体的布局
        frame.setLayout(null); 

        frame.setBounds(300,300,500,500);
        // 设置背景颜色1
        frame.setBackground(new Color(68, 227, 177)); 

        panel.setBounds(50,50,300,300);
        // 设置背景颜色2
        panel.setBackground(new Color(255, 242,0)); 

        frame.add(panel);

        frame.setVisible(true);

        // 监听事件,监听关闭事件
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                super.windowClosing(e);
            }
        });

    }
}

案例:用户登录之后才能进入主页!用户注销后就不能进入主页了!

  1. 用户登录之后,向Sesison中放入用户的数据

  2. 进入主页的时候要判断用户是否已经登录;要求:在过滤器中实现!

  • 因为:个人tomcat配置的为 /Filer 如下图:
  • LoginServlet.java
java 复制代码
package com.github.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        super.doGet(req,resp);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取前端请求的参数
        String username = req.getParameter("username");

        // 登陆成功
        if("admin".equals(username)){
            req.getSession().setAttribute("USER_SESSION",req.getSession().getId());
            resp.sendRedirect("/Filer/sys/success.jsp");

        } else {    // 登陆失败
            resp.sendRedirect("/Filer/error.jsp");
        }
    }
}
  • LogoutServlet.java
java 复制代码
package com.github.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Object user_session = req.getSession().getAttribute("USER_SESSION");
        if(user_session!=null){
            req.getSession().removeAttribute("USER_SESSION");
            resp.sendRedirect("/Filer/Login.jsp");
        } else {
            resp.sendRedirect("/Filer/Login.jsp");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req,resp);
    }
}

在extends HttpServlet的Servlet中,什么时候在doGet里面写,什么时候在doPost里写?

  1. doGet()方法
  • 当客户端发送一个HTTP GET请求时,Servlet容器会调用doGet()方法来处理该请求。
  • doGet()方法通常用于从服务器获取数据,通常是查询或检索数据,但不会对服务器上的资源进行更改。
  • GET请求中的参数通常通过URL的查询字符串传递,可以通过HttpServletRequest对象的getParameter()方法来获取这些参数。
  • 例如,当你想要获取某个资源的信息或者执行一些只读操作时,通常会在doGet()方法中编写相应的逻辑。
  1. doPost()方法
  • 当客户端发送一个HTTP POST请求时,Servlet容器会调用doPost()方法来处理该请求。
  • doPost()方法通常用于向服务器提交数据,通常是向数据库中添加或修改数据。
  • POST请求中的参数通常通过HTTP请求体传递,因此需要使用HttpServletRequest对象的getInputStream()或getReader()方法来读取请求体中的数据。
  • 例如,当你想要向服务器提交表单数据或执行一些需要更新服务器上数据的操作时,通常会在doPost()方法中编写相应的逻辑。

总的来说,doGet()方法用于处理获取数据的请求,而doPost()方法用于处理提交数据的请求。在实际应用中,通常会根据请求的类型和需要执行的操作,选择在doGet()或doPost()方法中编写相应的逻辑。

  • Login.jsp
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>Login</title>
</head>
<body>

<h1>登陆界面</h1>
<form action="/Filer/servlet/login" method="post">
    <input type="text" name="username">
    <input type="submit">
</form>

</body>
</html>
  • success.jsp
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>成功</title>
</head>
<body>

<%--<%--%>
<%--    Object userSession = request.getSession().getAttribute("USER_SESSION");--%>
<%--    if(userSession==null){--%>
<%--        response.sendRedirect("Filer/Login.jsp");--%>
<%--    }--%>
<%--%>--%>

<h1>主页</h1>

<p><a href="/Filer/servlet/logout">注销</a> </p>

</body>
</html>
  • error.jsp
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>error</title>
</head>
<body>

<h1>错误</h1>
<h3>没有权限,用户名错误</h3>

<p> <a href="/Filer/Login.jsp">返回登录主页</a></p>

</body>
</html>
  • 进行登录注销无法登录判断;
  • Constant.java
java 复制代码
package com.github.Util;

public class Constant {
    public static String USER_SESSION="USER_SESSION";
}
  • SysFilter.java
java 复制代码
package com.github.listener;

import com.github.Util.Constant;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class SysFilter implements Filter {

    public void init(FilterConfig filterConfig) throws ServletException {

    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        if (request.getSession().getAttribute(Constant.USER_SESSION)==null){
            response.sendRedirect("/Filer/error.jsp");
        }

        chain.doFilter(request,response);
    }

    public void destroy() {

    }
}
  • web.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.github.servlet.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/servlet/login</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>LogoutServlet</servlet-name>
        <servlet-class>com.github.servlet.LogoutServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>LogoutServlet</servlet-name>
        <url-pattern>/servlet/logout</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>SysFilter</filter-name>
        <filter-class>com.github.listener.SysFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>SysFilter</filter-name>
        <url-pattern>/sys/*</url-pattern>
    </filter-mapping>

</web-app>
  • 运行结果如下:

14.JDBC

  • 什么是JDBC : Java连接数据库!

需要jar包的支持:

  • java.sql
  • javax.sql
  • mysql-conneter-java... 连接驱动(必须要导入)

实验环境搭建

sql 复制代码
USE jdbc;

CREATE TABLE users(
    id INT PRIMARY KEY,
    `name` VARCHAR(40),
    `password` VARCHAR(40),
    email VARCHAR(60),
    birthday DATE
);

INSERT INTO users(id,`name`,`password`,email,birthday)
VALUES(1,'张三','123456','zs@qq.com','2000-01-01');
INSERT INTO users(id,`name`,`password`,email,birthday)
VALUES(2,'李四','123456','ls@qq.com','2000-01-01');
INSERT INTO users(id,`name`,`password`,email,birthday)
VALUES(3,'王五','123456','ww@qq.com','2000-01-01');

SELECT    * FROM users;
  • 导入数据库依赖
xml 复制代码
<!--mysql的驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>
  • IDEA中连接数据库:
  • JDBC 固定步骤:
  1. 加载驱动
  2. 连接数据库,代表数据库
  3. 向数据库发送SQL的对象Statement : CRUD
  4. 编写SQL (根据业务,不同的SQL)
  5. 执行SQL
  6. 关闭连接
java 复制代码
package com.github.test;

import java.sql.*;

public class TestJdbc {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        // 配置信息
        // useUnicode=true&characterEncoding=utf-8 解决中文乱码
        String url="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
        String username = "root";
        String password = "root";

        // 1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2.连接数据库,代表数据库
        Connection connection = DriverManager.getConnection(url, username, password);

        // 3.向数据库发送SQL的对象Statement,PreparedStatement : CRUD
        Statement statement = connection.createStatement();

        // 4.编写SQL
        String sql = "select * from users";

        // 5.执行查询SQL,返回一个 ResultSet  : 结果集
        ResultSet rs = statement.executeQuery(sql);

        while (rs.next()){
            System.out.println("id="+rs.getObject("id"));
            System.out.println("name="+rs.getObject("name"));
            System.out.println("password="+rs.getObject("password"));
            System.out.println("email="+rs.getObject("email"));
            System.out.println("birthday="+rs.getObject("birthday"));
        }

        // 6.关闭连接,释放资源(一定要做) 先开后关
        rs.close();
        statement.close();
        connection.close();
    }
}
  • 预编译SQL
java 复制代码
package com.github.test;

import java.sql.*;

public class TestJDBC2 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 配置信息
        // useUnicode=true&characterEncoding=utf-8 解决中文乱码
        String url="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
        String username = "root";
        String password = "root";

        // 1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 2.连接数据库,代表数据库
        Connection connection = DriverManager.getConnection(url, username, password);

        // 3.编写SQL
        String sql = "insert into  users(id, name, password, email, birthday) values (?,?,?,?,?);";

        // 4.预编译
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        // 给第一个占位符? 的值赋值为1;
        preparedStatement.setInt(1,2);
        // 给第二个占位符? 的值赋值为狂神说Java;
        preparedStatement.setString(2,"哇哈哈AD钙");
        // 给第三个占位符? 的值赋值为123456;
        preparedStatement.setString(3,"123456");
        // 给第四个占位符? 的值赋值为1;
        preparedStatement.setString(4,"24736743@qq.com");
        // 给第五个占位符? 的值赋值为new Date(new java.util.Date().getTime());
        preparedStatement.setDate(5,new Date(new java.util.Date().getTime()));

        // 5.执行SQL
        int i = preparedStatement.executeUpdate();

        if (i>0){
            System.out.println("插入成功@");
        }

        // 6.关闭连接,释放资源(一定要做) 先开后关
        preparedStatement.close();
        connection.close();
    }
}

事务

  • 要么都成功,要么都失败!

  • ACID原则:保证数据的安全。

java 复制代码
开启事务
事务提交  commit()
事务回滚  rollback()
关闭事务

转账:
A:1000
B:1000

A(900)   --100-->   B(1100) 

Junit单元测试

  • 依赖
xml 复制代码
<!--单元测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

简单使用

  • @Test注解只有在方法上有效,只要加了这个注解的方法,就可以直接运行!
java 复制代码
@Test
public void test(){
    System.out.println("Hello");
}
  • 失败的时候是红色:
  • 搭建一个环境
sql 复制代码
USE jdbc;

CREATE TABLE account(
   id INT PRIMARY KEY AUTO_INCREMENT,
   `name` VARCHAR(40),
   money FLOAT
);

INSERT INTO account(`name`,money) VALUES('A',1000);
INSERT INTO account(`name`,money) VALUES('B',1000);
INSERT INTO account(`name`,money) VALUES('C',1000);
java 复制代码
package com.github.test;

import org.junit.Test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class TestJdbc3 {
    @Test
    public void test() {
        // 配置信息
        // useUnicode=true&characterEncoding=utf-8 解决中文乱码
        String url="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&characterEncoding=utf-8";
        String username = "root";
        String password = "root";

        Connection connection = null;

        // 1.加载驱动
        try {
            Class.forName("com.mysql.jdbc.Driver");
            // 2.连接数据库,代表数据库
            connection = DriverManager.getConnection(url, username, password);

            // 3.通知数据库开启事务,false 开启
            connection.setAutoCommit(false);

            String sql = "update account set money = money-100 where name = 'A'";
            connection.prepareStatement(sql).executeUpdate();

            // 制造错误
            // int i = 1/0;

            String sql2 = "update account set money = money+100 where name = 'B'";
            connection.prepareStatement(sql2).executeUpdate();

            // 以上两条SQL都执行成功了,就提交事务!
            connection.commit();
            System.out.println("success");
        } catch (Exception e) {
            try {
                // 如果出现异常,就通知数据库回滚事务
                connection.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }finally {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

15.smbms项目

参考smbms项目

16.文件传输原理及实现

此部分内容参考:博客园-Jpbito

  • 新建一个空项目;
  • 修改web.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0"
         metadata-complete="true">
</web-app>
  • 配置Tomcat,运行空项目。可运行再执行下面操作。
  • 整体框架;

具体实现

如何查看所下载的jar包中的源码是什么?

  • 双击打开下载的jar包;
  • 这些都是别人写好的Java代码编译之后的*.class文件,如果想要查看源码,可以使用反编译工具来获得,最简单的反编译工具就是我们使用的IDEA,直接将 *.class拷贝到IDEA中的文件夹,就可以查看它的源码了。
  • 为什么压缩的是.class文件?
    • 因为这个文件已经通过了编译器的编译,引入 *.class文件可以减少编译这些引用的代码的步骤与时间,直接拿过来就可以使用;更是一种原作者保护自己源码的一种手段!
  • 如何查看这些*.class中的源码?
    • 反编译。

手动导入需要的jar包。

  • 在IDEA中,新建一个lib包,复制jar包到lib文件夹。

上传文件都是通过使用表单实现的。

jsp 复制代码
<p>     
    <input type="file" name="file"> 
</p>
  • 注意:上传文件的表单是上面这么写的,但是如果一个表单中包含文件数据,那么该表单如果想要提交文件数据,那么它必须包含enctype属性,且属性值必须为:enctype="multipart/form-data"
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h2>Hello World!</h2>

<%--
    通过表单上传文件
    get:上传文件有大小限制
    post:上传文件无大小限制
--%>

<form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post">
    上传用户: <input type="text" name="username"><br>
    上传文件1: <input type="file" name="file1"><br>
    上传文件2: <input type="file" name="file2"><br>
    <input type="submit" value="提交文件">
</form>

</body>
</html>
  • 【面试:文件上传调优】
    1. 上传的文件存放在一个不能使用外界URL访问的目录下面;
    2. 上传到同一个文件夹中的文件名称应该唯一:使用时间戳/UUID/MD5等手段实现;
    3. 限制上传文件的最大值:因为服务器上硬盘资源很贵,不能让用户随意的使用;
    4. 限制文件上传类型:比如这个文件夹只用来存储图片,那你就不能上传一个.mp4的文件;

编写servlet

  • 前端jsp页面------index.jsp;
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h2>Hello World!</h2>

<%--
    通过表单上传文件
    get:上传文件有大小限制,只能提交4-5kb的数据
    post:上传文件无大小限制
    注意:文件一般比较大,所以上传文件都是使用post方式提交
    ${pageContext.request.contextPath}:获取到webapp路径
--%>

<form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post">
    上传用户: <input type="text" name="username"><br>
    上传文件1: <input type="file" name="file1"><br>
    上传文件2: <input type="file" name="file2"><br>
    <p> <input type="submit" value="提交文件">  | <input type="reset" value="重置"> </p>
</form>

</body>
</html>
  • 上传成功后的页面------success.jsp;
js 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>成功</title>
</head>
<body>

<%=request.getAttribute("msg")%>
<h2>🎉上传成功🎉</h2>

</body>
</html>
  • 新建servlet包
  • 修改web.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0"
         metadata-complete="true">

    <servlet>
        <servlet-name>upload</servlet-name>
        <servlet-class>com.github.servlet.FileSerlvet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>upload</servlet-name>
        <url-pattern>/upload.do</url-pattern>
    </servlet-mapping>
</web-app>
  • servlet层
java 复制代码
package com.github.servlet;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;


/** * @author subei */
public class FileSerlvet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse     *      response)     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        // response.getWriter().append("Served at: ").append(request.getContextPath());

        // 判断上传的文件普通表单还是带文件的表单
        if (!ServletFileUpload.isMultipartContent(request)) {
            return;//终止方法运行,说明这是一个普通的表单,直接返回
        }
        //创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访间上传的文件;
        String uploadPath =this.getServletContext().getRealPath("/WEB-INF/upload");
        File uploadFile = new File(uploadPath);
        if (!uploadFile.exists()){
            uploadFile.mkdir(); //创建这个月录
        }

        // 创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访问上传的文件
        String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
        File file = new File(tmpPath);
        if (!file.exists()) {
            file.mkdir();//创建临时目录
        }

        // 处理上传的文件,一般都需要通过流来获取,我们可以使用 request, getInputstream(),原生态的文件上传流获取,十分麻烦
        // 但是我们都建议使用 Apache的文件上传组件来实现, common-fileupload,它需要旅 commons-io组件;
        try {
            // 创建DiskFileItemFactory对象,处理文件路径或者大小限制
            DiskFileItemFactory factory = getDiskFileItemFactory(file);
            /*             * //通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件 factory.setSizeThreshold(1024 *             * 1024); //缓存区大小为1M factory.setRepository (file);//临时目录的保存目录,需要一个File             */

            // 2、获取ServletFileUpload
            ServletFileUpload upload = getServletFileUpload(factory);

            // 3、处理上传文件
            // 把前端请求解析,封装成FileItem对象,需要从ServletFileUpload对象中获取
            String msg = uploadParseRequest(upload, request, uploadPath);

            // Servlet请求转发消息
            System.out.println(msg);
            if(msg == "文件上传成功!") {
                // Servlet请求转发消息
                request.setAttribute("msg",msg);
                request.getRequestDispatcher("success.jsp").forward(request, response);
            }else {
                msg ="请上传文件";
                request.setAttribute("msg",msg);
                request.getRequestDispatcher("success.jsp").forward(request, response);
            }

        } catch (FileUploadException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
    }

    public static DiskFileItemFactory getDiskFileItemFactory(File file) {
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中;
        factory.setSizeThreshold(1024 * 1024);// 缓冲区大小为1M
        factory.setRepository(file);// 临时目录的保存目录,需要一个file
        return factory;
    }

    public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
        ServletFileUpload upload = new ServletFileUpload(factory);
        // 监听长传进度
        upload.setProgressListener(new ProgressListener() {

            // pBYtesRead:已读取到的文件大小
            // pContextLength:文件大小
            @Override
            public void update(long pBytesRead, long pContentLength, int pItems) {
                System.out.println("总大小:" + pContentLength + "已上传:" + pBytesRead);
            }
        });

        // 处理乱码问题
        upload.setHeaderEncoding("UTF-8");
        // 设置单个文件的最大值
        upload.setFileSizeMax(1024 * 1024 * 10);
        // 设置总共能够上传文件的大小
        // 1024 = 1kb * 1024 = 1M * 10 = 10м

        return upload;
    }

    public static String uploadParseRequest(ServletFileUpload upload, HttpServletRequest request, String uploadPath)
            throws FileUploadException, IOException {

        String msg = "";

        // 把前端请求解析,封装成FileItem对象
        List<FileItem> fileItems = upload.parseRequest(request);
        for (FileItem fileItem : fileItems) {
            if (fileItem.isFormField()) {// 判断上传的文件是普通的表单还是带文件的表单
                // getFieldName指的是前端表单控件的name;
                String name = fileItem.getFieldName();
                String value = fileItem.getString("UTF-8"); // 处理乱码
                System.out.println(name + ": " + value);
            } else {// 判断它是上传的文件

                // ============处理文件==============

                // 拿到文件名
                String uploadFileName = fileItem.getName();
                System.out.println("上传的文件名: " + uploadFileName);
                if (uploadFileName.trim().equals("") || uploadFileName == null) {
                    continue;
                }

                // 获得上传的文件名/images/girl/paojie.png
                String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);
                // 获得文件的后缀名
                String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1);

                /*                 * 如果文件后缀名fileExtName不是我们所需要的 就直按return.不处理,告诉用户文件类型不对。                 */

                System.out.println("文件信息[件名: " + fileName + " ---文件类型" + fileExtName + "]");
                // 可以使用UID(唯一识别的通用码),保证文件名唯
                // 0UID. randomUUID(),随机生一个唯一识别的通用码;
                String uuidPath = UUID.randomUUID().toString();

                // ================处理文件完毕==============

                // 存到哪? uploadPath
                // 文件真实存在的路径realPath
                String realPath = uploadPath + "/" + uuidPath;
                // 给每个文件创建一个对应的文件夹
                File realPathFile = new File(realPath);
                if (!realPathFile.exists()) {
                    realPathFile.mkdir();
                }
                // ==============存放地址完毕==============


                // 获得文件上传的流
                InputStream inputStream = fileItem.getInputStream();
                // 创建一个文件输出流
                // realPath =真实的文件夹;
                // 差了一个文件;加上翰出文件的名产"/"+uuidFileName
                FileOutputStream fos = new FileOutputStream(realPath + "/" + fileName);

                // 创建一个缓冲区
                byte[] buffer = new byte[1024 * 1024];
                // 判断是否读取完毕
                int len = 0;
                // 如果大于0说明还存在数据;
                while ((len = inputStream.read(buffer)) > 0) {
                    fos.write(buffer, 0, len);
                }
                // 关闭流
                fos.close();
                inputStream.close();

                msg = "文件上传成功!";
                fileItem.delete(); // 上传成功,清除临时文件
                //=============文件传输完成=============
            }
        }
        return msg;

    }
}
  • 运行后,如下情况:
  • 在pom.xml中添加jar包的maven;
xml 复制代码
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.4</version>
    </dependency>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.6</version>
    </dependency>
  • 再次运行;
  • 显示成功,但文件在哪里???

17.邮件发送原理及实现

1.概述

传输协议

  • SMTP协议
    发送邮件:
    我们通常把处理用户smtp请求(邮件发送请求)的服务器称之为SMTP服务器(邮件发送服务器)。
  • POP3协议
    接收邮件:
    我们通常把处理用户pop3请求(邮件接收请求)的服务器称之为POP3服务器(邮件接收服务器)。

邮件收发原理

  • 请参考:连接
    使用Java实现邮件发送需要使用到的类
  • 我们将用代码完成邮件的发送。这在实际项目中应用的非常广泛,比如注册需要发送邮件进行账号激活,再比如A项目中利用邮件进行任务提醒等等。
  • 使用Java发送Emai分简单,但是首先你应该准备 JavaMail API和 Java Activation framework。
  • 得到两个jar包:

导入jar包时,报错:Cannot resolve mail.jar:mail.jar:1.4

  • 解决:maven重新导入如下。
xml 复制代码
    <dependency>
      <groupId>javax.activation</groupId>
      <artifactId>activation</artifactId>
      <version>1.1</version>
    </dependency>
    <dependency>
        <groupId>javax.mail</groupId>
        <artifactId>mail</artifactId>
        <version>1.4.7</version>
    </dependency>
  • JavaMail是sωn公司(现以被甲骨文收购)为方便Java开发人员在应用程序中实现邮件发送和接收功能而提供的一套标准开发包,它支持一些常用的邮件协议,如前面所讲的SMTP,POP3,IMAP,还有MIME等。我们在使用 JavaMail!API编写邮件时,无须考虑邮件的底层实现细节,只要调用 Javamail开发包中相应的API类就可以了。

  • 我们可以先尝试发送一封简单的邮件,确保电脑可以连接网络。

    • 创建包含邮件服务器的网络连接信息的Session对象。
    • 创建代表邮件内容的Message对象;
    • 创建Transport对象,连接服务器,发送Message,关闭连接;
  • 主要有四个核心类,我们在编写程序时,记住这四个核心类,就很容易编写出Java邮件处理程序。

2.简单邮件

邮件分类

  • 简单邮件:没有除了文字以外的其他所有文件(包括附件和图片、视频等),即纯文本邮件;
  • 复杂邮件:除了传统的文字信息外,还包括了一些非文字数据的邮件;

需要发送邮件首先就要我们的邮箱账号支持POP3和SMTP协议,所以我们需要开启邮箱的POP3+SMTP服务,然后我们需要复制下图中的授权码,这个授权码就相当于你的QQ密码,你可以使用你的邮箱账号+授权码来发送邮件,而SMTP服务器也就是使用这个来识别你的身份的。

java 复制代码
package com.github.test;

import com.sun.mail.util.MailSSLSocketFactory;

import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;

/** * @Author: subei * @Description: 一封简单的邮件 */
public class MailDemo01 {
    public static void main(String[] args) throws Exception {
        Properties prop = new Properties();
        prop.setProperty("mail.host", "smtp.qq.com");
        // 设置QQ邮件服务器
        prop.setProperty("mail.transport.protocol", "smtp");
        // 邮件发送协议
        prop.setProperty("mail.smtp.auth", "true");
        // 需要验证用户名密码

        // 关于QQ邮箱,还要设置SSL加密,加上以下代码即可
        MailSSLSocketFactory sf = new MailSSLSocketFactory();
        sf.setTrustAllHosts(true);
        prop.put("mail.smtp.ssl.enable", "true");
        prop.put("mail.smtp.ssl.socketFactory", sf);

        // 使用JavaMail发送邮件的5个步骤

        // 1.创建定义整个应用程序所需的环境信息的 Session 对象
        // 使用QQ邮箱的时候才需要,其他邮箱不需要这一段代码
        Session session = Session.getDefaultInstance(prop, new Authenticator() {
            // 获取和SMTP服务器的连接对象
            @Override
            public PasswordAuthentication getPasswordAuthentication() {
                // 发件人邮件用户名、授权码
                return new PasswordAuthentication("XXXX@qq.com", "授权码");
            }
        });

        // 开启Session的debug模式,这样就可以查看到程序发送Email的运行状态
        session.setDebug(true);

        // 2.通过session得到transport对象
        Transport ts = session.getTransport();
        // 通过这一次和SMTP服务器的连接对象获取发送邮件的传输对象

        // 3.使用邮箱的用户名和授权码连上SMTP邮件服务器,即登陆
        ts.connect("smtp.qq.com", "XXXX@qq.com", "授权码");

        // 4.创建邮件对象MimeMessage------点击网页上的写信
        // 创建一个邮件对象
        MimeMessage message = new MimeMessage(session);
        // 指明邮件的发件人------在网页上填写发件人
        message.setFrom(new InternetAddress("XXXX@qq.com"));
        // 设置发件人
        // 指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发------在网页上填写收件人
        message.setRecipient(Message.RecipientType.TO, new InternetAddress("XXXX@qq.com"));
        // 设置收件人
        // 邮件的标题------在网页上填写邮件标题
        message.setSubject("简单邮件发送实现");
        // 设置邮件主题
        // 邮件的文本内容------在网页上填写邮件内容
        message.setContent("<h2 style='color:red'>你好啊!</h2>", "text/html;charset=UTF-8");
        // 设置邮件的具体内容

        // 5.发送邮件------在网页上点击发送按钮
        ts.sendMessage(message, message.getAllRecipients());

        // 6.关闭连接对象,即关闭服务器上的连接资源
        ts.close();
    }
}
  • 运行测试

3.复杂邮件

  • 复杂邮件就是非纯文本的邮件,可能还包含了图片和附件等资源。

  • MIME(多用途互联网邮件扩展类型)

  • 先认识两个类一个名词:

  • MimeBodyPart类

    • javax.mail.internet.MimeBodyPart类表示的是一个MIME消息,它和MimeMessage类一样都是从Part接口继承过来。
    • 即一个MIME消息对应一个MimeBodyPart对象,而MimeBodyPart对象就是我们写的邮件内容中的元素。
  • MimeMultipart类

    • javax.mail.internet.MimeMultipart是抽象类 Multipart的实现子类,它用来组合多个MIME消息。一个MimeMultipart对象可以包含多个代表MIME消息的MimeBodyPart对象。
    • 即一个MimeMultipart对象表示多个MimeBodyPart的集合,而一个MimeMultipart表示的就是我们一封邮件的内容。
  • MimeMultipart对象的使用的时候需要设置setSubType()的属性值,一共就下面3种取值:

    • alternative:表明这个MimeMultipart对象中的MimeMessage对象的数据是纯文本文件;
    • related:表明这个MimeMultipart对象中的MimeMessage对象的数据包含非纯文本文件;
    • mixed:表明这个MimeMultipart对象中的MimeMessage对象的数据包含附件;

    我们在使得的时候如果不知道使用哪一个,直接使用mixed即可,使用这个属性值一定不会报错。

  • 相较于简单邮件,复杂邮件变化的地方只是在于邮件内容本身会发送变化,而其他的步骤都是一样的:
    1. 准备一些参数;
    2. 获取session对象;
    3. 获取传输对象;
    4. 登陆授权;
    5. 写邮件 (和简单邮件相区别);
    6. 发送邮件;
    7. 关闭服务器资源。

发送包含图片的复杂邮件

java 复制代码
package com.github.test;

import com.sun.mail.util.MailSSLSocketFactory;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.util.Properties;

/** * @Author: subei * @Description: 图片的邮件 */
public class MailDemo01 {
    public static void main(String[] args) throws Exception {
        Properties prop = new Properties();
        prop.setProperty("mail.host", "smtp.qq.com");
        // 设置QQ邮件服务器
        prop.setProperty("mail.transport.protocol", "smtp");
        // 邮件发送协议
        prop.setProperty("mail.smtp.auth", "true");
        // 需要验证用户名密码

        // 关于QQ邮箱,还要设置SSL加密,加上以下代码即可
        MailSSLSocketFactory sf = new MailSSLSocketFactory();
        sf.setTrustAllHosts(true);
        prop.put("mail.smtp.ssl.enable", "true");
        prop.put("mail.smtp.ssl.socketFactory", sf);

        // 使用JavaMail发送邮件的5个步骤

        // 1.创建定义整个应用程序所需的环境信息的 Session 对象
        // 使用QQ邮箱的时候才需要,其他邮箱不需要这一段代码
        Session session = Session.getDefaultInstance(prop, new Authenticator() {
            // 获取和SMTP服务器的连接对象
            @Override
            public PasswordAuthentication getPasswordAuthentication() {
                // 发件人邮件用户名、授权码
                return new PasswordAuthentication("XXXX@qq.com", "授权码");
            }
        });

        // 开启Session的debug模式,这样就可以查看到程序发送Email的运行状态
        session.setDebug(true);

        // 2.通过session得到transport对象
        Transport ts = session.getTransport();
        // 通过这一次和SMTP服务器的连接对象获取发送邮件的传输对象

        // 3.使用邮箱的用户名和授权码连上SMTP邮件服务器,即登陆
        ts.connect("smtp.qq.com", "XXXX@qq.com", "授权码");

        // 4.创建邮件对象MimeMessage------点击网页上的写信
        // 创建一个邮件对象
        MimeMessage message = new MimeMessage(session);
        // 指明邮件的发件人------在网页上填写发件人
        message.setFrom(new InternetAddress("XXXX@qq.com"));
        // 设置发件人
        // 指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发------在网页上填写收件人
        message.setRecipient(Message.RecipientType.TO, new InternetAddress("XXXX@qq.com"));
        // 设置收件人
        // 邮件的标题------在网页上填写邮件标题
        message.setSubject("简单邮件发送实现");
        // 设置邮件主题

        System.out.println("=============================复杂邮件的邮件内容设置==================================");

        // 准备邮件数据

        // 准备图片数据
        // 获取一个图片的MimeBodyPart对象
        MimeBodyPart image = new MimeBodyPart();
        // 由于图片需要字符化才能传输,所以需要获取一个DataHandler对象
        DataHandler dh = new DataHandler(new FileDataSource("图片的绝对地址"));
        // 将图片序列化
        image.setDataHandler(dh);
        // 为图片的MimeBodyPart对象设置一个ID,我们在文字中就可以使用它了
        image.setContentID("p6.jpg");

        // 准备正文数据
        MimeBodyPart text = new MimeBodyPart();
        // 获取一个文本的MimeBodyPart对象
        text.setContent("这是一封邮件正文带图片<img src='cid:p6.jpg'>的邮件", "text/html;charset=UTF-8");
        // 设置文本内容,注意在里面嵌入了<img src='cid:p6.jpg'>

        // 描述数据关系
        // 获取MimeMultipart
        MimeMultipart mm = new MimeMultipart();
        // 将文本MimeBodyPart对象加入MimeMultipart中
        mm.addBodyPart(text);
        // 将图片MimeBodyPart对象加入MimeMultipart中
        mm.addBodyPart(image);
        // 设置MimeMultipart对象的相对熟悉为related,即发送的数据为文本+非附件资源
        mm.setSubType("related");

        // 设置到消息中,保存修改
        message.setContent(mm);
        // 将MimeMultipart放入消息对象中
        message.saveChanges();
        // 保存上面的修改

        System.out.println("===============================================================");

        // 5.发送邮件------在网页上点击发送按钮
        ts.sendMessage(message, message.getAllRecipients());

        // 6.关闭连接对象,即关闭服务器上的连接资源
        ts.close();
    }
}
  • 只需修改第5步:
java 复制代码
        // 准备图片数据
        // 获取一个图片的MimeBodyPart对象
        MimeBodyPart image = new MimeBodyPart();
        // 由于图片需要字符化才能传输,所以需要获取一个DataHandler对象
        DataHandler dh = new DataHandler(new FileDataSource("图片的绝对地址"));
        // 将图片序列化
        image.setDataHandler(dh);
        // 为图片的MimeBodyPart对象设置一个ID,我们在文字中就可以使用它了
        image.setContentID("p6.jpg");

        // 准备正文数据
        MimeBodyPart text = new MimeBodyPart();
        // 获取一个文本的MimeBodyPart对象
        text.setContent("这是一封邮件正文带图片<img src='cid:p6.jpg'>的邮件", "text/html;charset=UTF-8");
        // 设置文本内容,注意在里面嵌入了<img src='cid:p6.jpg'>

        // 描述数据关系
        // 获取MimeMultipart
        MimeMultipart mm = new MimeMultipart();
        // 将文本MimeBodyPart对象加入MimeMultipart中
        mm.addBodyPart(text);
        // 将图片MimeBodyPart对象加入MimeMultipart中
        mm.addBodyPart(image);
        // 设置MimeMultipart对象的相对熟悉为related,即发送的数据为文本+非附件资源
        mm.setSubType("related");

        // 设置到消息中,保存修改
        message.setContent(mm);
        // 将MimeMultipart放入消息对象中
        message.saveChanges();
  • 测试运行:

发送包含附件的复杂邮件

  • 需修改的代码:
java 复制代码
        System.out.println("=============================复杂邮件的邮件内容设置==================================");

        // 图片
        MimeBodyPart body1 = new MimeBodyPart();
        body1.setDataHandler(new DataHandler(new FileDataSource("图片的绝对地址")));
        // 图片设置ID
        body1.setContentID("some.png");

        // 文本
        MimeBodyPart body2 = new MimeBodyPart();
        body2.setContent("请注意,这是文本附件<img src='cid:test.png'>","text/html;charset=utf-8");

        // 附件
        MimeBodyPart body3 = new MimeBodyPart();
        body3.setDataHandler(new DataHandler(new FileDataSource("附件1的绝对地址")));
        // 附件设置名字
        body3.setFileName("demo.c");

        MimeBodyPart body4 = new MimeBodyPart();
        body4.setDataHandler(new DataHandler(new FileDataSource("附件2的绝对地址")));
        // 附件设置名字
        body4.setFileName("demo.txt");

        // 拼装邮件正文内容
        MimeMultipart multipart1 = new MimeMultipart();
        multipart1.addBodyPart(body1);
        multipart1.addBodyPart(body2);
        // 1.文本和图片内嵌成功!
        multipart1.setSubType("related");

        // new MimeBodyPart().setContent(multipart1);
        // 将拼装好的正文内容设置为主体
        MimeBodyPart contentText =  new MimeBodyPart();
        contentText.setContent(multipart1);

        // 拼接附件
        MimeMultipart allFile =new MimeMultipart();
        // 附件
        allFile.addBodyPart(body3);
        // 附件
        allFile.addBodyPart(body4);
        // 正文
        allFile.addBodyPart(contentText);
        // 正文和附件都存在邮件中,所有类型设置为mixed;
        allFile.setSubType("mixed");

        // 设置到消息中,保存修改
        message.setContent(allFile);
        // 将MimeMultipart放入消息对象中
        message.saveChanges();
        // 保存上面的修改

        System.out.println("===============================================================");
  • 测试运行:

4.网站注册发送邮件功能实现

  • 创建一个javaWeb项目;
  • 环境搭建完成,测试通过。
  • 导入maven依赖------pom.xml:
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>mail</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!--   Servlet 依赖   -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <!--        JSP 依赖   -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.3</version>
        </dependency>
        <!--        JSTL表达式的依赖-->
        <dependency>
            <groupId>javax.servlet.jsp.jstl</groupId>
            <artifactId>jstl-api</artifactId>
            <version>1.2</version>
        </dependency>
        <!--        standard标签库-->
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
        </dependency>

        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4.7</version>
        </dependency>

    </dependencies>

</project>
  • 拷贝前端素材;
    • index.jsp------注册页面;
    • info.jsp------提示成功页面;
jsp 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<%--注册填写邮箱的前端页面--%>
<html>
<head>
  <title>注册</title>
</head>
<body>

<form action="${pageContext.request.contextPath}/RegisterServlet.do" method="post">
  用户名:<input type="text" name="username"><br/>
  密码:<input type="password" name="password"><br/>
  邮箱:<input type="text" name="email"><br/>
  <input type="submit" value="注册">
</form>

</body>
</html>
jsp 复制代码
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>注册成功</title>
</head>
<body>

<h2>🎉注册成功🎉</h2>
${message}

</body>
</html>
  • 编写User.java
java 复制代码
package com.github.pojo;

public class User {
    private String username;
    private String password;
    private String email;

    public User() {
    }

    public User(String username, String password, String email) {
        this.username = username;
        this.password = password;
        this.email = email;
    }

    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{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}
  • 编写工具类Sendmail.java
java 复制代码
package com.github.util;

/** * 多线程实现邮件发送 * 使用多线程的原因就是提高用户的体验,一旦一个页面3s及以上的时间白屏就可能被用户关掉 * 所以我们在用户提交表单之后,将费时的邮件发送工作使用一个子线程来完成,而主线程还是去完成它自己的事情 * 这么做就可以提高用户体验,不然用户等待邮件发送的时间 */

import com.github.pojo.User;
import com.sun.mail.util.MailSSLSocketFactory;

import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;

/** * @author subei * 多线程这种处理就可以称为异步处理 */
public class Sendmail extends Thread{

    // 用于向客户发送邮件的邮箱
    private String from = "XXXX@qq.com";
    // 用于登陆SMTP服务器的用户名
    private String username = "XXXX@qq.com";
    // 授权码
    private String password = "授权码";
    // 发送邮件的地址
    private String host = "smtp.qq.com";

    private User user;
    public Sendmail(User user) {
        this.user = user;
    }

    @Override
    public void run() {
        try {
            Properties prop = new Properties();
            // 设置QQ邮件服务器
            prop.setProperty("mail.host", host);
            // 邮件发送协议
            prop.setProperty("mail.transport.protocol", "smtp");
            // 需要验证用户名密码
            prop.setProperty("mail.smtp.auth", "true");

            // 关于QQ邮箱,还要设置SSL加密,加上以下代码即可
            MailSSLSocketFactory sf = new MailSSLSocketFactory();
            sf.setTrustAllHosts(true);
            prop.put("mail.smtp.ssl.enable", "true");
            prop.put("mail.smtp.ssl.socketFactory", sf);

            // 1.创建定义整个应用程序所需的环境信息的 Session 对象
            // 使用QQ邮箱的时候才需要,其他邮箱不需要这一段代码
            // 获取和SMTP服务器的连接对象
            Session session = Session.getDefaultInstance(prop, new Authenticator() {
                @Override
                public PasswordAuthentication getPasswordAuthentication() {
                    // 发件人邮件用户名、授权码
                    return new PasswordAuthentication("XXXX@qq.com", "授权码");
                }
            });

            // 开启Session的debug模式,这样就可以查看到程序发送Email的运行状态
            session.setDebug(true);

            // 2.通过session得到transport对象
            Transport ts = session.getTransport();

            // 3.使用邮箱的用户名和授权码连上SMTP邮件服务器,即登陆
            ts.connect(host, username, password);

            //4、创建邮件对象MimeMessage------点击网页上的写信
            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress(username));
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
            message.setSubject("用户注册邮件!");
            message.setContent("<p><h2>恭喜注册成功!</h2></p>您的用户名为: <h4>"+user.getUsername()+
                    "</h4><p>您的密码:" + user.getPassword() +
                    "</p><p>请妥善保管您的密码,如有问题请及时联系网站客服,再次欢迎您的加入!!</p>", "text/html;charset=UTF-8");

            // 5.发送邮件------在网页上点击发送按钮
            ts.sendMessage(message, message.getAllRecipients());

            // 6.关闭连接对象,即关闭服务器上的连接资源
            ts.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 编写Servlet.java
java 复制代码
package com.github.servlet;

import com.github.pojo.User;
import com.github.util.Sendmail;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class RegisterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1.接收用户填写的表单数据
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String email = req.getParameter("email");
        System.out.println(username+password+email);

        // 2.向用户邮箱发送邮件,注意发送邮件很耗时,所以我们启动一个子线程去做这件事,而用户则是直接跳转注册成功页面,以免降低用户体验
        User user = new User(username,password,email);
        // 获取子线程对象
        Sendmail sendmail = new Sendmail(user);
        // 启动子线程
        new Thread(sendmail).start();

        // 3.视图跳转
        req.setAttribute("message","注册成功!我们已经向您的邮箱发送了邮件,请您及时进行查收。由于网络原因,您收到邮件的时间存在延迟,敬请谅解~");
        req.getRequestDispatcher("info.jsp").forward(req,resp);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
  • web.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <servlet>
        <servlet-name>RegisterServlet</servlet-name>
        <servlet-class>com.github.servlet.RegisterServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>RegisterServlet</servlet-name>
        <url-pattern>/RegisterServlet.do</url-pattern>
    </servlet-mapping>

</web-app>
  • 运行测试,遇到如下报错。
  • 检查Artifacts;
  • 再次运行,还是报错500;
    • 将activation-1.1.jar、mail-1.4.7.jar导入到Tomcat中的lib文件夹中。
  • 再次运行。

🎉🎉全剧终🎉🎉

参考

狂神说笔记------JavaWeb快速入门13 - subeiLY - 博客园

相关推荐
njnu@liyong7 小时前
图解HTTP-HTTP报文
网络协议·计算机网络·http
ZachOn1y7 小时前
计算机网络:应用层 —— 应用层概述
计算机网络·http·https·应用层·dns
莫名其妙小饼干7 小时前
网上球鞋竞拍系统|Java|SSM|VUE| 前后端分离
java·开发语言·maven·mssql
.生产的驴11 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
weixin_5375904515 小时前
《Java编程入门官方教程》第八章练习答案
java·开发语言·servlet
Graceful_scenery17 小时前
https双向认证
服务器·网络·网络协议·http·https
梁萌18 小时前
Docker快速安装Tomcat
docker·容器·tomcat·镜像
njnu@liyong1 天前
图解HTTP-HTTP状态码
网络协议·计算机网络·http
代码洁癖症患者1 天前
HTTP请求的奇幻旅程:从发起至响应的全方位探索
网络·网络协议·http
全栈老实人_1 天前
时间管理系统|Java|SSM|JSP|
java·开发语言·tomcat·maven