JavaWeb:

学习视频-狂神说JavaWeb:视频链接

JavaWeb

1、基本概念

1.1、前言

web开发:

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

  • 静态web

    • html,css

    • 提供给所有人看的数据始终不会发生变化!

  • 动态web

    • 淘宝,几乎是所有的网站;

    • 提供给所有人看的数据始终会发生变化,每个人在不同的时间,不同的地点看到的信息各不相同!

    • 技术栈:Servlet/JSP,ASP,PHP

  • 在Java中,动态web资源开发的技术统称为JavaWeb;

1.2、web应用程序

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

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

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

  • URL

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

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

    • html,css,js

    • jsp,servlet

    • Java程序

    • jar包

    • 配置文件(Properties)

Web应用程序编写完毕后,若想提供给外界访问;需要一服务器来统一管理

1.3、静态web

  • *.htm, *.html这些都是网页的后缀、如果服务器上一直存在这些东西,我们就可以直接进行读取。网络;

静态web

  • 静态web存在的缺点

    • Web页面无法动态更新,所有用户看到都是同一个页面

      • 轮播图,点击特效:伪动态

      • JavaScript[实际开发中,它用的最多]

      • VBScript

    • 它无法和数据库交互(数据无法持久化,用户无法交互)

1.4、 动态web

页面会动态展示,“web页面的展示效果因人而异”

动态web

缺点:

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

    • 停机维护优点:
  • Web页面可以动态更新,所有用户看到都不是同一个页面

  • 它可以与数据库交互(数据持久化:注册,商品信息,用户信息………)

动态web的数据库

新手村:–魔鬼训练(分析原理,看源码)–>PK场

2、web服务器

2.1、技术讲解

ASP:

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

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

  • 在ASP开发中,基本一个页面都有几千行的业务代码,页面极其混乱

  • 维护成本高!

  • C#

  • IIS

  • <h1>
        <h1>
            <h1></h1>
        </h1>
        <%
           System.out.println("hello")
        %>
    </h1>
    

php:

  • PHP开发速度很快,功能很强大,跨平台,代码很简单(70%,WP)

  • 无法承载大访问量的情况(局限性)

JSP/Servlet:

  • B/S:浏览和服务器

  • C/S:客户端和服务器

  1. sun公司主推的B/S架构

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

  3. 可以承载三高问题带来的影响(高并发、高可用、高性能);

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

2.2、web服务器

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

lIS

  • 微软的;ASP…,Windows中自带的

Tomcat

Tomcat图标

面向百度编程;

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

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

Tomcat 实际上运行JSP页面和Servlet。

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

下载tomcat:

  1. 安装 or 解压

  2. 了解配置文件及目录结构

  3. 这个东西的作用

3、Tomcat

3.1、安装tomcat

官网:tomcat官网

Tomcat下载

Tomcat解压

3.2、Tomcat启动和配置

文件夹作用:

Tomcat文件夹作用

启动、关闭tomcat(bin文件夹下)

启动-关闭Tomcat

访问测试:http://localhost:8080/

可能遇到的问题:

  1. Java环境变量没有配置(到这还不会配的想一想自己的问题)

  2. 闪退问题:需要配置兼容性

  3. 乱码问题:配置文件中设置

3.3、配置

Tomcat服务器核心配置文件server.xml

Tomcat服务器核心配置文件

1、可以配置启动的端口号:
  • tomcat的默认端口号为:8080

  • mysql:3306

  • http:80

  • https:443

<Connector port="8081" protocol="HTTP/1.1"
      connectionTimeout="20000"
      redirectPort="8443" />
2、可以配置主机的名称:
  • 默认的主机名为:localhost->127.0.0.1

    • 系统的localhost设置(不要轻易设置):127.0.0.1
  • 默认网站应用存放的位置为:webapps

  <Host name="www.tang.com"  appBase="webapps"
        unpackWARs="true" autoDeploy="true">
3、高难度面试题:

请你谈谈网站是如何进行访问的!

  1. 输入一个域名;回车

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

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

      127.0.0.1		www.tang.com
      
    • 没有:去DNS服务器找,找到的话就返回,找不到就返回找不到;

域名

4、可以配置一下环境变量(可选性)
  • 百度搜索:配置tomcat环境变量,进行配置

3.4、发布一个web网站

  • 开启本地tomcat服务器的情况下,可以学习JSP/Servlet,可以看源码:JSP/Servlet学习

不会就先模仿

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

网站应该有的结构

--webapps :Tomcat服务器的web目录
	-ROOT
	-tang :网站的目录名
		- 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

4.1、什么是HTTP

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

  • 文本:html,字符串,…

  • 超文本:图片,音乐,视频,定位,地图.……

  • 默认端口:80

Https:安全的

  • 443

4.2、两个时代

  • http1.0

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

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

4.3、Http请求

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

百度:

Request URL: https://www.baidu.com/    请求地址
Request Method: GET    get方法/post方法
Status Code: 200 OK    状态码:200
Remote(远程) Address: 220.181.38.149:443

Accept:text/html
Accept-Encoding:gzip, deflate, br
Accept-Language:zh-CN,zh;q=0.9    语言
Cache-Control:max-age=0
Connection:keep-alive    连接

1、请求行

  • 请求行中的请求方式:GET

  • 请求方式:Get,Post,HEAD,DELETE,PUT,TRACT.…

    • get:请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效

    • post:请求能够携带的参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效。

2、消息头

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

4.4、Http响应

  • 服务器–响应→客户端

百度:

Cache-Control: private    缓存控制
Connection: keep-alive    连接
Content-Encoding: gzip    编码
Content-Type: text/html   类型

1、响应体

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

2、响应状态码(重点)

200:请求响应成功200

3xx:请求重定向

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

4xx:找不到资源 404

  • 资源不存在;

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

常见面试题:

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

5、Maven

我为什么要学习这个技术?

  1. 在Javaweb开发中,需要使用大量的jar包,我们手动去导入;

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

  3. 由此,Maven诞生了!

5.1、Maven项目架构管理工具

我们目前用来就是方便导入jar包的!

Maven的核心思想:约定大于配置

  • 有约束,不要去违反。

Maven会规定好你该如何去编写我们的Java代码,必须要按照这个规范来;

5.2、下载安装Maven

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

Maven下载

下载完成后,解压即可;

友情建议:电脑上的所有环境都放在一个文件夹下,方便管理;

5.3、配置环境变量

在我们的系统环境变量中配置如下配置:

  • M2_HOME:maven目录下的bin目录

  • MAVEN_HOME:maven的目录

  • 在系统的path中配置:%MAVEN_HOME%\bin

mvn环境变量

  • 测试Maven是否安装成功,保证必须配置完毕!

5.4、阿里云镜像

  • 在文件D:\software\apache-maven-3.6.3\conf\settings.xml中配置

  • 镜像:mirrors

  • 作用:加速我们的下载

  • 国内建议使用阿里云的镜像

<mirror>
	<id>nexus-aliyun</id>
	<mirrorOf>*,!jeecg,!jeecg-snapshots</mirrorOf>
	<name>Nexus aliyun</name>
	<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

5.5、本地仓库

  • 在文件D:\software\apache-maven-3.6.3\conf\settings.xml中配置

  • 在本地的仓库,远程仓库;建立一个本地仓库:localRepository

<localRepository>D:/software/repository</localRepository>

5.6、在IDEA中使用Maven

  1. 启动IDEA

  2. 创建一个MavenWeb项目

    MavenWeb项目创建

    MavenWeb项目结构

    MavenWeb项目仓库

    MavenWeb项目创建完成

  3. 等待项目初始化完毕

    Maven项目搭建成功

  4. 观察maven仓库中多了什么东西?

  5. IDEA中的Maven设置

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

    Maven设置

    Maven下载设置

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

5.7、创建一个普通的Maven项目

  • 创建普通maven项目

Maven普通项目创建

  • maven初始干净项目

Maven干净的项目

  • 这个只有在web应用下才会有!

MavenWeb应用下的文件

5.8、标记文件夹功能

  • 标记文件夹功能

标记文件夹功能

  • 项目结构配置

项目结构配置

项目结构设置

target目录存放class文件

5.9、在IDEA中配置Tomcat

tomcat的配置1

tomcat的配置2

tomcat的配置3

解决警告问题

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

tomcat的配置4

tomcat的配置5

tomcat的启动

tomcat的启动后的网页内容

5.10、pom文件

  • pow.xml 是Maven的核心配置文件

(百度)在maven仓库中想要什么就下载什么依赖就好了

Maven侧边栏使用

<?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.tang</groupId>
  <artifactId>01-maven</artifactId>
  <version>1.0-SNAPSHOT</version>
  <!-- Package:项目的打包方式
    jar:Java 应用
    war:JavaWeb应用
  -->
  <packaging>war</packaging>

  <!-- 可以删除 -->
  <name>01-maven Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

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

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

  <!-- 项目构建用的东西 -->
  <build>
    <finalName>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>

Maven导入jar包

5.11、配置无法被导出或失效的解决方案

  • Maven由于它的约定大于配置,我们之后可能遇到我们写的配置文件,无法被导出或失效的问题,解决方案:

  • <!--在build中配置resources,来防止我们资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
    

5.12、IDEA操作

Maven中jar包关联图

5.13、解决遇到的问题

  1. Maven可能会出现不兼容的问题

    • 解决办法:降低版本重试
  2. Tomcat闪退

  3. IDEA中每次都要重复配置Maven

    • 在IDEA中的全局默认配置中去配置

    Maven全局settings配置

  4. Maven项目中Tomcat无法配置

    • 可能是环境变量未配置
  5. maven默认web项目中的web.xml版本问题

    Maven默认web项目中的web.xml版本

  6. 替换为webapp4.0版本和tomcat一致

    <?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>
    
  7. Maven仓库的使用

6、Servlet

6.1、Servlet简介

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

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

    • 编写一个类,实现Servlet接口

    • 把开发好的Java类部署到web服务器中。

把实现了Servlet接口的Java程序叫做,Servlet

6.2、HelloServlet

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

  1. 构建一个普通的Maven项目,删除里面的src目录,以后我们的学习就在这个项目里面建立Moudel;这个空的工程就是Maven主工程;

  2. 关于Maven父子工程的理解;

    父项目中会有

    <modules>
        <module>servlet-01</module>
    </modules>
    

    子项目会有

    <parent>
        <artifactId>02-servlet</artifactId>
        <groupId>com.tang</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    

    父项目中的jar包子项目可以直接使用

    son extends father
    
  3. Maven环境优化

    1. 修改web.xml为最新的

    2. 将maven的结构搭建完整

  4. 编写一个Servlet程序

    1. 编写一个普通类

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

      package com.tang.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.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.println("Hello,Servlet");
          }
      
          @Override
          protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              doGet(req, resp);
          }
      }     
      
  5. 编写Servlet的映射

    • 为什么需要映射:我们写的是JAVA程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要在web服务中注册我们写的Servlet,还需给他一个浏览器能够访问的路径;

      <!-- 注册Servlet -->
      <servlet>
          <servlet-name>helloServlet</servlet-name>
          <servlet-class>com.tang.servlet.HelloServlet</servlet-class>
      </servlet>
      <!-- Servlet的请求路径 -->
      <servlet-mapping>
          <servlet-name>helloServlet</servlet-name>
          <url-pattern>/hello</url-pattern>
      </servlet-mapping>
      
  6. 配置Tomcat

    • 注意:配置项目发布的路径就可以了
  7. 启动测试(运行就好了)

6.3、Servlet原理

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

    servlet请求原理

6.4、Mapping问题

  1. 一个Servlet可以指定一个映射路径

    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    
  2. 一个servlet可以指定多个映射路径

    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello1</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello2</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello3</url-pattern>
    </servlet-mapping>
    
  3. 一个servlet可以指定通用映射路径

    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello/*</url-pattern>
    </servlet-mapping>
    
  4. 默认请求路径(会把主页干掉)

    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    
  5. 指定一些后缀或者前缀等等…

    <!--可以自定义后缀实现请求映射
        注意点,*前面不能加项目映射的路径
        hello/sajdlk.tang
    -->
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>*.tang</url-pattern>
    </servlet-mapping>
    
  6. 优先级问题
    指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求;

    <!-- 404 -->
    <servlet>
        <servlet-name>error</servlet-name>
        <servlet-class>com.tang.servlet.ErrorServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>error</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    

6.5、ServletContext

web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用;

1、共享数据

我在这个Servlet中保存的数据,可以在另外一个servlet中拿到;

1.保存数据到Servlet

package com.tang.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 {
        ServletContext context = this.getServletContext();
        String username = "糖果";//数据
        context.setAttribute("username",username);//将一个数据保存在了ServletContext中,名字为:username ,值 username
    }

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

2.获取Servlet中保存的数据

package com.tang.servlet;

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

public class GetServlet extends HelloServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String username = (String) context.getAttribute("username");
        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        resp.getWriter().print("名字:"+username);
    }

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

3.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>hello</servlet-name>
        <servlet-class>com.tang.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.tang.servlet.GetServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>getc</servlet-name>
        <url-pattern>/getc</url-pattern>
    </servlet-mapping>
</web-app>

测试访问结果;

2、获取初始化参数

1.xml中配置web应用的初始化参数

<!-- 配置一些web应用的初始化参数 -->
<context-param>
    <param-name>url</param-name>
    <param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>

2.获取参数

package com.tang.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 {
        doGet(req, resp);
    }
}

3.xml映射

<servlet>
    <servlet-name>getparam</servlet-name>
    <servlet-class>com.tang.servlet.ServletDemo03</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>getparam</servlet-name>
    <url-pattern>/getparam</url-pattern>
</servlet-mapping>

3、请求转发

1.请求转发

package com.tang.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 ServletDemo04 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();

        // RequestDispatcher requestDispatcher = context.getRequestDispatcher("/getp");//转发的请求路径
        // requestDispatcher.forward(req,resp);//调用forward实现请求转发;
        context.getRequestDispatcher("/getp").forward(req,resp);
    }

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

2.xml映射

<servlet>
    <servlet-name>getd</servlet-name>
    <servlet-class>com.tang.servlet.ServletDemo04</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>getd</servlet-name>
    <url-pattern>/getd</url-pattern>
</servlet-mapping>

请求转发

4、读取资源文件

Properties

  • 在java目录下新建properties

    <!--在build中配置resources,来防止我们资源导出失败的问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <excludes>
                    <exclude>**/*.properties</exclude>
                    <exclude>**/*.xml</exclude>
                </excludes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
    
  • 在resources目录下新建properties

发现:都被打包到了同一个路径下:classes,我们俗称这个路径为classpath:
思路:需要一个文件流

1.db.properties

username=tangGuo
password=123456

2.读取资源文件

package com.tang.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 ServletException, IOException {
        InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.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 {
        doGet(req, resp);
    }
}

3.xml映射

<servlet>
    <servlet-name>getprop</servlet-name>
    <servlet-class>com.tang.servlet.ServletDemo05</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>getprop</servlet-name>
    <url-pattern>/getprop</url-pattern>
</servlet-mapping>

测试访问结果;

6.6、HttpServletResponse

web服务器接收到客户端的http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,一个代表响应的HttpServletResponse对象

  • 如果要获取客户端请求过来的参数:找HttpServletRequest

  • 如果要给客户端响应一些信息:找HttpServletResponse

1、简单分类

1.负责向浏览器发送数据的方法

public ServletOutputStream getOutputStream() throws IOException;
public PrintWriter getWriter() throws IOException;

2.负责向浏览器发送响应头的方法

void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentLengthLong(long var1);
void setContentType(String var1);
void setDateHeader(String varl,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 varl,int var2);

3.响应的状态码

/**
 * Status code (100) indicating the client can continue.
 */
public static final int SC_CONTINUE = 100;

/**
 * Status code (101) indicating the server is switching protocols
 * according to Upgrade header.
 */
public static final int SC_SWITCHING_PROTOCOLS = 101;

/**
 * Status code (200) indicating the request succeeded normally.
 */
public static final int SC_OK = 200;

/**
 * Status code (201) indicating the request succeeded and created
 * a new resource on the server.
 */
public static final int SC_CREATED = 201;

/**
 * Status code (202) indicating that a request was accepted for
 * processing, but was not completed.
 */
public static final int SC_ACCEPTED = 202;

/**
 * Status code (203) indicating that the meta information presented
 * by the client did not originate from the server.
 */
public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;

/**
 * Status code (204) indicating that the request succeeded but that
 * there was no new information to return.
 */
public static final int SC_NO_CONTENT = 204;

/**
 * Status code (205) indicating that the agent <em>SHOULD</em> reset
 * the document view which caused the request to be sent.
 */
public static final int SC_RESET_CONTENT = 205;

/**
 * Status code (206) indicating that the server has fulfilled
 * the partial GET request for the resource.
 */
public static final int SC_PARTIAL_CONTENT = 206;

/**
 * Status code (300) indicating that the requested resource
 * corresponds to any one of a set of representations, each with
 * its own specific location.
 */
public static final int SC_MULTIPLE_CHOICES = 300;

/**
 * Status code (301) indicating that the resource has permanently
 * moved to a new location, and that future references should use a
 * new URI with their requests.
 */
public static final int SC_MOVED_PERMANENTLY = 301;

/**
 * Status code (302) indicating that the resource has temporarily
 * moved to another location, but that future references should
 * still use the original URI to access the resource.
 *
 * This definition is being retained for backwards compatibility.
 * SC_FOUND is now the preferred definition.
 */
public static final int SC_MOVED_TEMPORARILY = 302;

/**
* Status code (302) indicating that the resource reside
* temporarily under a different URI. Since the redirection might
* be altered on occasion, the client should continue to use the
* Request-URI for future requests.(HTTP/1.1) To represent the
* status code (302), it is recommended to use this variable.
*/
public static final int SC_FOUND = 302;

/**
 * Status code (303) indicating that the response to the request
 * can be found under a different URI.
 */
public static final int SC_SEE_OTHER = 303;

/**
 * Status code (304) indicating that a conditional GET operation
 * found that the resource was available and not modified.
 */
public static final int SC_NOT_MODIFIED = 304;

/**
 * Status code (305) indicating that the requested resource
 * <em>MUST</em> be accessed through the proxy given by the
 * <code><em>Location</em></code> field.
 */
public static final int SC_USE_PROXY = 305;

 /**
 * Status code (307) indicating that the requested resource 
 * resides temporarily under a different URI. The temporary URI
 * <em>SHOULD</em> be given by the <code><em>Location</em></code> 
 * field in the response.
 */
public static final int SC_TEMPORARY_REDIRECT = 307;

/**
 * Status code (400) indicating the request sent by the client was
 * syntactically incorrect.
 */
public static final int SC_BAD_REQUEST = 400;

/**
 * Status code (401) indicating that the request requires HTTP
 * authentication.
 */
public static final int SC_UNAUTHORIZED = 401;

/**
 * Status code (402) reserved for future use.
 */
public static final int SC_PAYMENT_REQUIRED = 402;

/**
 * Status code (403) indicating the server understood the request
 * but refused to fulfill it.
 */
public static final int SC_FORBIDDEN = 403;

/**
 * Status code (404) indicating that the requested resource is not
 * available.
 */
public static final int SC_NOT_FOUND = 404;

/**
 * Status code (405) indicating that the method specified in the
 * <code><em>Request-Line</em></code> is not allowed for the resource
 * identified by the <code><em>Request-URI</em></code>.
 */
public static final int SC_METHOD_NOT_ALLOWED = 405;

/**
 * Status code (406) indicating that the resource identified by the
 * request is only capable of generating response entities which have
 * content characteristics not acceptable according to the accept
 * headers sent in the request.
 */
public static final int SC_NOT_ACCEPTABLE = 406;

/**
 * Status code (407) indicating that the client <em>MUST</em> first
 * authenticate itself with the proxy.
 */
public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;

/**
 * Status code (408) indicating that the client did not produce a
 * request within the time that the server was prepared to wait.
 */
public static final int SC_REQUEST_TIMEOUT = 408;

/**
 * Status code (409) indicating that the request could not be
 * completed due to a conflict with the current state of the
 * resource.
 */
public static final int SC_CONFLICT = 409;

/**
 * Status code (410) indicating that the resource is no longer
 * available at the server and no forwarding address is known.
 * This condition <em>SHOULD</em> be considered permanent.
 */
public static final int SC_GONE = 410;

/**
 * Status code (411) indicating that the request cannot be handled
 * without a defined <code><em>Content-Length</em></code>.
 */
public static final int SC_LENGTH_REQUIRED = 411;

/**
 * Status code (412) indicating that the precondition given in one
 * or more of the request-header fields evaluated to false when it
 * was tested on the server.
 */
public static final int SC_PRECONDITION_FAILED = 412;

/**
 * Status code (413) indicating that the server is refusing to process
 * the request because the request entity is larger than the server is
 * willing or able to process.
 */
public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413;

/**
 * Status code (414) indicating that the server is refusing to service
 * the request because the <code><em>Request-URI</em></code> is longer
 * than the server is willing to interpret.
 */
public static final int SC_REQUEST_URI_TOO_LONG = 414;

/**
 * Status code (415) indicating that the server is refusing to service
 * the request because the entity of the request is in a format not
 * supported by the requested resource for the requested method.
 */
public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;

/**
 * Status code (416) indicating that the server cannot serve the
 * requested byte range.
 */
public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;

/**
 * Status code (417) indicating that the server could not meet the
 * expectation given in the Expect request header.
 */
public static final int SC_EXPECTATION_FAILED = 417;

/**
 * Status code (500) indicating an error inside the HTTP server
 * which prevented it from fulfilling the request.
 */
public static final int SC_INTERNAL_SERVER_ERROR = 500;

/**
 * Status code (501) indicating the HTTP server does not support
 * the functionality needed to fulfill the request.
 */
public static final int SC_NOT_IMPLEMENTED = 501;

/**
 * Status code (502) indicating that the HTTP server received an
 * invalid response from a server it consulted when acting as a
 * proxy or gateway.
 */
public static final int SC_BAD_GATEWAY = 502;

/**
 * Status code (503) indicating that the HTTP server is
 * temporarily overloaded, and unable to handle the request.
 */
public static final int SC_SERVICE_UNAVAILABLE = 503;

/**
 * Status code (504) indicating that the server did not receive
 * a timely response from the upstream server while acting as
 * a gateway or proxy.
 */
public static final int SC_GATEWAY_TIMEOUT = 504;

/**
 * Status code (505) indicating that the server does not support
 * or refuses to support the HTTP protocol version that was used
 * in the request message.
 */
public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;

2、下载文件

  1. 向浏览器输出消息(一直在讲,就不说了)

  2. 下载文件

    1. 要获取下载文件的路径

    2. 下载的文件名是啥?

    3. 设置想办法让浏览器能够支持下载我们需要的东西

    4. 获取下载文件的输入流

    5. 创建缓冲区

    6. 获取OutputStream对象

    7. 将FileInputStream流写入到buffer缓冲区

    8. 使用OutputStream将缓冲区中的数据输出到客户端!

      package com.tang.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 ServletException, IOException {
              // 1. 要获取下载文件的路径
              String realPath = "D:\\IT\\狂神说\\JavaWeb\\02-servlet\\response\\src\\main\\resources\\糖果.gif";
              System.out.println("下载文件的路径:"+realPath);
              // 2. 下载的文件名是啥?
              String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
              // 3. 设置想办法让浏览器能够支持(Content-disposition)下载我们需要的东西,中文名可能会出现乱码,用 URLEncoder.encode 编码,
              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. 将FileInputStream流写入到buffer缓冲区
              while ((len=in.read(buffer))>0){
                  // 8. 使用OutputStream将缓冲区中的数据输出到客户端!
                  out.write(buffer,0,len);
              }
              in.close();
              out.close();
          }
      
          @Override
          protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              doGet(req, resp);
          }
      }
      

3、 验证码功能

验证怎么来的?

  • 前端实现

  • 后端实现,需要用到Java的图片类,生产一个图片

package com.tang.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 ServletException, IOException {
        //如何让浏览器3秒自动刷新一次;
        resp.setHeader("refresh","3");
        //在内存中创建一个图片
        BufferedImage image = new BufferedImage(80, 60, BufferedImage.TYPE_INT_RGB);
        //得到图片
        Graphics2D g = (Graphics2D) image.getGraphics();//画笔
        //设置图片的背景颜色
        g.setBackground(Color.black);
        g.setColor(Color.red);
        g.fillRect(0,0,80,30);
        //给图片写数据
        g.setColor(Color.blue);
        g.setFont(new Font(null, Font.BOLD,20));
        g.drawString(makeNum(),0,20);
        //告诉浏览器,这个请求用图片的方式打开
        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(999999999) + "";
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 9-num.length(); i++) {
            sb.append(0);
        }
        return sb+num;
    }
    //生成随机数
    

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

4、实现重定向

重定向

B一个web资源收到客户端A请求后B他会通知A客户端去访问另外一个web资源C,这个过程叫重定向

常见场景:

  • 用户登录
public void sendRedirect(String location) throws IOException;

测试:

package com.tang.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","image");
            resp.setStatus(302);
         */
        resp.sendRedirect("image");
    }

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

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

相同点

  • 页面都会实现跳转

不同点

  • 请求转发时,url不会产生变化;307

  • 请求转发,只能在本项目下进行转发。

  • 重定向时,url地址栏会发生变化;302

  • 重定向,可以在不同项目下进行。

转发-重定向

index.jsp

<html>
<body>
<h2>Hello World!</h2>

<%-- 这里提交的路径,需要寻找到项目的路径 --%>
<%-- ${pageContext.request.contextPath}代表当前的项目 --%>
<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>

RequestTest.java

package com.tang.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;
        resp.sendRedirect("success.jsp");
    }

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

重定向页面success.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>Success</h1>
</body>
</html>

web.xml配置

<servlet>
    <servlet-name>RequestTest</servlet-name>
    <servlet-class>com.tang.servlet.RequestTest</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>RequestTest</servlet-name>
    <url-pattern>/login</url-pattern>
</servlet-mapping>

pom.xml导入的jar包

<dependencies>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>javax.servlet.jsp-api</artifactId>
        <version>2.3.3</version>
    </dependency>
</dependencies>

6.7、HttpServletRequest

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

获取客户端的所有信息

1、获取参数,请求转发

获取前端传递的参数

jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>

<h1>登录</h1>

<div style="text-align: center">
    <%-- 以post方式提交表单,到login请求 --%>
    <form action="${pageContext.request.contextPath}/login" method="post">
        用户名:<input type="text" name="username"><br/>
        密码:<input type="password" name="password"><br/>
        爱好:
        <input type="checkbox" name="hobbies" value="代码">代码
        <input type="checkbox" name="hobbies" value="唱歌">唱歌
        <input type="checkbox" name="hobbies" value="动漫">动漫
        <input type="checkbox" name="hobbies" value="电影">电影
        <input type="checkbox" name="hobbies" value="aaa">aaa
        <br/>
        <input type="submit">
    </form><br/>
</div>


</body>
</html>

java

package com.tang.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 LoginServlet 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[] hobbies = req.getParameterValues("hobbies");
        System.out.println("=====================");
        System.out.println(username);
        System.out.println(password);
        System.out.println(Arrays.toString(hobbies));
        System.out.println("=====================");
        //通过请求转发
        //这里的 / 代表当前的web应用
        req.getRequestDispatcher("/success.jsp").forward(req,resp);
    }

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

7、Cookie、Session

7.1、会话

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

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

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

你 西开

  1. 发票 西开给你发票

  2. 学校登记 西开标记你来过了

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

客户端 服务端

  1. 服务端给客户端一个 信件,客户端下次访问服务端带上信件就可以了; cookie

  2. 服务器登记你来过了,下次你来的时候我来匹配你; seesion

7.2、保存会话的两种技术

cookie

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

session

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

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

7.3、Cookie

cookie

  1. 从请求中拿到cookie信息

  2. 服务器响应给客户端cookie

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
  • 保存用户上一次访问的时间
package com.tang.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.setContentType("text/html;charset=utf-8");
        resp.setCharacterEncoding("utf-8");

        PrintWriter out = resp.getWriter();
        //Cookie,服务器端从客户端获取Cookie
        Cookie[] cookies = req.getCookies();//这里返回数组,说明Cookie可能存在多个
        //判断Cookie是否存在
        if(cookies!=null){
            //如果存在Cookie
            out.write("您上一次访问的时间是:");
            for (int i = 0; i < cookies.length; i++) {
                Cookie cookie = cookies[i];
                //获取cookie的名字
                if (cookie.getName().equals("lastLoginTime")){
                    //获取cookie中的值
                    long lastLoginTime= Long.parseLong(cookie.getValue());
                    Date date = new Date(lastLoginTime);
                    //out.write(date.toString());
                    out.write(date.toLocaleString());
                }
            }
        }else {
            out.write("这是您第一次访问本站");
        }
        //服务器给客户端响应一个cookie
        Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
        //cookie有效期为1天
        cookie.setMaxAge(24*60*60);

        resp.addCookie(cookie);
    }

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

cookie:一般会保存在本地的 用户目录下 appdata;

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

  • 一个Cookie只能保存一个信息;

  • 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie;

  • Cookie大小有限制4kb;

  • 300个cookie浏览器上限

删除Cookie;

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

  • 设置有效期时间为 0 ;

    package com.tang.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;
    
    //删除cookie
    public class CookieDemo02 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //创建一个cookie,名字必须要和要删除的名字一致
            Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
            cookie.setMaxAge(0);
            resp.addCookie(cookie);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    

编码解码:

URLEncoder.encode("糖果","utf-8");//编码
URLDecoder.decode(cookie.getValue(),"UTF-8");//解码

7.4、Session(重点)

session

什么是Session:

  • 服务器会给每一个用户(浏览器)创建一个Session对象;

  • 一个Session独占一个浏览器,只要浏览器没有关闭,这个Session就存在;

  • 用户登录之后,整个网站它都可以访问!–> 保存用户的信息;保存购物车的信息……

Session和cookie的区别:

  • Cookie是把用户的数据写给用户的浏览器,浏览器保存 (可以保存多个)

  • Session把用户的数据写到用户独占Session中,服务器端保存 (保存重要的信息,减少服务器资源的浪费)

  • Session对象由服务创建;

使用场景:

  • 保存一个登录用户的信息;

  • 购物车信息;

  • 在整个网站中经常会使用的数据,我们将它保存在Session中;

使用Session:

  • Session中存数据
package com.tang.servlet;

import com.tang.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.setContentType("text/html;charset=utf-8");
        resp.setCharacterEncoding("utf-8");

        //得到session
        HttpSession session = req.getSession();
        //给Session中存东西
        session.setAttribute("person",new Person("糖果",25));
        //获取Session的ID
        String sessionId = session.getId();
        //判断Session是不是新创建
        if (session.isNew()){
            resp.getWriter().write("session是新创建的,id为"+sessionId);
        }else {
            resp.getWriter().write("session已经在服务器中存在了,id为"+sessionId);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
  • Session中取数据
package com.tang.servlet;

import com.tang.pojo.Person;

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

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

        //得到session
        HttpSession session = req.getSession();
        Person person = (Person) session.getAttribute("person");
        System.out.println(person);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
  • 移除Session中存数据,注销Session
package com.tang.servlet;

import com.tang.pojo.Person;

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

public class SessionDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //得到session
        HttpSession session = req.getSession();
        session.removeAttribute("person");
        //手动注销session
        session.invalidate();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
  • xml配置映射
<servlet>
    <servlet-name>SessionDemo01</servlet-name>
    <servlet-class>com.tang.servlet.SessionDemo01</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>SessionDemo01</servlet-name>
    <url-pattern>/s1</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>SessionDemo02</servlet-name>
    <servlet-class>com.tang.servlet.SessionDemo02</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>SessionDemo02</servlet-name>
    <url-pattern>/s2</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>SessionDemo03</servlet-name>
    <servlet-class>com.tang.servlet.SessionDemo03</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>SessionDemo03</servlet-name>
    <url-pattern>/s3</url-pattern>
</servlet-mapping>
  • xml配置会话自动过期
<!--设置Session默认的失效时间-->
<session-config>
    <!--15分钟后Session自动失效,以分钟为单位-->
    <session-timeout>15</session-timeout>
</session-config>

ServletContxt

8、JSP

8.1、什么是JSP

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

最大的特点:

  • 写JSP就像在写HTML

  • 区别:

    • HTML只给用户提供静态的数据

    • JSP页面中可以嵌入JAVA代码,为用户提供动态数据;

8.2、JSP原理

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

  • 代码层面没有任何问题

  • 服务器内部工作

    tomcat中有一个work目录;

    IDEA中使用Tomcat,会在IDEA的tomcat中生成一个work目录

我电脑的地址:

C:\Users\糖果\AppData\Local\JetBrains\IntelliJIdea2021.3\tomcat\581a1eae-36e2-4b66-aee2-3c843ad4ee58\work\Catalina\localhost\sc\org\apache\jsp

发现页面转变成了Java程序!

jsp

浏览器向服务器发送请求,不管访问什么资源,其实都是在访问Servlet!

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

JSP 本质上就是一个Servlet

//初始化
  public void _jspInit() {}
//销毁
  public void _jspDestroy() {}
//JSPService
  public void _jspService(HttpServletRequest request,HttpServletResponse response){}
  1. 判断请求

  2. 内置一些对象

    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                      //响应
    
  3. 输出页面前增加的代码

    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;
    
  4. 以上的这些个对象我们可以在JSP页面中直接使用!

jsp原理

在JSP页面中;

  • 只要是 JAVA代码就会原封不动的输出;

  • 如果是HTML代码,就会被转换为:

    out.write("<html>\r\n");
    
  • 这样的格式,输出到前端!

8.3、JSP基础语法

任何语言都有自己的语法,JAVA中有, JSP 作为java技术的一种应用,它拥有一些自己扩充的语法(了解,知道即可!),Java所有语法都支持!

JSP表达式

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

JSP脚本片段

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

脚本片段的再实现

<%--在代码嵌入HTML元素--%>
<%
  int x = 10;
  out.println(x);
%>
<p>这是一个JSP文档</p>
<%
  int y = 2;
  out.println(y);
%>

<hr>

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

JSP声明

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

  private int globalVar = 0;

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

JSP声明:会被编译到JSP生成的Java类中!其他的,就会被生成到_jspService方法中!

在JSP,嵌入Java代码即可!

<%%>  <%-- JSP表达式:表达式的结果等 --%>
<%=%> <%-- JSP脚本片段:代码,for循环等 --%>
<%!%> <%-- JSP声明:静态代码块,定义方法,私有变量等 --%>

<%--注释--%>
<!-- 我是HTML的注释 -->
<%-- 我是JSP的注释 --%>

JSP的注释,不会在客户端显示,HTML会!

8.4、JSP指令

<%@page args.... %>
<%@include file=""%>

定制错误界面

<%-- 定制错误界面 --%>
<%@ page errorPage="error/500.jsp" %>
<%-- 显示的声明这是一个错误页面 --%>
<%@ page isErrorPage="true" %>
<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>

拼接页面

<%-- @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"/>

8.5、九大内置对象

  • PageContext 存东西

  • Request 存东西

  • Response

  • Session 存东西

  • Application 【ServletContext】 存东西

  • config 【ServletConfig】

  • out

  • page ,不用了解

  • exception

pageContext.setAttribute("name1","糖果1号");//保存的数据只在一个页面中有效
request.setAttribute("name2","糖果2号");//保存的数据只在一次请求中有效,请求转发会携带这个数据
session.setAttribute("name3","糖果3号");//保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
application.setAttribute("name4","糖果4号");//保存的数据只在服务器中有效,从打开服务器到关闭服务器

//从底层到高层(作用域):page->request->session->application

四大作用域

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

8.6、JSP标签、JSTL标签、EL表达式

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

EL表达式: $

  • 获取数据

  • 执行运算

  • 获取web开发的常用对象

JSP标签

<%--jsp:include--%>

<%--
	http://localhost:8080/jsptag.jsp?name=tangguo&age=12
--%>

<jsp:forward page="/jsptag2.jsp">
    <jsp:param name="name" value="tangguo"/>
    <jsp:param name="age" value="25"/>
</jsp:forward>

JSTL表达式

JSTL标签库的使用就是为了弥补HTML标签的不足;它自定义许多标签,可以供我们使用,标签的功能和Java代码一样!

核心标签 (掌握部分)

格式化标签

SQL标签

XML 标签

jstl

JSTL标签库使用步骤

  • 引入对应的 taglib

  • 使用其中的方法

  • 在Tomcat 也需要引入 jstl的包,否则会报错:JSTL解析错误

c:if
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--引入JSTL核心标签,我们才能使用JSTL标签 core--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<h4>if测试</h4>

<hr>

<form action="coreif.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:set c:choose c:when
<body>

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

<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:when test="${score<60}">
        你的成绩为不及格!
    </c:when>
</c:choose>

</body>
c:forEach
<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,  哪里开始[begin,end]
end,    到哪里
step,   步长
--%>
<c:forEach var="people" items="${list}">
    <c:out value="${people}"/><br>
    <%--${people}<br>--%>
</c:forEach>

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

</body>

9、JavaBean

实体类

JavaBean有特定的写法:

  • 必须要有一个无参构造

  • 属性必须私有化

  • 必须有对应的get/set方法;

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

ORM :对象关系映射

  • 表—>类

  • 字段–>属性

  • 行记录—->对象

people表

id name age address
1 糖果1号 3 天津
2 糖果2号 19 上海
3 糖果3号 25 河南
class People{
    private int id;
    private String name;
    private int age;
    private String address;
}

class A{
    new People(1,"糖果1号",3,"天津");
    new People(2,"糖果2号",19,"上海");
    new People(3,"糖果3号",25,"河南");
}
<%@ page import="com.tang.pojo.People" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<%
    // People people = new People();
    // people.setId(1);
    // people.setName("糖果");
    // people.setAge(25);
    // people.setAddress("河南");

%>
<jsp:useBean id="people" class="com.tang.pojo.People" scope="page"/>

<jsp:setProperty name="people" property="id" value="1"/>
<jsp:setProperty name="people" property="name" value="糖果"/>
<jsp:setProperty name="people" property="age" value="25"/>
<jsp:setProperty name="people" property="address" value="河南"/>

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

<%--<%=people.getAddress()%>--%>

</body>
</html>
  • 过滤器

  • 文件上传

  • 邮件发送

  • JDBC 复习 : 如何使用JDBC , JDBC crud, jdbc 事务

10、MVC三层架构

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

10.1、以前的架构

以前的架构

用户直接访问控制层,控制层就可以直接操作数据库;

servlet--CRUD-->数据库
弊端:程序十分臃肿,不利于维护  
servlet的代码中:处理请求、响应、视图跳转、处理JDBC、处理业务代码、处理逻辑代码

架构:没有什么是加一层解决不了的!
程序猿调用
↑
JDBC (实现该接口)
↑
Mysql Oracle SqlServer ....(不同厂商)

10.2、MVC三层架构

三层架构

Model

  • 业务处理 :业务逻辑(Service)

  • 数据持久层:CRUD (Dao – 数据持久化对象)

View

  • 展示数据

  • 提供链接发起Servlet请求 (a,form,img…)

Controller (Servlet)

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

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

  • 控制视图的跳转

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

11、Filter (重点)

比如 Shiro安全框架技术就是用Filter来实现的

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

  • 处理中文乱码

  • 登录验证….

(比如用来过滤网上骂人的话,我***我自己 0-0)

过滤器

Filter开发步骤:

  1. 导包

    <!-- servlet依赖 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</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>
    
  2. 编写过滤器

    1. 导包不要错 (注意)

      导包

    2. 实现Filter接口,重写对应的方法即可

      package com.tang.filter;
      
      import javax.servlet.*;
      import java.io.IOException;
      
      public class CharacterEncodingFilter implements Filter {
          //初始化:web服务器启动,就以及初始化了,随时等待过滤对象出现!
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
              System.out.println("CharacterEncodingFilter 初始化");
          }
      
          //Chain:链
          /*
              1. 过滤中的所有代码,在过滤特定请求的时候都会执行
              2. 必须要让过滤器继续同行
                  chain.doFilter(request,response);
          */
          @Override
          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服务器关闭的时候,过滤器会销毁
          @Override
          public void destroy() {
              System.out.println("CharacterEncodingFilter 销毁");
          }
      }
      
  3. 在web.xml中配置 Filter

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

    package com.tang.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 ShowServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            resp.getWriter().write("糖果");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
  5. xml配置

    <servlet>
        <servlet-name>ShowServlet</servlet-name>
        <servlet-class>com.tang.servlet.ShowServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ShowServlet</servlet-name>
        <url-pattern>/servlet/show</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>ShowServlet</servlet-name>
        <url-pattern>/show</url-pattern>
    </servlet-mapping>
    

12、监听器

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

  1. 编写一个监听器

    实现监听器的接口

    监听器接口

    package com.tang.listener;
    
    import javax.servlet.ServletContext;
    import javax.servlet.http.HttpSessionEvent;
    import javax.servlet.http.HttpSessionListener;
    
    //统计网上在线人数,统计session
    public class OnlineCountListener implements HttpSessionListener {
        //创建session监听,看你的一举一动
        //一旦创建session就会触发这个事件!
        @Override
        public void sessionCreated(HttpSessionEvent se) {
            System.out.println(se.getSession().getId());
            ServletContext ctx = se.getSession().getServletContext();
            Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
            if (onlineCount==null) onlineCount = 1;
            else onlineCount++;
            ctx.setAttribute("OnlineCount",onlineCount);
        }
    
        //销毁session监听
        //一旦销毁session就会触发这个事件!
        @Override
        public void sessionDestroyed(HttpSessionEvent se) {
            ServletContext ctx = se.getSession().getServletContext();
            Integer onlineCount = (Integer) ctx.getAttribute("OnlineCount");
            if (onlineCount==null) onlineCount = 0;
            else onlineCount--;
            ctx.setAttribute("OnlineCount",onlineCount);
        }
        /*
        Session销毁:
        1. 手动销毁  getSession().invalidate();
        2. 自动销毁
         */
    }
    
  2. web.xml中注册监听器

    <!-- 注册监听器 -->
    <listener>
        <listener-class>com.tang.listener.OnlineCountListener</listener-class>
    </listener>
    
  3. 看情况是否使用!

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

监听器:GUI编程中经常使用;

package com.tang.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);
        frame.setBackground(Color.red);//设置背景颜色

        panel.setBounds(50,50,300,300);
        panel.setBackground(Color.green);//设置背景颜色

        frame.add(panel);
        frame.setVisible(true);

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

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

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

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

package com.tang.filter;

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

public class SysFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

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

        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

14、JDBC

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

jdbc

需要jar包的支持:

  • java.sql

  • javax.sql

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

实验环境搭建

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');

导入数据库依赖

<!--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. 关闭连接(先开的后关)

package com.tang.test;

import java.sql.*;

public class TestJdbc {
    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&useSSL=false";
        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.getInt("id"));
            System.out.println("name="+rs.getString("name"));
            System.out.println("password="+rs.getString("password"));
            System.out.println("email="+rs.getString("email"));
            System.out.println("birthday="+rs.getDate("birthday"));
        }

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

预编译SQL

package com.tang.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&useSSL=false";
        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 ps = connection.prepareStatement(sql);
        ps.setInt(1,4);
        ps.setString(2,"糖果");
        ps.setString(3,"123456");
        ps.setString(4,"tg@qq.com");
        ps.setDate(5,new Date(new java.util.Date().getTime()));

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

        if (i>0) System.out.println("插入成功");
        //6.关闭连接,释放资源(一定要做) 先开后关
        ps.close();
        connection.close();
    }
}

事务

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

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

开启事务
事务提交  commit()
事务回滚  rollback()
关闭事务

转账:
A:1000
B:1000
    
A(900)   --100-->   B(1100) 

Junit单元测试

依赖

<!-- 单元测试 -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
</dependency>

简单使用

@Test注解只有在方法上有效,只要加了这个注解的方法,就可以直接运行!失败的时候是红色:

package com.tang.test;

import org.junit.Test;

public class TestJdbc3 {
    @Test
    public void test(){
        System.out.println("Hello");
    }
}

搭建一个环境

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);
package com.tang.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&useSSL=false";
        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、通知数据库开启事务
            connection.setAutoCommit(false);

            //4、编写sql
            String sql1 = "update account set money = money-100 where name='A'";
            connection.prepareStatement(sql1).executeUpdate();
            //制造错误
            // int i = 1/0;
            String sql2 = "update account set money = money+100 where name='B'";
            connection.prepareStatement(sql2).executeUpdate();

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

15、SMBMS(超市订单管理项目)

16、文件上传

上传流程

1、准备工作

1、文件上传-流的形式

对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的

  • 一般选择采用apache的开源工具common-fileupload这个文件上传组件

  • 是依赖于common-io这个包的,所以还需要下载这个包

2、新建JavaWeb项目

  1. 在项目中,新建lib目录,导入下载的jar包

    • commons-io-2.11.0.jar

    • commons-fileupload-1.4.jar

  2. 将lib添加到项目类库中

新建file项目

  1. 将lib目录放入项目发布中

  2. 配置tomcat

2、使用介绍

文件上传的注意事项

文件上传的注意事项

需要用到的类详解

  • ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象,在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象,所以,我们需要进行解析工作前构造好DiskFileItemFactory对象,通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性。
FileItem类

在HTML页面的 input 必须有 name <input type="file" name="filename">

表单如果包含一个文件上传输入项的话,这个表单的enctype属性就必须设置为multipart/form-data

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

浏览器表单的类型如果为multipart/form-data,在服务器端想要获取数据就要通过流。

常用方法介绍

FileItem常用方法介绍

ServletFileUpload类

ServletFileUpload类

3、代码编写

  1. 在lib目录导入下载的jar包

    • javax.servlet-api-4.0.1.jar

    • javax.servlet.jsp-api-2.3.3.jar

    • jstl-api-1.2.jar

    • standard-1.1.2.jar

  2. 解决新建java类时无servlet选项的问题

解决新建无servlet选项

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>$Title$</title>
    </head>
    <body>
    <%--通过表单上传文件
        get:   上传文件大小有限制
        post:  上传文件大小没有限制
    --%>
    <%--${pageContext.request.contextPath} 获取服务器路径--%>
    <form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post">
        上传用户:<input type="text" name="username"><br/>
        <p><input type="file" name="file1"></p>
        <p><input type="file" name="file2"></p>
        <p><input type="submit"> <input type="reset"></p>
    </form>
    </body>
</html>

FileServlet

//下面类中设置的暂存区设置没有效果
//这个方法的参数列表更该为:uploadParseRequest(ServletFileUpload upload,HttpServletRequest request,String uploadPathFirst,String tmpFile),

//在foreach代码中第一行添加下面代码,判断上传到永久区还是暂存区,后续还能再改进
String uploadPath = (fileItem.getSize()<(1024*1024))?uploadPathFirst:tmpFile;
package com.tang.servlet;

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

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;

public class FileServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //判断上传的文件是普通表单还是带文件的表单
        if (!ServletFileUpload.isMultipartContent(request)){
            return;//如果是普通表单,直接返回
        }//如果通过了这个if,说明我们的表单是带文件上传的

        //创建文件上传的保存路径,建议在WEB-INF路径下,安全,用户无法直接访问上传的文件;
        String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
        System.out.println(uploadPath);
        File uploadFile = new File(uploadPath);
        if (!uploadFile.exists()){
            uploadFile.mkdir();//创建这个目录
        }

        //缓存,临时文件
        //临时路径,假如文件超过了预期的大小,我们就把他放到一个临时文件中,或者提醒用户转存为永久
        String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
        File tmpFile = new File(tmpPath);
        if (!tmpFile.exists()){
            tmpFile.mkdir();//创建这个临时目录
        }

        //处理上传的文件一般需要通过流来获取,我们可以通过request.getInputStream(),原生态文件上传流获取,十分麻烦
        //但是我们都建议使用Apache的文件上传组件来实现,common-fileupload,它需要依赖于common-io组件;
        try {
            //1、创建DiskFileItemFactory对象,处理文件上传路径或限制文件大小
            DiskFileItemFactory factory = getDiskFileItemFactory(tmpFile);
            //2、获取ServletFileUpload
            ServletFileUpload upload = getServletFileUpload(factory);
            //3、处理上传文件
            String msg = uploadParseRequest(upload, request, uploadPath);
            //Servlet请求转发消息
            request.setAttribute("msg",msg);
            request.getRequestDispatcher("/info.jsp").forward(request,response);
        } catch (FileUploadException e) {
            e.printStackTrace();
        }
    }

    public static DiskFileItemFactory getDiskFileItemFactory(File file){
        //1、创建DiskFileItemFactory对象,处理文件上传路径或限制文件大小
        DiskFileItemFactory factory = new DiskFileItemFactory();
        //通过这个工厂设置一个缓冲区,当上传的文件大小大于缓冲区的时候,将它放到临时文件中;
        factory.setSizeThreshold(1024 * 1024);//缓冲区大小为1M
        factory.setRepository(file);//临时目录的保存目录
        return factory;
    }

    public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory){
        //2、获取ServletFileUpload
        ServletFileUpload upload = new ServletFileUpload(factory);
        //监听文件上传进度
        upload.setProgressListener((fileReadLength, fileSize, i) -> {
            //fileReadLength:已读取到的文件大小
            //fileSize:文件大小

            System.out.println("上传进度:"+(Math.floor((double)fileReadLength/fileSize*10000)/100)+"%");
        });

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

    public static String uploadParseRequest(ServletFileUpload upload,HttpServletRequest request,String uploadPath) throws IOException, FileUploadException {
        String msg = "";
        //3、处理上传文件
        //把前端请求解析封装成一个FileItem对象,需要从ServletFileUpload对象中获取
        List<FileItem> fileItems = upload.parseRequest(request);
        //fileItem每一个表单对象
        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;
                }
                //获得上传的文件名 /img/girl/111.jpg
                String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);
                //获得文件的后缀名
                String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1);
                /*
                    如果文件后缀名fileExtName不是我们所需要的
                    就直接return,不处理,告诉用户文件类型不对
                 */
                System.out.println("文件信息【文件名:"+fileName+"文件类型:"+fileExtName+"】");

                //======================存放地址=======================//

                //可以使用UUID(唯一识别的通用码),保证文件名唯一
                //UUID.randomUUID,随机生一个唯一识别的通用码

                //网络传输中的东西,都需要序列化
                //pojo,实体类,如果想要在多个电脑上运行,传输--->需要把对象都序列化了
                //JNI=java Native Interface
                //implements Serializable :标记接口,JVM--->java栈 本地方法栈 native-->c++
                UUID uuidPath = UUID.randomUUID();

                //文件真实存放的路径 realPath
                String realPath = uploadPath + "/" + uuidPath;
                //给每个文件创建一个对应的文件夹
                File realPathFile = new File(realPath);
                if (!realPathFile.exists()) {
                    realPathFile.mkdir();
                }
                //======================文件传输=======================//

                //获得文件上传的流
                InputStream fis = fileItem.getInputStream();

                //创建一个文件输出流
                FileOutputStream fos = new FileOutputStream(realPathFile + "/" + fileName);

                //创建一个缓冲区
                byte[] buffer = new byte[1024 * 1024];

                //判断是否读取完毕
                int len = 0;
                //如果大于0,说明还存在数据
                while ((len = fis.read(buffer)) > 0) {
                    fos.write(buffer, 0, len);
                }

                //关闭流
                fos.close();
                fis.close();
                msg = "文件上传成功!";
                fileItem.delete();//上传成功,清除临时文件
            }
        }
        return msg;
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }
}

info.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<h1 style="color: lime">${msg}</h1>

</body>
</html>

17、文件下载

18、邮件发送

1、邮件发送原理

邮件发送原理

邮件发送流程

2、邮件发送代码测试

  • Java普通项目

1、导入jar包

Java发送E-mail邮件需要两个jar包,javax.mail和javax.activation

  • mail-1.4.7.jar

  • activation-1.1.1.jar

JavaMail

2、记住四个核心类

四个核心类

3、获取QQ邮箱授权码

QQ邮箱中获取对应的权限

QQ邮箱需要安全验证,我们需要获取他对应的权限;

QQ邮箱–>邮箱设置–>账户

4、简单邮件

package com.tang;

import com.sun.mail.util.MailSSLSocketFactory;

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

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

        // 注释或删除以下代码,否则报错
        //QQ邮箱,需要设置SSL加密,加上以下代码即可,大厂,其他邮箱不需要!
        // MailSSLSocketFactory msf = new MailSSLSocketFactory();
        // msf.setTrustAllHosts(true);
        // prop.put("mail.smtp.ssl.enable","true");
        // prop.put("mail.smtp.ssl.socketFactory",msf);


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

        //1.创建定义整个应用程序所需要的环境信息的session对象
        //QQ才有,其他邮箱就不用
        Session session = Session.getDefaultInstance(prop, new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                //发件人邮件用户名、授权码
                return new PasswordAuthentication("2715274537@qq.com", "vewynrbqirvaddgh");
            }
        });
        //开启session的debug模式,这样可以查看到程序发送Email的运行状态
        session.setDebug(true);
        //2.通过session得到transport对象
        Transport ts = session.getTransport();
        //3.使用邮箱的用户名和授权码连上邮件服务器
        ts.connect("smtp.qq.com","2715274537@qq.com","vewynrbqirvaddgh");
        //4.创建邮件:写文件
        //注意需要传递session
        MimeMessage message = new MimeMessage(session);
        //指明邮件的发件人
        message.setFrom(new InternetAddress("2715274537@qq.com"));
        //指明邮件的收件人
        message.setRecipient(Message.RecipientType.TO,new InternetAddress("2715274537@qq.com"));
        //邮件标题
        message.setSubject("只包含文本的简单邮件");
        //邮件的文本内容
        message.setContent("你好","text/html;charset=utf-8");
        //5.发送邮件
        ts.sendMessage(message,message.getAllRecipients());
        //6.关闭连接
        ts.close();
    }
}

5、复杂邮件

  • 带图片和附件的邮件
1、先认识两个类和一个名词:

两个类一个名词

MIME支持

多用途互联网邮件扩展(英语:Multipurpose Internet Mail Extensions,缩写:MIME)是一个互联网标准,它扩展了电子邮件标准,使其能够支持:

multipart的范围

2、有正文和图片的邮件
package com.tang;

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;

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

        // 注释或删除以下代码,否则报错
        //QQ邮箱,需要设置SSL加密,加上以下代码即可,大厂,其他邮箱不需要!
        // MailSSLSocketFactory msf = new MailSSLSocketFactory();
        // msf.setTrustAllHosts(true);
        // prop.put("mail.smtp.ssl.enable","true");
        // prop.put("mail.smtp.ssl.socketFactory",msf);


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

        //1.创建定义整个应用程序所需要的环境信息的session对象
        //QQ才有,其他邮箱就不用
        Session session = Session.getDefaultInstance(prop, new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                //发件人邮件用户名、授权码
                return new PasswordAuthentication("2715274537@qq.com", "vewynrbqirvaddgh");
            }
        });
        //开启session的debug模式,这样可以查看到程序发送Email的运行状态
        session.setDebug(true);
        //2.通过session得到transport对象
        Transport ts = session.getTransport();
        //3.使用邮箱的用户名和授权码连上邮件服务器
        ts.connect("smtp.qq.com","2715274537@qq.com","vewynrbqirvaddgh");
        //4.创建邮件:写文件
        //注意需要传递session
        MimeMessage message = new MimeMessage(session);
        //指明邮件的发件人
        message.setFrom(new InternetAddress("2715274537@qq.com"));
        //指明邮件的收件人
        message.setRecipient(Message.RecipientType.TO,new InternetAddress("2715274537@qq.com"));

        //=====================简单邮件=====================
        //邮件标题
        // message.setSubject("只包含文本的简单邮件");
        //邮件的文本内容
        // message.setContent("你好","text/html;charset=utf-8");
        //==========================================

        //======================复杂邮件无附件====================
        //邮件标题
        message.setSubject("包含图片的复杂邮件");
        //准备图片数据
        MimeBodyPart image = new MimeBodyPart();
        //图片需要经过数据化的处理...  DataHandler:数据处理
        DataHandler dh = new DataHandler(new FileDataSource("D:\\IT\\狂神说\\JavaWeb\\功能扩展\\mail-java\\src\\鸡蛋.jpg"));
        image.setDataHandler(dh);//在part中放入这个数据化过的图片数据
        image.setContentID("bz.jpg");//给这个part设置一个ID名字,后面可以使用

        //准备正文数据
        MimeBodyPart text=new MimeBodyPart();
        text.setContent("这是一张正文<img src='cid:bz.jpg'>","text/html;charset=UTF-8");

        //描述数据关系
        MimeMultipart mm = new MimeMultipart();
        mm.addBodyPart(text);
        mm.addBodyPart(image);
        mm.setSubType("related");

        //设置到消息中,保存修改
        message.setContent(mm);//把编辑好的邮件放到消息当中
        message.saveChanges();//保存修改
        //==========================================

        //5.发送邮件
        ts.sendMessage(message,message.getAllRecipients());
        //6.关闭连接
        ts.close();
    }
}
3、有正文、图片和附件的邮件
  • 只有部分代码,结合上面的代码补充
//======================复杂邮件有附件====================
//邮件标题
message.setSubject("包含图片和附件的复杂邮件");
//准备图片数据
MimeBodyPart image = new MimeBodyPart();
//图片需要经过数据化的处理...  DataHandler:数据处理
DataHandler dh = new DataHandler(new FileDataSource("D:\\IT\\狂神说\\JavaWeb\\功能扩展\\mail-java\\src\\鸡蛋.jpg"));
image.setDataHandler(dh);//在part中放入这个数据化过的图片数据
image.setContentID("bz.jpg");//给这个part设置一个ID名字,后面可以使用

//准备正文数据
MimeBodyPart text=new MimeBodyPart();
text.setContent("这是一张正文<img src='cid:bz.jpg'>","text/html;charset=UTF-8");

//附件
MimeBodyPart body = new MimeBodyPart();
body.setDataHandler(new DataHandler(new FileDataSource("D:\\IT\\狂神说\\JavaWeb\\功能扩展\\mail-java\\src\\鸡蛋.jpg")));//在part中放入这个数据化过的图片数据
body.setFileName("鸡蛋.jpg");//给这个part设置一个ID名字,后面可以使用

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

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

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

//设置到消息中,保存修改
message.setContent(allFile);//把编辑好的邮件放到消息当中
message.saveChanges();//保存修改
//==========================================

3、邮件发送MavenWeb项目

1、pom.xml导jar包

<?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.tang</groupId>
    <artifactId>WebMail</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.3</version>
        </dependency>
        <!-- get set toString 有参 无参 偷懒插件 -->
        <!-- <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency> -->
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4.7</version>
        </dependency>
    </dependencies>
</project>
项目之间的jar包复用

jar包复用1

jar包复用2

jar包添加到项目类库中

jar包添加到web目录

2、index.jsp首页

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<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="注册"><br/>
    </form>
    </body>
</html>

3、SendMail工具类

  • 网站3秒原则:用户体验

  • 多线程实现用户体验!(异步处理)

package com.tang.utils;

import com.tang.pojo.User;

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

//网站3秒原则:用户体验
//多线程实现用户体验!(异步处理)
public class SendMail extends Thread {
    //邮箱的密码
    private String password = "vewynrbqirvaddgh";
    //发送邮件的服务器地址
    private static final String SMTP_HOST = "smtp.qq.com";

    private User user;

    public SendMail(User user) {
        this.user = user;
    }

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

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

            //1.创建定义整个应用程序所需要的环境信息的session对象
            //QQ才有,其他邮箱就不用
            Session session = Session.getDefaultInstance(prop, new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    //发件人邮件用户名、授权码
                    return new PasswordAuthentication(user.getUsername(), password);
                }
            });
            //开启session的debug模式,这样可以查看到程序发送Email的运行状态
            session.setDebug(true);
            //2.通过session得到transport对象
            Transport ts = session.getTransport();

            //3.使用邮箱的用户名和授权码连上邮件服务器
            ts.connect(SMTP_HOST, user.getUsername(), password);

            //4.创建邮件:写文件
            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress(user.getUsername()));//发件人
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));//收件人
            message.setSubject("用户注册邮件");//邮件标题

            String info = "恭喜您注册成功,您的用户名:"+user.getUsername()+",您的密码:"+user.getPassword()+",请妥善保管,如有问题请联系网站客服!!";
            //设置到消息中,保存修改
            message.setContent(info,"text/html;charset=utf-8");//把编辑好的邮件放到消息当中
            message.saveChanges();//保存修改
            //==========================================

            //5.发送邮件
            ts.sendMessage(message, message.getAllRecipients());
            //6.关闭连接
            ts.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

4、RegisterServlet编写

package com.tang.servlet;

import com.tang.pojo.User;
import com.tang.utils.SendMail;

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

//脚手架
public class RegisterServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            //接收用户请求,
            String username = request.getParameter("username");
            String password = request.getParameter("password");
            String email = request.getParameter("email");

            User user = new User(username, password, email);

            //写完工具类后,用户注册成功之后,给用户发送一封邮件
            //我们使用线程来专门发送邮件,防止出现耗时,和网站注册人数过多的情况;
            SendMail sendMail = new SendMail(user);
            //启动线程,线程启动之后就会执行run方法来发送邮件
            sendMail.start();

            //注册用户
            request.setAttribute("message","注册成功,我们已经发了一封带了注册信息的电子邮件,请查收!如网络不稳定,可能过会儿才能收到!!");
            request.getRequestDispatcher("info.jsp").forward(request,response);

        } catch (Exception e) {
            request.setAttribute("message", "注册失败!!");
            request.getRequestDispatcher("info.jsp").forward(request, response);
            e.printStackTrace();
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }
}

5、web.xml注册

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

6、info.jsp提示页

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>xxx网站温馨提示</h1>
${message}
</body>
</html>

19、开发过程中遇到的问题

1、解决Tomcat启动时端口占用问题

结合这两篇博客应该可以解决,Tomcat启动时1099端口占用问题

2、解决IDEA控制台输出中文乱码的问题

3、解决jstl架包启动时失败的问题

  • tomcat服务器缺少jstl的架包,在tomcat的lib目录下导入jstl-api-1.2.jarstandard-1.1.2.jar

4、解决MySQL连接IDEA出现[08S01]错误的问题

5、解决邮件发送遇到的问题

版权声明:本文为糖果~原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/ZhaiTangGuo/p/16694514.html