JavaWeb

1、基本概念

1.1、前言

web开发:

  • web,网页的意思
  • 静态web
    • html、css
    • 提供给所有人看的数据始终不会发生变化!
  • 动态web
    • 淘宝、京东...,几乎所有的的网站
    • 提供给所有人看的数据始终会发生变化,每个人在不同的时间不同的地点看到的信息各不相同!
    • 技术栈:Servlet/JSP、ASP、PHP

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

1.2、web应用程序

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

  • a.html、b.html... 多个web资源,这些web资源可以被外界访问,对外界提供服务
  • 你们能访问到的任何一个页面或者资源,都存在于这个世界的某一个角落的计算机上
  • URL
  • 这个统一的web资源会被放在同一个文件夹下,web应用程序放在tomcat服务器下
  • 一个web应用由多部分组成(静态web、动态web)
    • html、css、js
    • jsp、servlet
    • java程序
    • jar包
    • 配置文件(Properties)

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

1.3、静态web

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

  • 静态web存在的缺点

    • web页面无法动态更新,所有用户看到的都是同一个页面
      • 轮播图,点击特效:伪动态
      • JavaScript(实际开发中,它用的最多)
      • VBScript
    • 它无法和数据库交互(数据无法持久化,用户无法交互)

1.4、动态web

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

缺点:

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

优点:

  • web页面可以动态更新,所有用户看到的都不是同一个页面
  • 它可以与数据库交互(数据持久化)

2、Web服务器

2.1、技术讲解

ASP:

  • 微软:国内最早流行的就是ASP
  • 在HTML中嵌入了VB的脚本,ASP + COM
  • 在ASP开发中,基本一个页面都有几千行业务代码,页面极其混乱
  • 维护成本高
  • C#
  • IIS

PHP:

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

JSP/Servlet:

B/S:浏览器和服务器

C/S:客户端和服务器

  • sun公司主推的B/S架构
  • 基于Java语言的(所有的大公司,或者一些开源的组件,都是用Java写的)
  • 可以承载三高(高并发、高可用、高性能)问题带来的影响
  • 语法像ASP,可以让ASP的人可以转JSP,加强市场强度

2.2、web服务器

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

  • IIS

    微软的,Windows中自带的

  • Tomcat

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

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

    Tomcat 实际上运行JSP 页面和Servlet。目前Tomcat最新版本为10.0.5

3、Tomcat

3.1、安装Tomcat

从官网下载tomcat,Apache Tomcat® - Apache Tomcat 9 Software Downloads

下载完成后,解压即可

3.2、Tomcat启动和配置

文件夹作用:

启动、关闭tomcat:

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

可能遇到的问题:

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

3.3、配置

可以配置启动的端口号

  • tomcat的默认端口号为8080
  • mysql:3306
  • http:80
  • https:443
<Connector port="8081" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"/>

可以配置主机的名称

  • 默认的主机名为:localhost->127.0.0.1
  • 默认网站应用存放的位置为:webapps
<Host name="www.aaa.com"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

高难度面试题:

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

  1. 输入一个域名,点击回车

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

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

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

  3. 可以配置一下环境变量(可选性)

3.4、发布一个web网站

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

  • 网站应该有的结构

    --webapps:tomcat服务器的web目录
        -ROOT
        -test:网站的目录名
        	-WEB—INF
        		-classes:java程序
        		-lib:web应用所依赖的jar包
        		-web.xml:网站配置文件
        	-index.html:默认的首页
        	-static
        		-css
        			-style.css
        		-js
        		-img
    
    

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: 14.215.177.39:443
Referrer Policy: strict-origin-when-cross-origin
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响应

服务器 --- 响应(response)--- 客户端

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:主机
    Refrush:告诉客户端,多久刷新一次
    Location:让网页重新定位
    
  2. 响应状态码

    • 200:请求响应成功

    • 3xx:请求重定向

      • 重定向:你重新到我给你新位置去
    • 4xx:找不到资源 404

      • 资源不存在
    • 5xx:服务器代码错误 500、502:网关错误

5、Maven

我们为什么要学习Maven?

  1. 在JavaWeb开发中,需要使用大量的jar包,我们手动去导入,太麻烦了
  2. 如何能够让一个东西自动帮我们导入和配置这些jar包,由此,Maven诞生了

5.1、Maven项目架构管理工具

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

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

  • 有约束,不要去违反

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

5.2、下载安装Maven

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

下载完成后,解压即可

5.3、配置环境变量

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

  • M2_HOME:maven目录下的bin目录
  • MAVEN_HOME:maven的目录
  • 在系统的path中配置%MAVEN_HOME%\bin

测试是否安装成功,CMD中输入mvn -version出现如图所示:

5.4、阿里云镜像

  • 镜像:mirrors

    • 作用:加速我们的下载
  • 国内建议使用阿里云的镜像

    在maven的conf目录中配置settings.xml文件,在<mirrors></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、本地仓库

在本地的仓库、远程仓库

建立一个本地仓库:localRepository

在maven的conf目录中配置settings.xml文件,在<localRepository></localRepository>标签中配置如下:

本地仓库的路径可以自己定义

<localRepository>D:\Environment\apache-maven-3.8.1\maven-repo</localRepository>

5.6、在IDEA中使用Maven

  1. 创建一个模版Maven项目

  1. 等待项目初始化完毕

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

  2. IDEA中的Maven设置

    注意:IDEA项目创建成功后,看一眼Maven的配置,路径是否是自己设置的路径

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

比较干净的项目

这些只有在web应用下才会有

5.8、标记文件夹功能

第一个种方式:

第二种方式:

5.9、在IDEA中配置tomcat

5.10、pom.xml文件

pom.xml是maven的核心配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!--mavaen的版本和头文件-->
<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.yunlbd</groupId>
  <artifactId>javaweb-01-maven</artifactId>
  <version>1.0-SNAPSHOT</version>
  <!--项目的打包方式
    jar:Java应用
    war:JavaWeb应用-->
  <packaging>war</packaging>

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

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

  <!--项目构建用的东西-->
  <build>
    <finalName>javaweb-01-maven</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

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

在pom.xml文件中配置如下标签:

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

5.11、IDEA的操作

这里可以生成maven的jar包的目录树结构

5.12、解决遇到的问题

  1. 无法导入maven

    由于兼容性导致的问题,降低maven的版本即可。

  2. Tomcat闪退

    由于没有配置java环境变量,配置一下即可。

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

    在全局设置中配置即可

  4. Maven项目中Tomcat无法配置

  5. maven默认web项目中的web.xml版本问题

    直接用tomcat中ROOT目录中的web.xml配置,这是最标准的

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0"
             metadata-complete="true">
    
    </web-app>
    

6、Servlet

6.1、Servlet简介

  • Servlet就是sun公司开发动态web的一门技术
  • sun公司在这些API中提供了一个接口叫做:Servlet,如果你想开发一个Servlet程序,只需要完成两个步骤:
    • 编写一个类,实现Servlet接口
    • 把开发好的Java类部署到web服务器中

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

6.2、HelloServlet

Servlet接口Sun公司有两个默认的实现类:HttpServlet、GenericServlet

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

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

    父项目的pom.xml中会有:

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

    子项目的pom.xml中会有:

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

    父项目中的java,子项目中可以直接使用

  3. Maven环境优化

    1. 修改web.xml为最新的
    2. 将maven的结构搭建完整
  4. 配置servlet依赖包

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>
    </dependencies>
    
  5. 编写一个Servlet程序

    1. 编写一个普通类
    2. 实现Servlet接口,这里我们直接继承HttpServlet
    package com.yunlbd;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    public class HelloServlet extends HttpServlet {
        //由于get和post只是请求实现的不同方式,可以相互调用,业务逻辑都一样
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            PrintWriter writer = resp.getWriter();
            writer.print("Hello, Servlet");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
  6. 编写Servlet的映射

    我们写的是Java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要在web服务中注册我们写的Servlet,还需要给他一个浏览器可以访问的路径。

    web.xml

    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.yunlbd.HelloServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    
  7. 配置Tomcat

    注意:配置项目发布的路径就可以了

  8. 启动测试

6.3、Servlet原理

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

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>/hello</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. 指定一些后缀或者前缀等等

    <!--注意点,*前面不能加项目映射的路径-->
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    
  6. 优先级问题

    指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求

    <!--404-->
    <servlet>
        <servlet-name>error</servlet-name>
        <servlet-class>com.yunlbd.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应用

6.5.1、共享数据

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

SetServlet.java

package com.yunlbd.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 SetServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String username = "张三";
        context.setAttribute("username", username);
    }

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

GetServlet.java

package com.yunlbd.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;
import java.io.PrintWriter;

public class GetServlet extends HttpServlet {

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

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

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="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>SetServlet</servlet-name>
    <servlet-class>com.yunlbd.servlet.SetServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>SetServlet</servlet-name>
    <url-pattern>/setName</url-pattern>
  </servlet-mapping>

  <servlet>
    <servlet-name>GetServlet</servlet-name>
    <servlet-class>com.yunlbd.servlet.GetServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>GetServlet</servlet-name>
    <url-pattern>/getName</url-pattern>
  </servlet-mapping>
</web-app>

测试:

先用浏览器访问:http://localhost:8080/s2/setname

然后再访问:http://localhost:8080/s2/getname

6.5.2、获取初始化参数

  1. 先在web.xml中配置

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

    package com.yunlbd.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;
    import java.io.PrintWriter;
    
    public class GetServletParams extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ServletContext context = this.getServletContext();
            String url = context.getInitParameter("url");
            PrintWriter writer = resp.getWriter();
            writer.print(url);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
  3. 再在web.xml中配置

    <servlet>
        <servlet-name>GetServletParams</servlet-name>
        <servlet-class>com.yunlbd.servlet.GetServletParams</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>GetServletParams</servlet-name>
        <url-pattern>/getParams</url-pattern>
    </servlet-mapping>
    
  4. 浏览器访问测试

6.5.3、请求转发

ServletDispatcher.java

package com.yunlbd.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 ServletDispatcher extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        context.getRequestDispatcher("/getParams").forward(req, resp);
    }

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

web.xml

<servlet>
    <servlet-name>ServletDispatcher</servlet-name>
    <servlet-class>com.yunlbd.servlet.ServletDispatcher</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>ServletDispatcher</servlet-name>
    <url-pattern>/dispatcher</url-pattern>
</servlet-mapping>

测试

浏览器中输入:http://localhost:8080/s2/dispatcher

6.5.4、读取资源文件

Properties

  • 在java目录下新建properties
  • 在resources目录下新建properties

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

思路:需要一个文件流

  1. 在resources目录下新建一个properties文件

    username=root
    password=111111
    
  2. 编写servlet

    package com.yunlbd.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;
    import java.io.InputStream;
    import java.util.Properties;
    
    public class ServletDemo05 extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            ServletContext context = this.getServletContext();
            InputStream is = context.getResourceAsStream("/WEB-INF/classes/db.properties");
            Properties prop = new Properties();
            prop.load(is);
            String username = (String) prop.get("username");
            String password = (String) prop.get("password");
            resp.getWriter().print(username + ":" + password);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
    
  3. 配置pom.xml

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

    <servlet>
        <servlet-name>ServletDemo05</servlet-name>
        <servlet-class>com.yunlbd.servlet.ServletDemo05</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>ServletDemo05</servlet-name>
        <url-pattern>/getProp</url-pattern>
    </servlet-mapping>
    
  5. 测试

    在浏览器地址栏输入:http://localhost:8080/s2/getProp

6.6、HttpServletResponse

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

  • 如果要获取客户端请求过来的参数:找HttpServletRequest
  • 如果要给客户端响应一些信息:找HttpServletResponse

6.7.1、简单分类

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

    ServletOutputStream getOutputStream() throws IOException;
    PrintWriter getWriter() throws IOException;
    
  • 负责向浏览器发送响应头的方法

    void setCharacterEncoding(String var1);
    void setContentLength(int var1);
    void setContentLengthLong(long var1);
    void setContentType(String var1);
    void setDateHeader(String var1, long var2);
    void addDateHeader(String var1, long var2);
    void setHeader(String var1, String var2);
    void addHeader(String var1, String var2);
    void setIntHeader(String var1, int var2);
    void addIntHeader(String var1, int var2);
    
  • 响应的状态码

    int SC_CONTINUE = 100;
    int SC_SWITCHING_PROTOCOLS = 101;
    int SC_OK = 200;
    int SC_CREATED = 201;
    int SC_ACCEPTED = 202;
    int SC_NON_AUTHORITATIVE_INFORMATION = 203;
    int SC_NO_CONTENT = 204;
    int SC_RESET_CONTENT = 205;
    int SC_PARTIAL_CONTENT = 206;
    int SC_MULTIPLE_CHOICES = 300;
    int SC_MOVED_PERMANENTLY = 301;
    int SC_MOVED_TEMPORARILY = 302;
    int SC_FOUND = 302;
    int SC_SEE_OTHER = 303;
    int SC_NOT_MODIFIED = 304;
    int SC_USE_PROXY = 305;
    int SC_TEMPORARY_REDIRECT = 307;
    int SC_BAD_REQUEST = 400;
    int SC_UNAUTHORIZED = 401;
    int SC_PAYMENT_REQUIRED = 402;
    int SC_FORBIDDEN = 403;
    int SC_NOT_FOUND = 404;
    int SC_METHOD_NOT_ALLOWED = 405;
    int SC_NOT_ACCEPTABLE = 406;
    int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
    int SC_REQUEST_TIMEOUT = 408;
    int SC_CONFLICT = 409;
    int SC_GONE = 410;
    int SC_LENGTH_REQUIRED = 411;
    int SC_PRECONDITION_FAILED = 412;
    int SC_REQUEST_ENTITY_TOO_LARGE = 413;
    int SC_REQUEST_URI_TOO_LONG = 414;
    int SC_UNSUPPORTED_MEDIA_TYPE = 415;
    int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
    int SC_EXPECTATION_FAILED = 417;
    int SC_INTERNAL_SERVER_ERROR = 500;
    int SC_NOT_IMPLEMENTED = 501;
    int SC_BAD_GATEWAY = 502;
    int SC_SERVICE_UNAVAILABLE = 503;
    int SC_GATEWAY_TIMEOUT = 504;
    int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
    

6.7.2、下载文件

  1. 向浏览器输出消息

  2. 下载文件

    1. 要获取下载文件的路径
    2. 下载的文件名是啥?
    3. 想办法让浏览器能够支持下载我们需要的东西
    4. 获取下载文件的输入流
    5. 创建缓冲区
    6. 获取OutputStream对象
    7. 将FileOutputStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端
  3. 代码实现

    package com.yunlbd.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 FileDownloadServlet extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //1. 要获取下载文件的路径
            String realPath = this.getServletContext().getRealPath("/WEB-INF/classes/测试.jpg");
            System.out.println(realPath);
            //2. 下载的文件名是啥?
            String filename = realPath.substring(realPath.lastIndexOf("\\") + 1);
            //3. 想办法让浏览器能够支持下载我们需要的东西
            resp.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
            //4. 获取下载文件的输入流
            FileInputStream in = new FileInputStream(realPath);
            //5. 创建缓冲区
            int len = 0;
            byte[] buffer = new byte[1024];
            //6. 获取OutputStream对象
            ServletOutputStream out = resp.getOutputStream();
            //7. 将FileOutputStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端
            while ((len = in.read(buffer)) > 0) {
                out.write(buffer, 0, len);
            }
            //8.关闭流
            in.close();
            out.close();
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    

6.7.3、验证码功能

  • 前端实现

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

    package com.yunlbd.servlet;
    
    import javax.imageio.ImageIO;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.awt.*;
    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.util.Random;
    
    public class ImageServlet extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
            //让浏览器3秒自动刷新一次
            resp.setHeader("refresh", "3");
            //在内存中创建一个图片
            BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);
            //得到图片
            Graphics2D g = (Graphics2D) image.getGraphics();
            //设置图片的背景颜色
            g.setColor(Color.WHITE);
            g.fillRect(0, 0, 80, 20);
            //给图片写数据
            g.setColor(Color.blue);
            g.setFont(new Font(null, Font.BOLD, 20));
            g.drawString(createNum(), 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 createNum() {
            Random random = new Random();
            String num = random.nextInt(999999) + "";
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 6 - num.length(); i++) {
                sb.append("0");
            }
            return sb + num;
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    

6.7.4、实现重定向

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

常见场景:

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

测试:

package com.yunlbd.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.sendRedirect("/s3/img");
    }

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

重定向和转发的区别:

  • 相同点
    • 页面都会实现跳转
  • 不同点
    • 请求转发,url不会产生变化,状态码为307
    • 重定向,url会产生变化,状态码为302

6.7、HttpServletRequest

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

获取前端传递的参数、请求转发

req.getParameter();   //常用
req.getParameterValues();  //常用
req.getParameterMap();  
req.getParameterNames();

//请求转发
req.getRequestDispatcher("/success.jsp").forward(req, resp);

示例代码:

  1. pom.xml中引入javax.servlet.jspjar包

    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>javax.servlet.jsp-api</artifactId>
        <version>2.3.3</version>
    </dependency>
    
  2. 编写login.jspsuccess.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
        <head>
            <title>登录</title>
        </head>
        <body>
            <form action="${pageContext.request.contextPath}/login" method="post">
                帐号:<input type="text" name="username"><br>
                密码:<input type="password" name="password"><br>
                爱好:<input type="checkbox" name="hobby" value="看书">看书
                <input type="checkbox" name="hobby" value="篮球">篮球
                <input type="checkbox" name="hobby" value="代码">代码
                <input type="checkbox" name="hobby" value="音乐">音乐
                <br>
                <input type="submit" value="登录">
            </form>
        </body>
    </html>
    
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
        <head>
            <title>Title</title>
        </head>
        <body>
            <h2>登录成功</h2>
        </body>
    </html>
    
  3. 编写LoginServlet.java

    package com.yunlbd.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("hobby");
            System.out.println(username);
            System.out.println(password);
            System.out.println(Arrays.toString(hobbies));
    
            req.getRequestDispatcher("/success.jsp").forward(req, resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
  4. 配置web.xml文件

    <servlet>
        <servlet-name>LoginServlet</servlet-name>
        <servlet-class>com.yunlbd.servlet.LoginServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>LoginServlet</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>
    
  5. 测试

7、Cookie、Session

7.1、会话

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

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

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

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

7.2、保存会话的两种技术

  • cookie:客户端技术(响应、请求)
  • session:服务器技术,利用这个技术,可以保存用户的会话信息,我们可以把信息或数据放在session中

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

7.3、Cookie

  1. 从请求中拿到cookie信息
  2. 服务器响应给客户端cookie
package com.yunlbd.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 CookieServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html; charset=utf-8");

        PrintWriter out = resp.getWriter();
        //服务器从客户端获取cookie
        Cookie[] cookies = req.getCookies();
        if (cookies.length > 0) {
            out.write("你上次访问的时间是:");
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals("lastLoginTime")) {
                    long lastLoginTime = Long.parseLong(cookie.getValue());
                    Date date = new Date(lastLoginTime);
                    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只能保存一个信息
  • 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie
  • cookie大小限制为4kb
  • 300个cookie是浏览器上限

删除cookie:

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

编码解码

URLEncoder.encode("张三", "UTF-8");
URLDecoder.decode(cookie.getValue(), "UTF-8");

7.4、Session(重点)

什么是session:

  • 服务器会给每一个用户(浏览器)创建一个session对象
  • 一个session独占一个浏览器,只要浏览器没关闭,这个session就存在
  • 比如:用户登录之后,整个网站都可以访问

session和cookie的区别:

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

使用场景:

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

示例代码:

1、创建session,并赋值

package com.yunlbd.servlet;

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 SessionServlet01 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=utf-8");
        HttpSession session = req.getSession();
        session.setAttribute("name", "张三");
        resp.getWriter().write("session被创建,ID为" + session.getId() + ",name为" + session.getAttribute("name"));
    }

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

2、手动销毁session

package com.yunlbd.servlet;

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 SessionServlet02 extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        session.removeAttribute("name");
        //手动注销session
        session.invalidate();
    }

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

3、自动销毁session,在web.xml中配置

<!--设置session失效的时间-->
<session-config>
    <!--15分钟后,session自动失效-->
    <session-timeout>15</session-timeout>
</session-config>

8、JSP

8.1、什么是JSP

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

最大的特点:

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

8.2、JSP原理

服务器内部工作:

tomcat中有一个work目录,在work\Catalina\localhost\ROOT\org\apache\jsp目录下你会发现index_jsp.class和index_jsp.java

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

JSP最终也会被转换成一个java类

JSP本质上就是一个servlet

在JSP页面中,只要是Java代码就会原封不动的输出,如果是HTML代码,就会被转换为:

out.write("<html>\r\n");

这样的格式,输出到前端!

8.3、JSP的基础语法

JSP作为Java技术的一种应用,它拥有一些自己扩充的语法,Java所有的语法都支持。

  • JSP表达式

    <%--JSP表达式,用来将程序输出到客户端--%>
    <%=new java.util.Date()%>
    
  • JSP脚本片段

    <%--JSP脚本片段--%>
    <%  
      int num = 0;
      for (int i = 0; i < 100; i++) {
        num += i;
      }
      out.println("<h1>sum=" + num + "</h1>");
    %>
    
  • 脚本片段的在实现

    <%
        int x = 10;
        out.println(x);
    %>
    <p>这是一个JSP文档</p>
    <%
        int y = 5;
        out.println(y);
    %>
    <hr>
    <%--在代码中嵌入HTML元素--%>
    <%
        for (int i = 0; i < 5; i++) {
    %>
        <h2><%=i%> Hello, JSP!</h2>
    <%
        }
    %>
    
  • JSP声明

    <%!
        static {
            System.out.println("Loading Servlet!");
        }
    
        private final int globalVar = 0;
    
        public void f() {
            System.out.println("进入了方法f");
        }
    %>
    

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

    <%=%> JSP表达式

    <%%> JSP脚本片段

    <%!%> JSP声明

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

8.4、JSP指令

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

<%--@include会将两个页面合二为一--%>
<%@ include file="common/header.jsp"%>
<h1>网页主体</h1>
<%@ include file="common/footer.jsp"%>

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

8.5、9大内置对象

  • 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号");//保存的数据只在服务器中有效,从打开服务器到关闭服务器

request:客户端向服务器发送请求,产生的数据,用户看完就没用了,比如:新闻

session:客户端向服务器发送请求,产生的数据,用户用完一会还有用,比如:购物车

application:客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用,比如:聊天数据

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

所需依赖包配置,pom.xml

<!--JSTL表达式依赖-->
<dependency>
    <groupId>javax.servlet.jsp.jstl</groupId>
    <artifactId>jstl-api</artifactId>
    <version>1.2</version>
</dependency>

<!--standard标签库-->
<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

EL表达式:${ }

  • 获取数据

  • 执行运算

  • 获取web开发的常用对象

JSP标签

<%--跳转页面并携带参数--%>
<jsp:forward page="jsptag2.jsp">
    <jsp:param name="name" value="zhangsan"/>
    <jsp:param name="age" value="30"/>
</jsp:forward>

JSTL标签

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

  • 核心标签

    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    

  • 格式化标签

    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    

  • SQL标签

    <%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>
    

  • XML标签

    <%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
    

    在使用xml标签前,你必须将XML 和 XPath 的相关包拷贝至你的<Tomcat 安装目录>\lib下:

  • JSTL 函数

    <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
    

使用步骤:

  1. 引入对应的taglib
  2. 使用其中的方法
  3. 在Tomcat也要引入jstl包,否则会报错:JSTL解析错误(低版本会报错)

9、JavaBean

实体类

JavaBean有特定的写法:

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

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

package com.yunlbd.pojo;

public class People {
    private int id;
    private String name;
    private int age;
    private String address;

    public People() {}

    public People(int id, String name, int age, String address) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.address = address;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "People{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
  <jsp:useBean id="people" class="com.yunlbd.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="30"/>
  <jsp:setProperty name="people" property="address" value="合肥"/>

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

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)

  • 接收用户的请求
  • 交给业务层处理对应的代码
  • 控制视图的跳转

例如登录:

  1. 登录

  2. 接收用户的登录请求

  3. 处理用户的请求(获取用户登录的参数,username,password)

  4. 交给业务层处理登录业务(判断用户名密码是否正确:事务)

  5. Dao层查询用户名和密码是否正确

  6. 连接数据库进行查询

11、过滤器Filter

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

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

Filter开发步骤:

  1. 导包

    pom.xml

    <dependencies>
        <!--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>
    </dependencies>
    
  2. 编写servlet

    package com.yunlbd.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);
        }
    }
    
  3. 编写过滤器

    package com.yunlbd.filter;
    
    import javax.servlet.*;
    import java.io.IOException;
    
    public class CharacterEncodingFilter implements Filter {
    
        /**
         * web服务器启动,就已经初始化了,随时等待过滤对象出现
         * @param filterConfig
         * @throws ServletException
         */
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("CharacterEncodingFilter初始化");
        }
    
        /**
         * 1.过滤中的所有代码,在过滤特定请求的时候都会执行
         * 2.必须要让过滤器继续通行
         * @param request
         * @param response
         * @param chain
         * @throws IOException
         * @throws ServletException
         */
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            request.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/html;charset=UTF-8");
            System.out.println("CharacterEncodingFilter执行前...");
            //让我们的请求继续走,如果不写,程序到这里就被拦截停止
            chain.doFilter(request, response);
            System.out.println("CharacterEncodingFilter执行后...");
        }
    
        /**
         * web服务器关闭的时候,过滤器会销毁
         */
        public void destroy() {
            System.out.println("CharacterEncodingFilter被销毁");
        }
    }
    
  4. 在web.xml中配置Filter

    <?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>ShowServlet</servlet-name>
            <servlet-class>com.yunlbd.servlet.ShowServlet</servlet-class>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>ShowServlet</servlet-name>
            <url-pattern>/show</url-pattern>
        </servlet-mapping>
    
        <servlet-mapping>
            <servlet-name>ShowServlet</servlet-name>
            <url-pattern>/servlet/show</url-pattern>
        </servlet-mapping>
    
        <filter>
            <filter-name>CharacterEncodingFilter</filter-name>
            <filter-class>com.yunlbd.filter.CharacterEncodingFilter</filter-class>
        </filter>
    
        <filter-mapping>
            <filter-name>CharacterEncodingFilter</filter-name>
            <!--只要是/servlet下的任何请求,都会经过这个过滤器-->
            <url-pattern>/servlet/*</url-pattern>
        </filter-mapping>
    </web-app>
    
  5. 测试

12、监听器

实现一个监听器的接口,用于统计网站在线人数

  1. 编写一个监听器

    package com.yunlbd.listener;
    
    import javax.servlet.ServletContext;
    import javax.servlet.http.HttpSessionEvent;
    import javax.servlet.http.HttpSessionListener;
    
    //统计网站在线人数
    public class OnlineCountListener implements HttpSessionListener {
    
        public void sessionCreated(HttpSessionEvent se) {
            ServletContext  ctx = se.getSession().getServletContext();
            System.out.println(se.getSession().getId());
            Integer onlineCount = (Integer) ctx.getAttribute("onlineCount");
            if (onlineCount == null) {
                onlineCount = 1;
            } else {
                onlineCount += 1;
            }
            ctx.setAttribute("onlineCount", onlineCount);
        }
    
        public void sessionDestroyed(HttpSessionEvent se) {
            ServletContext  ctx = se.getSession().getServletContext();
            Integer onlineCount = (Integer) ctx.getAttribute("onlineCount");
            if (onlineCount == null) {
                onlineCount = 0;
            } else {
                onlineCount -= 1;
            }
            ctx.setAttribute("onlineCount", onlineCount);
        }
    }
    
  2. 在web.xml中注册监听器

    <listener>
        <listener-class>com.yunlbd.listener.OnlineCountListener</listener-class>
    </listener>
    
  3. 写一个jsp页面用于测试

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
        <head>
            <title>Title</title>
        </head>
        <body>
            <h1>当前有 <span style="color: blue"><%=this.getServletConfig().getServletContext().getAttribute("onlineCount")%></span> 人在线</h1>
        </body>
    </html>
    
  4. 测试

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

用户登录只有才能进入主页,用户注销后就不能进入主页了

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

  2. 进入主页的时候要判断用户是否已经登录

    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) resp;
    if(resquest.getSession.getAttribute(Constant.USER_SESSION) == null) {
        response.sendRedirect("/error.jsp");
    }
    chain.doFilter(request, response);
    

14、Junit单元测试

依赖

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

简单实用

@Test直接只有在方法上有效,只要加了这个注解的方法,就可以直接运行!

import org.junit.Test;

public class TestJunit {

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

15、文件上传

15.1、文件上传调优

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

15.2、文件上传需要使用到的3个类+1个属性

  • ServletFileUpload:在后端获取文件上传的文件数据,并将上传文件数据的表单中的每个输入项都封装为一个FileItem对象
  • FileItem
  • DiskFileItemFactory:使用ServletFileUpload解析前端表单传过来的文件数据时需要使用到DiskFileItemFactory对象,所以在获取ServletFileUpload对象之前我们需要先获取DiskFileItemFactory对象
  • fileItemFactory属性:ServletFileUpload对象的一个属性

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

15.2、示例代码

所需jar包:

index.jsp

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

<%--通过表单上传文件
    post:上传的文件没有限制
    get:上传的文件有限制
--%>
<form action="upload.do" method="post" enctype="multipart/form-data">
  上传用户:<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>

info.jsp

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

FileUpload.java

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

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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 FileUpload extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //判断上传的表单是普通表单还是带文件的表单,是返回true,否返回false;
        if (!ServletFileUpload.isMultipartContent(request)) {
            return;//如果这是一个普通文件我们直接返回
        }//如果通过了这个if,说明我们的表单是带文件上传的

        //创建上传文件的保存目录,为了安全建议在WEB-INF目录下,用户无法访问
        String uploadpath = this.getServletContext().getRealPath("WEB-INF/Upload");//获取上传文件的保存路径
        File uploadfile = new File(uploadpath);
        if (!uploadfile.exists()) {
            uploadfile.mkdir();//如果目录不存在就创建这样一个目录
        }

        //临时文件
        //临时路径,如果上传的文件超过预期的大小,我们将它存放到一个临时目录中,过几天自动删除,或者提醒用户转存为永久
        String tmppath = this.getServletContext().getRealPath("WEB-INF/tmp");
        File file = new File(tmppath);
        if (!file.exists()) {
            file.mkdir();//如果目录不存在就创建这样临时目录
        }
        //处理上传的文件一般需要通过流来获取,我们可以通过request.getInputstream(),原生态文件上传流获取,十分麻烦
        //但是我们都建议使用Apache的文件上传组件来实现,common-fileupload,它需要依赖于common-io组件;

        try {
            //1、创建DiskFileItemFactory对象,处理文件上传路径或限制文件大小
            DiskFileItemFactory factory = gteDiskFileItemFactory(file);
            //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 gteDiskFileItemFactory(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(new ProgressListener() {
            public void update(long pBytesRead, long lpContentLenght, int i) {
                //pBytesRead:已读取到的文件大小
                //pContentLenght:文件大小
                System.out.println("总大小:" + lpContentLenght + "已上传:" + pBytesRead);
            }
        });

        //处理乱码问题
        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对象
        List<FileItem> fileItems = upload.parseRequest(request);
        for (FileItem fileItem : fileItems) {
            if (fileItem.isFormField()) { //判断是普通表单还是带文件的表单
                //getFieldName指的是前端表单控件的name
                String name = fileItem.getFieldName();
                String value = fileItem.getString("UTF-8");//处理乱码
                System.out.println(name + ":" + value);
            } else {//判断它是带文件的表单

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

                //拿到文件的名字
                String uploadFileName = fileItem.getName();
                System.out.println("上传的文件名:" + uploadFileName);

                if (uploadFileName.trim().equals("")) {
                    continue;
                }

                //获得上传的文件名,例如/img/girl/ooa.jpg,只需要ooa,其前面的后面的都不需要
                String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);
                //获得文件的后缀名
                String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1);
                      /*
                        如果文件后缀名fileExtName不是我们所需要的
                        就直接return,不处理,告诉用户文件类型不对
                     */

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

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

                String uuidPath = UUID.randomUUID().toString();
                System.out.println("文件信息【文件名:" + fileName + "文件类型:" + fileExtName + "】");

                //可以使用UUID(唯一通用识别码)来保证文件名的统一
                String uuidFileName = UUID.randomUUID().toString();

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

                //创建一个文件输出流
                FileOutputStream fos = new FileOutputStream(uploadpath + "/" + uuidFileName + "." + fileExtName);

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

                //判断是否读取完毕
                int len;
                //如果大于0,说明还存在数据
                while ((len = inputStream.read(buffer)) > 0) {
                    fos.write(buffer, 0, len);
                }
                //关闭流
                fos.close();
                inputStream.close();
                msg = "文件上传成功!";
                fileItem.delete();//上传成功,清除临时文件
            }
        }
        return msg;
    }
}

web.xml

<servlet>
    <servlet-name>FileUpload</servlet-name>
    <servlet-class>com.yunlbd.servlet.FileUpload</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>FileUpload</servlet-name>
    <url-pattern>/upload.do</url-pattern>
</servlet-mapping>

16、邮件发送原理及实现

16.1、电子邮件

要在网络上实现邮件功能,必须要有专门的邮件服务器。 这些邮件服务器类似于现实生活中的邮局,它主要负责接收用户投递过来的邮件,并把邮件投递到邮件接收者的电子邮箱中。 SMTP服务器地址:一般是 smtp.xxx.com,比如163邮箱是smtp.163.com,qq邮箱是smtp.qq.com。 电子邮箱(E-Mail地址)的获得需要在邮件服务器上进行申请。比如我们要使用QQ邮箱,就需要开通邮箱功能;

16.2、传输协议

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

16.3、邮件收发原理

1、邮件服务器

①SMTP邮件服务器:替用户发送邮件和接收外面发送给本地用户的邮件

②POP3/IMAP邮件服务器:帮助用户读取SMTP邮件服务器接收进来的邮件

2、邮件传输协议

①电子邮件需要在邮件客户端和邮件服务器之间,以及两个邮件服务器之间进行邮件传递,那就必须要遵守一定的规则,这个规则就是邮件传输协议

②SMTP协议:全称为 Simple Mail Transfer Protocol,简单邮件传输协议。它定义了邮件客户端软件和SMTP邮件服务器之间,以及两台SMTP邮件服务器之间的通信规则

③POP3协议:全称为 Post Office Protocol,邮局协议。它定义了邮件客户端软件和POP3邮件服务器的通信规则

④IMAP协议:全称为 Internet Message Access Protocol,Internet消息访问协议,它是对POP3协议的一种扩展,也是定义了邮件客户端软件和IMAP邮件服务器的通信规则

我们说所有的邮件服务器和邮件客户端软件程序都是基于上面的协议编写的

16.4、使用Java实现邮件发送需要使用到的类

我们将用代码完成邮件的发送。这在实际项目中应用的非常广泛,比如注册需要发送邮件进行账号激活,再比如OA项目中利用邮件进行任务提醒等等。

使用Java发送 E-mail 十分简单,但是首先你应该准备 JavaMail API 和Java Activation Framework 。

得到两个jar包:

  1. mail.jar
  2. activation.jar

JavaMail 是sun公司(现以被甲骨文收购)为方便Java开发人员在应用程序中实现邮件发送和接收功能而提供的一套标准开发包,它支持一些常用的邮件协议,如前面所讲的SMTP,POP3,IMAP,还有MIME等。我们在使用JavaMail API 编写邮件时,无须考虑邮件的底层实现细节,只要调用JavaMail 开发包中相应的API类就可以了。

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

  • 创建包含邮件服务器的网络连接信息的Session对象。
  • 创建代表邮件内容的Message对象
  • 创建Transport对象,连接服务器,发送Message,关闭连接

主要有四个核心类,我们在编写程序时,记住这四个核心类,就很容易编写出Java邮件处理程序。

16.5、简单邮件发送实现

邮件分类

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

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

代码实现:

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邮件服务器
        prop.setProperty("mail.transport.protocol", "smtp"); // 邮件发送协议
        prop.setProperty("mail.smtp.auth", "true"); // 需要验证用户名密码

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

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

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

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

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

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

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

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

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

运行测试:

16.6、复杂邮件发送实现

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

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

  • MimeBodyPart类

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

  • MimeMultipart类

    javax.mail.internet.MimeMultipart是抽象类Multipart的实现子类,它用来组合多个MIME消息。一个MimeMultipart对象可以包含多个代表MIME消息的MimeBodyPart对象。即一个MimeMultipart对象表示多个MimeBodyPart的集合,而一个MimeMultipart表示的就是我们一封邮件的内容

    MimeMultipart对象的使用的时候需要设置setSubType()的属性值,一共就下面3种取值

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

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

相较于简单邮件,复杂邮件变化的地方只是在于邮件内容本身会发送变化,而其他的步骤都是一样的

1、准备一些参数

2、获取session对象

3、获取传输对象

4、登陆授权

5、写邮件 (和简单邮件相区别)

6、发送邮件

7、关闭服务器资源

发送包含图片的复杂邮件的代码实现:

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 MailDemo02 {
    public static void main(String[] args) throws Exception {
        Properties prop = new Properties();
        prop.setProperty("mail.host", "smtp.qq.com");  //设置QQ邮件服务器
        prop.setProperty("mail.transport.protocol", "smtp"); // 邮件发送协议
        prop.setProperty("mail.smtp.auth", "true"); // 需要验证用户名密码

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

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

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

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

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

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

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

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

        // 准备邮件数据

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

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

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

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

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

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

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

发送包含附件的复杂邮件:

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 MailDemo03 {
    public static void main(String[] args) throws Exception {
        Properties prop = new Properties();
        prop.setProperty("mail.host", "smtp.qq.com");  //设置QQ邮件服务器
        prop.setProperty("mail.transport.protocol", "smtp"); // 邮件发送协议
        prop.setProperty("mail.smtp.auth", "true"); // 需要验证用户名密码

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

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

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

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

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

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

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

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

        /*
         *  编写邮件内容
         *  1.图片
         *  2.附件
         *  3.文本
         */

        //图片
        MimeBodyPart body1 = new MimeBodyPart();
        body1.setDataHandler(new DataHandler(new FileDataSource("图片路径")));
        body1.setContentID("bz.jpg"); //图片设置ID

        //文本
        MimeBodyPart body2 = new MimeBodyPart();
        body2.setContent("这是一张图片<img src='cid:bz.jpg'>", "text/html;charset=utf-8");

        //附件
        MimeBodyPart body3 = new MimeBodyPart();
        body3.setDataHandler(new DataHandler(new FileDataSource("文件路径\\test1.txt")));
        body3.setFileName("test1.txt"); //附件设置名字

        MimeBodyPart body4 = new MimeBodyPart();
        body4.setDataHandler(new DataHandler(new FileDataSource("文件路径\\test2.doc")));
        body4.setFileName("test2.doc"); //附件设置名字

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

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

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

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

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

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

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

16.7、网站注册发送邮件功能实现

分析:在我们注册的时候,前端我们填写的就是一个表单,这个表单提交给后端的servlet,这个servlet就向我们填写的那个邮箱中发送一封邮件

注册页面(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="注册">
</form>

</body>
</html>

提示成功的页面(info.jsp):

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>提示信息</title>
</head>
<body>
<h2>温馨提示:</h2>
${message}
</body>
</html>

编写pojo(User.java):

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

    public User(){}

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

编写多线程工具类(Sendmail.java):

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

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

public class Sendmail implements Runnable {
    private final String from = "xxxxx@qq.com";//我们用来向客户发送邮件的邮箱
    private final String username = "xxxxx@qq.com";//用于登陆SMTP服务器的用户名
    private final String password = "授权码";//授权码

    private User user;

    public Sendmail(User user) {
        this.user = user;//用于获取用户邮箱地址
    }

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

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

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

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

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

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

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

            //4、创建邮件对象MimeMessage——点击网页上的写信
            //创建一个邮件对象
            MimeMessage message = new MimeMessage(session);
            //指明邮件的发件人——在网页上填写发件人
            message.setFrom(new InternetAddress(username));//设置发件人
            //指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发——在网页上填写收件人
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));//设置收件人
            //邮件的标题——在网页上填写邮件标题
            message.setSubject("欢迎注册!");//设置邮件主题
            //邮件的文本内容——在网页上填写邮件内容
            message.setContent("<p><h2>恭喜注册成功!</h2></p>您的用户名为:<h4>" + user.getUsername() + "</h4>请妥善保管您的密码,如有问题请及时联系网站客服,再次欢迎您的加入!!", "text/html;charset=UTF-8");//设置邮件的具体内容

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

            //6、关闭连接对象,即关闭服务器上的连接资源
            ts.close();
        } catch (MessagingException | GeneralSecurityException e) {
            e.printStackTrace();
        }
    }
}

编写servlet(RegisterServlet.java):

import com.yunlbd.pojo.User;
import com.yunlbd.utils.Sendmail;

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

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

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

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

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

配置web.xml

<servlet>
    <servlet-name>RegisterServlet</servlet-name>
    <servlet-class>com.yunlbd.servlet.RegisterServlet</servlet-class>
</servlet>

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

测试:

posted @ 2021-08-11 16:28  蓝色空间号  阅读(43)  评论(0)    收藏  举报