JavaWeb

JavaWeb

课程:【狂神说Java】JavaWeb入门到实战_哔哩哔哩_bilibili

一.基本概念

1.基本概念

  • 静态web:html,css(提供给所有人看的数据,始终不会发生变化)
  • 动态web:Servlet/JSP,ASP,PHP,淘宝等几乎所有的网站(提供给所有人看的数据,始终会发生变化,每个人在不同时间,不同地方看到的都不同)

 2.web应用程序

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

  • a.html、b.html等多个web资源,这些web资源可以被外界访问,对外界提供服务。
  • 你们能访问到的任何一个页面或者资源,都存在于这个世界的某一个角落的计算机上。
  • URL:统一资源定位符。
  • 这个统一的web资源会被放在同一个文件夹下,web应用程序—>Tomcat:服务器。
  • web应用程序编写完毕后,若想提供给外界访问:需要一个服务器来统一管理。

3.静态web 

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

缺点:

(1)web页面无法更新,所有用户看到的都是同一个页面。(可以使用轮播图,实现伪动态)

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

4.动态web 

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

缺点:

(1)假如服务器的动态web资源出了问题,需要我们停机维护重新编写后台程序,重新发布。 

优点:

弥补了静态web的缺点。

二.web服务器 

1.技术讲解 

2. web服务器

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

(1)IIS:微软的服务器 

(2)tomcat:
Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun和其他一些公司及个人共同开发而成。由于有了Sun 的参与和支持,最新的Servlet 和JSP规范总是能在Tomcat 中得到体现,Tomcat 5支持最新的Servlet 2.4和JSP2.0规范。
Tomcat服务器是一个免费的开放源代码的Web应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP程序的首选。、

对于一个初学者来说,可以这样认为,当在一台机器上配置好Apache服务器,可利用它响应HTML(标准通用标记语言下的一个应用)页面的访问请求。实际上Tomcat是Apache服务器的扩展,但运行时它是独立运行的,所以当你运行tomcat时,它实际上作为一个与Apache 独立的进程单独运行的。

三.tomcat详解 

1.安装与启动 

javaweb-03:Tomcat详解_哔哩哔哩_bilibili

2. 配置

 

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

  1. 输入一个域名,回车
  2. 检查本机的C:Windows\System32\drivers\etc\hosts配置文件下有没有这个域名映射
    有:直接返回对应的ip地址,这个地址中,有我们需要访问的web程序,可以直接访问。

    没有:去DNS服务器找,找到的话就返回,找不到就返回找不到。

3.解决中文乱码 

查找以下文件:logging.properties,打开找到照片行显示的UTF-8,改成GBK

4.项目结构

四.Http  

1.什么是http

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

  • 文本:html,字符串…
  • 超文本:图片,音乐,视频,定位,地图…
  • 默认端口:80
    HTTPS:安全的协议
  • 默认端口:443

2.http的两个时代

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

3.http请求 

客户端—->发请求(request)—->服务器(比如:访问百度) 

 (1)常规:

// 请求地址
Request URL: https://www.baidu.com/
// 请求方法
Request Method: GET
// 状态代码
Status Code: 200 OK
// 远程地址
Remote Address: 14.215.177.38:443
// 引用站点策略
Referrer Policy: strict-origin-when-cross-origin

(2)请求行:

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

(3)请求头(消息头) 

Accept: text/html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: max-age=0
Connection: keep-alive
Accept: 告诉浏览器,它所支持的数据类型
Accept-Encoding: 告诉浏览器,它支持哪种编码格式:GBK,UTF-8,GB2312,ISO8859-1
Accept-Language: 告诉浏览器,它的语言环境
Cache-Control: 缓存控制
Connection: 告诉浏览器,请求完成是断开还是保持
HOST:主机

4.http响应 

// 缓存控制
Cache-Control: no-cache
// 保持连接(http1.1)
Connection: keep-alive
// 文本编码类型
Content-Encoding: gzip
// 响应类型
Content-Type: text/html;charset=utf-8

(1)响应体

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

(2)响应状态码

  • 200:响应成功
  • 3xx:请求重定向(304等等)
  • 4xx:找不到资源(404等等)
  • 5xx:服务器代码错误(500代码错误,502网关错误)

 五.Maven

 帮我们自动导入配置jar包,Maven项目架构管理工具。

Maven 是一个项目管理工具,可以对 Java 项目进行构建、依赖管理。 

Maven 也可被用于构建和管理各种项目,例如 C#,Ruby,Scala 和其他语言编写的项目。Maven 曾是 Jakarta 项目的子项目,现为由 Apache 软件基金会主持的独立 Apache 项目。 

教程:javaweb-05:Maven环境搭建_哔哩哔哩_bilibili 

Maven 能够帮助开发者完成以下工作:构建,文档生成,报告,依赖,SCMs,发布,分发,邮件列表

1.下载 

IDEA 2018 2020 2021 2022 各版本对Maven版本兼容问题汇总:

IDEA 2018 2020 2021 2022 各版本对Maven版本兼容问题汇总_idea和maven版本对照-优快云博客

下载链接: 

Maven – 下载 Apache Maven

2. 配置与问题

 Maven配置教程-优快云博客

Unable to import maven project: See logs for details问题的解决方法:

关于配置idea中Maven报错Unable to import maven project: See logs for details问题的解决方法_see logs for details怎么办-优快云博客

IDEA日常填坑:Cannot resolve plugin org.apache.maven.plugins:maven-war-plugin :

IDEA日常填坑:Cannot resolve plugin org.apache.maven.plugins:maven-war-plugin-优快云博客

IDEA中Maven Project 的Plugins项目出现红色波浪线解决办法:

Maven项目中出现红色波浪线的解决过程_maven plugins下有红色波浪线-优快云博客

3.web初始文件 

常见问题:在IDEA中创建Maven项目时只有一个idea文件夹

在IDEA中创建Maven项目时没有src文件、不自动配置文件_2023idea创建maven项目没有src文件-优快云博客

普通maven项目

 只有web情况下的maven项目

文件修改

补全mavenweb项目的文件

 4.idea安装tomcat 

错误:Error: Application Server not specified-优快云博客 

5.在idea在运行项目与问题 

问题:JavaWeb学习报错【HTTP Status 404 – 未找到】 

JavaWeb学习报错【HTTP Status 404 – 未找到】_javawebhttp找不到状态-优快云博客

6.pom文件 

idea创建项目没有target:idea创建项目没有target_idea 梅有target-优快云博客

 

你真的熟悉POM文件吗?-KuangStudy-文章

    <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>

六.Servlet 

架包: 

Maven Repository: javax.servlet.jsp » jsp-api (mvnrepository.com)

1.servlet简介 

(1).Servlet就是sun公司开发动态web的一门技术。

(2).Sun在这些API中提供一个接口叫做:Servlet,如果你想开发一个Servlet程序,只需要完成两个小步骤:一.编写一个类,实现Servlet接口;二.把开发好的Java类部署到web服务器中。把实现了Servlet接口Java程序叫做,Servlet

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

2.HelloServlet 

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

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

父工程中会有:

    <groupId>com.bash.web</groupId>
    <artifactId>javaweb</artifactId>
    <version>1.0-SNAPSHOT</version>

    <modules>
        <module>web</module>
    </modules>

子项目中会有:

  <parent>
    <groupId>com.bash.web</groupId>
    <artifactId>javaweb</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>

父项目中的jar包,子项目可以直接使用。反之,不可。

(3).Maven环境优化

  1.  修改web.xml为最新的
  2. 将maven的结构搭建完整

(4).编写一个Servlet程序 

关于IDEA WEB项目的创建与无法继承HttpServlet问:

关于IDEA WEB项目的创建与无法继承HttpServlet问题_idea 继承不了 http-优快云博客

1.编写一个普通的类
2.实现Servlet接口,这里我们直接继承HttpServlet类

 public class HelloServlet extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         System.out.println("hello servlet");
         PrintWriter writer = resp.getWriter();
         writer.println("Hello Servlet");
     }
     @Override
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         this.doGet(req,resp);
     }
 }

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

<!--注册servlet-->
<servlet>
 <servlet-name>helloservlet</servlet-name>
 <servlet-class>com.sunyiwenlong.servlet.HelloServlet</servlet-class>
</servlet>
<!--servlet请求路径-->
<servlet-mapping>
 <servlet-name>helloservlet</servlet-name>
 <url-pattern>/hello</url-pattern>
</servlet-mapping>

(6).配置tomcat
注意:配置项目发布路径就可以了

(7).启动测试

3. Servlet原理 

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. 优先级:固定优先,默认靠后

5.ServletContext 


    // string this.getInitParameter(); 获取初始化参数(web.xml文件中的初始化参数)
    // ServletConfig this.getServletConfig(); 获取servlet的配置(web.xml文件中的配置)
    // ServletContext this.getServletContext(); 获取servlet上下文

一.共享数据 



public class SetServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context =this.getServletContext();
        String username = "fuck";
        // void setAttribute(String s, Object o)将一个数据保存在了ServletContext中
        context.setAttribute("username",username);

    }

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

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

        ServletContext context =this.getServletContext();
        // object getAttribute(String s)从ServletContext中取出数据
        String name = (String)context.getAttribute("username");

        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        resp.getWriter().println(name);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }
}
// web.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
      xmlns="http://java.sun.com/xml/ns/j2ee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
 <servlet-name>helloservlet1</servlet-name>
 <servlet-class>HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
 <servlet-name>helloservlet1</servlet-name>
 <url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet>
 <servlet-name>getservlet</servlet-name>
 <servlet-class>GetServlet</servlet-class>
</servlet>
<servlet-mapping>
 <servlet-name>getservlet</servlet-name>
 <url-pattern>/getc</url-pattern>
</servlet-mapping>
</web-app>

相当于容器,把值先存储进去,再取出来 

二.获取初始化参数

// web.xml文件
<!--配置一些web应用一些初始化参数-->
<context-param>
 <param-name>url</param-name>
 <param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>
public class ServletDemo03 extends HttpServlet {
 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     String url = this.getInitParameter("url");
     resp.getWriter().println(url);
 }
 @Override
 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     this.doGet(req, resp);
 }
}

三.请求转发 

// web.xml文件
// 请求sd4
<servlet>
 <servlet-name>gp</servlet-name>
 <servlet-class>ServletDemo03</servlet-class>
</servlet>
<servlet-mapping>
 <servlet-name>gp</servlet-name>
 <url-pattern>/gp</url-pattern>
</servlet-mapping>
<servlet>
 <servlet-name>sd4</servlet-name>
 <servlet-class>ServletDemo04</servlet-class>
</servlet>
<servlet-mapping>
 <servlet-name>sd4</servlet-name>
 <url-pattern>/sd4</url-pattern>
</servlet-mapping>
// 请求/sd4找到ServletDemo04,ServletDemo04进行请求转发到/gp,到/gp的页面
// (浏览器路径是sd4的路径,页面拿到的是/gp的数据)

public class ServletDemo04 extends HttpServlet {
 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     ServletContext context = this.getServletContext();
     System.out.println("进入了demo04");
     // RequestDispatcher requestDispatcher = context.getRequestDispatcher("String s");// 转发的路径
     // requestDispatcher.forward(req,resp);// 调用forward请求转发
     context.getRequestDispatcher("/gp").forward(req,resp);
 }

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

第一个为请求转发,第二个为重定向 

四.读取资源文件 

public class ServletDemo05 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        InputStream stream = this.getServletContext().getResourceAsStream("/WEB-INF/CLASSES/db.properties");
        Properties properties = new Properties();
        properties.load(stream);
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        resp.getWriter().println(username+":"+password);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

由于maven约定大于配置,所以要在pom.xml配置

    <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>

 6.HttpServletResponse

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

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

一.方法分类 

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

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

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

响应的状态码

public static final int SC_CONTINUE = 100;

public static final int SC_OK = 200;
 
public static final int SC_MOVED_TEMPORARILY = 302;

public static final int SC_FOUND = 302;

public static final int SC_NOT_MODIFIED = 304;

public static final int SC_NOT_FOUND = 404;

public static final int SC_INTERNAL_SERVER_ERROR = 500;

public static final int SC_BAD_GATEWAY = 502;

二.应用 

(1).向浏览器输出信息

前面使用的就是。

(2). 下载文件

public class FileServlet extends HttpServlet {
 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     // 1.要获取下载文件的路径
     String realPath = "E:\\dev\\StudyProjects\\javaweb-servlet\\response\\src\\main\\resources\\大乔.jpg";
     // 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流写入到bufer缓冲区
     while ((len = in.read(buffer))>0){// 每次读取的长度大于0的情况下,就写出去
         out.write(buffer,0,len);// 写出字节,从0写到len
     }
     // 8.使用OutputStream将缓冲区中的数据输出到客户端!
     in.close();
     out.close();
 }
 @Override
 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     this.doGet(req, resp);
 }
}

(3).验证码功能 

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, 20, BufferedImage.TYPE_INT_RGB);// 宽、高、颜色
     // 得到图片
     Graphics2D g = (Graphics2D) image.getGraphics();// 得到一只2D的笔
     // 设置图片的背景颜色
     g.setColor(Color.white);
     g.fillRect(0, 0, 80, 20);// 填充颜色
     // 换个背景颜色
     g.setColor(Color.BLUE);
     // 设置字体样式:粗体,20
     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");
     // 把图片写给浏览器
     boolean write = ImageIO.write(image, "jpg",resp.getOutputStream());
 }
 // 生成随机数
 private String makeNum() {
     Random random = new Random();
     String num = random.nextInt(9999999) + "";// 随机数,最大七位,[0,9999999)
     StringBuffer sb = new StringBuffer();
     for (int i = 0; i < 7 - num.length(); i++) {// 不足七位,则添加0
         sb.append("0");
     }
     num = sb.toString()+num;// 不足七位,在随机数前面添加0
     return num;
 }
 @Override
 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     this.doGet(req, resp);
 }
}

(4).实现请求重定向 

public class RedirectServlet extends HttpServlet {
 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     /*resp.setHeader("Location","/response_war/image");
     resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);*/
     resp.sendRedirect("/response_war/image");// 重定向相当于上面两行代码
 }
 @Override
 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     this.doGet(req, resp);
 }
}

面试题:请你聊聊重定向和转发的区别?
相同点:页面都会实现跳转
不同点:

  • 请求转发的时候,url地址栏不会产生变化。307
  • 重定向时候,url地址栏会发生变化。302

7.HttpServletRequest 

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

一.获取前端传递的参数 

二.请求转发 

前端:

<%@ 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="hobbys" value="代码"> 代码
 <input type="checkbox" name="hobbys" value="唱歌"> 唱歌
 <input type="checkbox" name="hobbys" value="女孩"> 女孩
 <input type="checkbox" name="hobbys" value="电影"> 电影
 <br>
 <input type="submit" name="提交">
</form>
</body>
</html>

后端:

public class LoginServlet extends HttpServlet {
 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     this.doPost(req, resp);
 }
 @Override
 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     // 处理请求中文乱码(后期可以使用过滤器来解决)
     req.setCharacterEncoding("utf-8");
     resp.setCharacterEncoding("utf-8");
     String username = req.getParameter("username");
     String password = req.getParameter("password");
     String[] hobbys = req.getParameterValues("hobbys");
     System.out.println(username);
     System.out.println(password);
     System.out.println(Arrays.toString(hobbys));
     // 这里的 / 代表当前的web应用,所以不需要再加上/request_war这个上下文路径了,否则会出现404错误
     req.getRequestDispatcher("/success.jsp").forward(req,resp);
 }
}

web.xml 

七.cookie/session 

 1.会话

无状态的会话:用户打开一个浏览器,点击了很多超链接,访问多个web资源,关闭浏览器,这个过程可以称之为会话。
有状态的会话:一个用户打开一个浏览器,访问某些资源(网站),下次再来访问该资源(网站),我们会知道这个用户曾经来过,称之为有状态会话;

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

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

 2.保存会话的两种技术

cookie:

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

session:

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

3.Cookie 

(1).常用方法与使用

Cookie[] cookies = req.getCookies();// 获得cookie
cookie.getName();// 获得cookie中的key
cookie.getValue();// 获得cookie中的value
new Cookie("lastLoginTime",System.currentTimeMills()+"");// 新建一个cookie
cookie.setMaxAge(24*60*60);// 设置cookie的有效期,单位:秒
resp.addCookie(cookie);// 响应给客户端一个cookie
// 保存用户上一次访问的时间
public class CookieDemo01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 服务器告诉你,你来的时间,把这个时间封装成一个信息,你下次带来,我就知道你上次来的时间
        // 解决中文乱码
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");
        PrintWriter out = resp.getWriter();
        // Cookie,服务器端从客户端获取cookie
        Cookie[] cookies = req.getCookies();// 数组,说明cookie可以有多个
        // 判断cookie是否
        if (cookies != null) {
            out.write("你上一次登录的时间是:");
            for (int i = 0; i < cookies.length; i++) {
                // 获取cookie的名字
                if (cookies[i].getName().equals("lastLoginTime")) {
                    // 获取cookie的值
                    long l = Long.parseLong(cookies[i].getValue());
                    Date date = new Date(l);
                    out.write(date.toLocaleString());
                }
            }
        } else {
            out.write("你是第一次登录!");
        }
        Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis() + "");
        cookie.setMaxAge(24*60*60);// 设置cookie的有效期为一天,单位是:秒
        resp.addCookie(cookie);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

(2).删除cookie

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

 (3).编码解码,解决中文乱码问题

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

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

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

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

4.session(重点) 

(1).什么是Session

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

(2).session的常用方法与使用 

public class SessionDemo01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 解决中文乱码
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=utf-8");
        // 得到session
        HttpSession session = req.getSession();
        // 给session中存东西
        session.setAttribute("name", "张三");
        // 获取session的id
        String sessionId = session.getId();
        // 判断session是不是新创建
        if (session.isNew()) {
            resp.getWriter().write("session创建成功,ID:" + sessionId);
        } else {
            resp.getWriter().write("session已经存在了,ID:" + sessionId);
        }
        // session创建的时候做了什么事情
        /*Cookie cookie = new Cookie("JSESSIONID", sessionId);
        resp.addCookie(cookie);*/
        //------------------
        // 从session中获取数据
        String name = (String) session.getAttribute("name");
        //------------------
        // 从session中删除指定name的数据
        session.removeAttribute("name");
        // 手动注销session
        session.invalidate();
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}

 (3).会话自动过期

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

(4).使用场景 

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

(5).session和cookie的区别 

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

八.jsp 

1.什么是jsp 

Java Server Pages:Java服务端页面,和servlet一样,用于动态web技术
最大的特点:

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

2.jsp原理 

思路:jsp是怎样执行的?

  • 代码层面没有任何问题
  • 服务器内部工作
    • tomcat中有一个work目录;IDEA中使用tomcat的会在IDEA的tomcat中生产一个work目录
    • 目录地址C:\Users\26868\.IntelliJIdea2019.2\system\tomcat\Unnamed_web\work\
    • 发现页面会被转换成为一个Java类!
    • C:\Users\26868\.IntelliJIdea2019.2\system\tomcat\Unnamed_web\work\Catalina\localhost\webone\org\apache\jsp\src\main\web

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

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

JSP本质上就是一个Servlet

// 初始化
public void _jspInit() {
  }
// 销毁
  public void _jspDestroy() {
  }
// JSPservice
  public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
  1. 判断请求的方式
  2. 内置一些对象(9个)

    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;charset=UTF-8");// 设置响应的页面类型
    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页面中直接使用

 5.原理图

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

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

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

 3.JSP基础语法

导入依赖

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

1.jsp表达式

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

2.jsp脚本片段

<%--jsp脚本片段--%>
<%
 int sum = 0;
 for (int i = 0; i < 100; i++) {
   sum+=i;
 }
 out.print("<h1>sum="+sum+"</h1>");
%>
//--------------------------
<%--EL表达式:${变量} --%>
<%
 for (int i = 0; i < 3; i++) {
%>
<h1>Hello World! ${i}</h1>
<%
 }
%>

3.jsp声明

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

JSP声明:会被编译到jsp生成java的类中!
其他的,就会被生成到_jspService方法中!
在jsp,嵌入Java代码即可

4.jsp指令

<%@ page ... %>:声明页面属性,如内容类型、编码、缓冲等。
<%@ taglib prefix="..." uri="..." %>:导入外部标记库。
<%@ include file="..." %>:包含另一个 JSP 文件。

1.定制错误页面 

<%--定制错误页面--%>
<%@ page errorPage="error/500.jsp" %>

或者在web.xml定制全局的错误页面

2.包含头部和尾部 

5.九大内置对象 

  • PageContext 存东西
  • Request 存东西
  • Response
  • Session 存东西
  • Application(ServletContext) 存东西
  • Config(ServletConfig)
  • out
  • page
  • exception
​​​​​​<%@ page import="java.util.Date" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>首页</title>
</head>
<body>
<%   // 一.内置对象存储数据
    // 保存的数据只在一个页面中有效
    pageContext.setAttribute("one","1号");
    // 保存的数据只在一次请求中有效,请求转发会携带这个数据
    // 客户端向服务器发送请求,产生的数据,可能过会就用不上了,例:某个文章!
    request.setAttribute("two","2号");
    // 保存的数据只在一次会话中有效,从打开浏览器到关闭浏览器
    // 客户端向服务器发送请求,产生的数据,可能用完了还要用,例:用户信息;
    session.setAttribute("three","3号");
    // 保存的数据只在服务器中有效,从打开服务器到关闭服务器
    // 客户端向服务器发送请求,产生的数据,一个用户用完了,其他用户还可能使用。例:全局参数。
    application.setAttribute("four","4号");

    // 二.取数据,通过寻找的方式来取
    String one = (String)pageContext.findAttribute("one");
    String two = (String)pageContext.findAttribute("two");
    String three = (String)pageContext.findAttribute("three");
    String four = (String)pageContext.findAttribute("four");
    String five = (String)pageContext.findAttribute("five");// 加入一个不存在的数据看看会怎么样
%>

<%= one %>
<%= two %>
<%= three %>
<%= four %>
<%= five %>
</body>
</html>

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>

 1.JSP标签:JSP自带比较简单的标签。

<jsp:useBean ... %>:创建或查找 Bean 对象。
<jsp:setProperty ... %>:设置 Bean 对象的属性。
<jsp:getProperty ... %>:获取 Bean 对象的属性。
<jsp:forward ... %>:将请求转发到另一个 JSP 页面。
<jsp:include ... %>:将另一个 JSP 页面内容包含到当前页面。
<%-- 下面的表示转跳页面并设置信息http://localhost:8080/a.jsp?name=frank&age=19 --%>
<jsp:forward page="index.jsp">
    <jsp:param name="name" value="frank"/>
    <jsp:param name="age" value="19"/>
</jsp:forward>

<%-- 再index.jsp页面取参数 --%>
<%= request.getParameter("name")%>
<%= request.getParameter("age")%>

2.EL表达式: ${ }

  • 获取数据
  • 执行运算
  • 获取web开发的常用对象

3.JSTL表达式 

 JSTL支持通用的、结构化的任务,比如迭代,条件判断,XML文档操作,国际化标签,SQL标签

教程: JSP 标准标签库(JSTL) | 菜鸟教程 (runoob.com)

  • c:if 
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<html>
<head>
    <title>首页</title>
</head>
<body>

<form action="index.jsp" method="get">
    <%-- EL表达式获取表单中的数据 ${param.参数名} --%>
    <input type="text" name="username" value="${param.username}">
    <input type="submit" value="登录">
</form>
<%--判断如果提交的用户名是管理员,则登录成功,var为ture--%>
<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,值为85--%>
<c:set var="score" value="55"/>
<c:choose>
    <c:when test="${score>=90}">
    你的成绩为优秀
    </c:when>
    <c:when test="${score>=80}">
    你的成绩为一般
    </c:when>
    <c:when test="${score>=70}">
    你的成绩为良好
    </c:when>
    <c:when test="${score<=60}">
    你的成绩为不及格
    </c:when>
</c:choose>
</body>
  • c:forEach
    <%
        List<String> nameLists = new ArrayList<>();
        nameLists.add("张三");
        nameLists.add("李四");
        nameLists.add("王五");
        nameLists.add("赵六");
        nameLists.add("田六");
        request.setAttribute("list",nameLists);
    %>
    <%--
    var , 每一次遍历出来的变量
    items, 要遍历的对象
    begin, 哪里开始
    end, 到哪里
    step, 步长
    --%>
    <c:forEach var="name" items="${list}">
        <c:out value="${name}"/> <br>
    </c:forEach>
    <hr>
    <c:forEach var="name" items="${list}" begin="1" end="3" step="1" >
        <c:out value="${name}"/> <br>
    </c:forEach>

 九.javaBean

1.JavaBean有特定的写法:

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

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

2.ORM :对象关系映射

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

3.user表 

idnameaddresssex
1张三北京
2李四广东
3王五上海
class User{
    private int id;
    private String name;
    private String address;
    private String sex;
}
List<User> userList = new ArrayList<>();
userList.add(new User(1,"张三","北京","男"));
userList.add(new User(1,"李四","广东","男"));
userList.add(new User(1,"王五","上海","男"));

十.mvc三层架构 

1.早些年没有mvc的时候 

用户直接访问控制层,控制层直接操作数据库

弊端:程序十分臃肿,不利于维护。(servlet代码中处理请求,响应,视图转跳,处理业务代码,处理逻辑代码,处理JDBC。

解决:架构(没有什么是加一层解决不了的) 

2.什么是MVC 

Model(模型),view(视图),Controller(控制器)

十一.过滤器(Filter) 

1.用处 

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

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

2.Filter开发步骤: 

(1).导包

  <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>

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

        <dependency>
            <groupId>javax.servlet.jsp.jstl</groupId>
            <artifactId>jstl-api</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
    </dependencies>

(2).编写过滤器(自定义类实现javax.servlet.Filter接口)

public class CharaterEncodingFilter implements Filter {

 // 初始化:服务器启动的时候,就已经初始化了,随时等待过滤对象出现!
 @Override
 public void init(FilterConfig filterConfig) throws ServletException {
     System.out.println("CharaterEncodingFilter初始化");
 }

// chain:链
 @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("CharaterEncodingFilter执行前");
     // 让我们的请求继续走,如果不写,程序到这里就被拦截停止
     chain.doFilter(request,response);
     System.out.println("CharaterEncodingFilter执行后");
 }

// 销毁:web服务器关闭的时候,过滤会被销毁
 @Override
 public void destroy() {
     System.out.println("CharaterEncodingFilter销毁");
 }
}

 (3).在web.xml中配置Filter

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

3.应用:模拟用户登入 

jsp

<body>

<h1>登入页面</h1>
<%--action: 提交的到处理登入的UserName类的映射路径下--%>
<form action="/username" method="post">
用户名:<input type="text" name="username">
提交:<input type="submit">
</form>

</body>
<body>

<h1>主页</h1>
<%--提交 提交到处理注销的Delete类映射路径下  --%>
<a href="/delete"><samp>注销</samp></a>

</body>
<body>

<h1>用户错误</h1>
<a href="index.jsp"><span>转跳到登入页面</span></a>

</body>

java

// 处理用户登入
public class UserName extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 取出用户名
        String name = req.getParameter("username");

        // 判断用户名
        if (name.equals("123")) {// 用户名正确
            // 生成用户id
            req.getSession().setAttribute(Const.USER_SESSION,req.getSession().getId());
            // 转跳成功页面
            resp.sendRedirect("/sys/yes.jsp");
        } else { // 用户名错误
            // 转跳错误页面
            resp.sendRedirect("/no.jsp");
        }

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      doGet(req, resp);
    }
}
// 实现注销
public class Delete extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 取出用户id
        String id = (String) req.getSession().getAttribute(Const.USER_SESSION);

        // 判断是否有id
        if (id != null) {
            // 注销
            req.getSession().removeAttribute(Const.USER_SESSION);
            // 转跳登入页面
            resp.sendRedirect("/index.jsp");
        } else {
            // id为空,直接返回登入页面
            resp.sendRedirect("/index.jsp");
            // 有一个问题,已经为null为什么还能加入欢迎页面,点击注销。
            // 如何解决,我们可以使用过滤器,处理为null的情况
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
// 处理注销时为null的问题
public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        // 如何拿到id,先向下转型,再拿id
        HttpServletRequest req = (HttpServletRequest)servletRequest;
        HttpServletResponse resp = (HttpServletResponse)servletResponse;
        String id = (String) req.getSession().getAttribute(Const.USER_SESSION);

        // 如果id为null,直接转跳到错误页面
        if (id == null) {
            resp.sendRedirect("/no.jsp");
        }
        filterChain.doFilter(req,resp);


    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException { }

    @Override
    public void destroy() { }
}

 XML

  <filter>
    <filter-name>myfilter</filter-name>
    <filter-class>MyFilter</filter-class>
  </filter>
<filter-mapping>
  <filter-name>myfilter</filter-name>
  <url-pattern>/sys/*</url-pattern>
</filter-mapping>

<servlet>
  <servlet-name>username</servlet-name>
  <servlet-class>UserName</servlet-class>
</servlet>
  <servlet>
  <servlet-name>delete</servlet-name>
  <servlet-class>Delete</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>username</servlet-name>
  <url-pattern>/username</url-pattern>
</servlet-mapping>
 <servlet-mapping>
   <servlet-name>delete</servlet-name>
   <url-pattern>/delete</url-pattern>
 </servlet-mapping>






注意要给主页页面单独开一个文件夹,不然会报错 localhost 重定向次数过多

localhost 将您重定向的次数过多问题解决。_重定向次数过多清除也没用-优快云博客

十二.监听器 

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

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

// 统计在线人数
public class ListenerTest implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        ServletContext context = se.getSession().getServletContext();

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

        if (onlineCount==null){
            onlineCount = new Integer(1);
        }else {
            int count = onlineCount.intValue();
            onlineCount = new Integer(count+1);
        }
        context.setAttribute("OnlineCount",onlineCount);

    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {

        ServletContext context = se.getSession().getServletContext();

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

        if (onlineCount==null){
            onlineCount = new Integer(0);
        }else {
            int count = onlineCount.intValue();
            onlineCount = new Integer(count-1);
        }
        context.setAttribute("OnlineCount",onlineCount);
    }
}
jsp

<h1>当前<span><%= getServletConfig().getServletContext().getAttribute("OnlineCount")%></span>人在线</h1>
XML
 <listener>
     <listener-class>com.sunyiwenlong.listener.OnlineCountListener</listener-class>
 </listener>

十三.JDBC复习 

1.基础复习 

基础知识:Java进阶十—JDBC-优快云博客 

MySQL入门-优快云博客

2.Idea连接数据库 

Intellij IDEA——配置MySQL的驱动程序_idea配置mysql驱动-优快云博客

超详细的Idea与MySQL的连接(从入门到精通)_idea连接数据库-优快云博客

3.事务 



import java.sql.*;

public class UDBCTest  {

    // 配置
    public static final String URL = "jdbc:mysql://localhost:3306/student";
    public static final String USER = "root";
    public static final String PASSWORD = "123456";
    public static final String DRIVER = "com.mysql.cj.jdbc.Driver";

    public static Connection connection;


    public static void main(String[] args) {

        try {
            // 1.加载驱动
            Class.forName(DRIVER);

            // 2.连接数据库
            connection = DriverManager.getConnection(URL,USER,PASSWORD);

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

            // 4.执行sql语句
            String sqlOne = " update student set age=33-10 where id=1";
            connection.prepareStatement(sqlOne).executeUpdate();

            String sqlTwo = "update student set age=56+10 where id=2";
            connection.prepareStatement(sqlTwo).executeUpdate();

            // 4.提交事务
            connection.commit();
        } catch (ClassNotFoundException e) {
            // 5.如果遇到异常,那就回滚事务
            try {
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                // 6.关闭
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }


    }

}

十四.文件传输原理及实现 

1.基础配置 

  • 修改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>
    

  • 配置Tomcat,运行空项目。

  • 导入架包:文件在网络上都是使用IO的方式,即流的方式进行的传输,要实现的文件上传功能可以直接使用apache的组件commons-fileupload(针对文件上传的工具类包),这个jar包又依赖commons-io包(封装了大量的IO操作的工具类),所以在实现文件上传功能的时候需要导入如下两个依赖:
    ​
        <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
        <dependency>
          <groupId>commons-io</groupId>
          <artifactId>commons-io</artifactId>
          <version>2.11.0</version>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
        <dependency>
          <groupId>commons-fileupload</groupId>
          <artifactId>commons-fileupload</artifactId>
          <version>1.5</version>
        </dependency>
    
        <!--servlet依赖-->
        <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
          <version>3.1.0</version>
        </dependency>
    ​

2.整体框架

 

3.编写前端与支持 

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

<head>
    <title>文件上传</title>
</head>

<body>
<%--
    通过表单上传文件
    get:上传文件有大小限制
    post:上传文件无大小限制
    enctype="multipart/form-data"支持上传文件格式
--%>
<form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post">
    <p>上传用户<input type="text" name="username"></p>
    <p>上传文件1<input type="file" name="file1"></p>
    <p>上传文件2<input type="file" name="file2"></p>
    <p><input type="submit"> | <input type="reset"></p>
</form>

</body>

</html>

<%--
  Created by IntelliJ IDEA.
  User: 26868
  Date: 2024/3/16
  Time: 16:24
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
${msg}
</body>
</html>

 4.后端编写

先配置web.xml

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

  编写FileSerlvet类

   // ServletFileUpload类
    // 一.构造器
    // ServletFileUpload() 创建一个 ServletFileUpload 实例,使用默认设置。
    // ServletFileUpload(factory) 创建一个上传工具,指定使用缓存区与临时文件存储位置.
    // 二.常用方法
    // 1.static final boolean isMultipartContent(HttpServletRequest request) 检查请求是否            是multipart/form-data类型的请求。
    // 2.void setProgressListener(ProgressListener pListener) 监听进度
    //   会重新ProgressListener接口,update方法的三个参数为已读字节数,总内容长度(以字节为单位),已处理的项目数。
    // 3.void setHeaderEncoding(String encoding) 设置字符编码
    // 4.void setFileSizeMax(long fileSizeMax) 设置文件读取大小、
    // 5.List<FileItem> parseRequest(HttpServletRequest request) 解析请求并提取文件项

    // DiskFileItemFactory类
    // 一.构造器
    // DiskFileItemFactory() - 创建一个新的 DiskFileItemFactory 实例,使用默认设置。
    // 二.常用方法
    // 1.void setSizeThreshold(int sizeThreshold) 设置缓冲区大小
    // 2.void setRepository(File repository) 设置用于存储临时文件的目录


    // UUID类通用唯一识别码
    // 一.构造器(不常用)
    // 二.常用方法
    // 1.static UUID randomUUID() 生成一个新的随机UUID
    // 2.tring toString() 将UUID转换为字符串表示



    // FileItem类
    // 一.构造器(不常用)
    // 二.常用方法
    // 1..boolean isFormField() 判断上传的文件是普通的表单还是带文件的表单
    // 2.InputStream getInputStream() 获取输入流
    // 2.String getFieldName() 获取前端普通表单name参数
    // 3.String getString(String var1) 获取前端普通表单的值,var1设置编码格式
    // 4.void getName(): 返回文件的文件名。
    // 5.void getSize(): 返回文件的大小。
    // 6.void delete(): 删除文件。



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

    // getServletContext方法
    // 1.String getRealPath(String var1) 将给定的虚拟路径转换为服务器文件系统中的绝对路径
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;

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

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


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

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

        // 1.判断上传的文件普通表单还是带文件的表单
        if (!ServletFileUpload.isMultipartContent(request)) {
            return;//终止方法运行,说明这是一个普通的表单,直接返回
        }

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

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



        // 处理上传的文件,一般都需要通过流来获取,我们可以使用 request, getInputstream(),原生态的文件上传流获取,十分麻烦
        // 但是我们都建议使用 Apache的文件上传组件来实现, common-fileupload,它需要旅 commons-io组件;
        // 这里我们把它们提炼成一个一个方法,然后调用

        try {
            // 4.调用下面写好的getDiskFileItemFactory方法,来判断文件的大小与限制
            DiskFileItemFactory factory = getDiskFileItemFactory(file);


            // 5.调用下面写好的getServletFileUpload方法,监听文件读取进度与文件上传的总大小
            ServletFileUpload upload = getServletFileUpload(factory);

            // 6.调用下面写好的uploadParseRequest方法,处理上传文件
            // 把前端请求解析,封装成FileItem对象,需要从ServletFileUpload对象中获取
            String msg = uploadParseRequest(upload, request, uploadPath);

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

        } catch (FileUploadException e) {
            e.printStackTrace();
        }
    }

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

    // 5.监听文件读取进度与文件上传的总大小
    public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
        // 创建一个上传工具,指定使用缓存区与临时文件存储位置
        ServletFileUpload upload = new ServletFileUpload(factory);
        // 监听长传进度
        upload.setProgressListener(new ProgressListener() {
            // 三个参数为已读字节数,总内容长度(以字节为单位),已处理的项目数
            public void update(long l, long l1, int i) {

                System.out.println("以读取"+((l/l1)*100)+"%");
            }
        });

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

    // 6.处理上传文件
    public static String uploadParseRequest(ServletFileUpload upload, HttpServletRequest request, String uploadPath) throws FileUploadException, IOException {

        String msg = "";

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

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

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

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

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

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

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

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


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

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

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

    }
}

十五.邮件发送原理及实现 

1.原理 

 

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

 2.引入架包

Maven Repository: javax.mail » mail » 1.4.7 (mvnrepository.com)

Maven Repository: javax.activation » activation » 1.1.1 (mvnrepository.com)

 

3.简单邮件:纯文本

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

package com.github.test;

import com.sun.mail.util.MailSSLSocketFactory;

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

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

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

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

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

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

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

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

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

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

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

4.复杂邮件:有附件(图片,视频等等)

 

import com.sun.mail.util.MailSSLSocketFactory;

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

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

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

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

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

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

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

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

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

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

        // 准备邮件数据

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

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

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

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

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

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

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

发送包含附件的复杂邮件 

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

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

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

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

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

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

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

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

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

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

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

 (1).前端编写

<%@ page contentType="text/html;charset=UTF-8" %>
<%--注册填写邮箱的前端页面--%>
<html>
<head>
  <title>注册</title>
</head>
<body>

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

</body>
</html>

(2).后端编写 

  • 编写实体类编写User.java,下面是封装架包

Maven Repository: org.projectlombok » lombok » 1.18.24 (mvnrepository.com)

package com.pojo;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private String username;
    private String password;
    private String email;

}
  • 编写工具类Sendmail.java
    package com.util;
    
    /**
     * 多线程实现邮件发送
     * 使用多线程的原因就是提高用户的体验,一旦一个页面3s及以上的时间白屏就可能被用户关掉
     * 所以我们在用户提交表单之后,将费时的邮件发送工作使用一个子线程来完成,而主线程还是去完成它自己的事情
     * 这么做就可以提高用户体验,不然用户等待邮件发送的时间
     */
    
    import com.github.pojo.User;
    import com.sun.mail.util.MailSSLSocketFactory;
    
    import javax.mail.*;
    import javax.mail.internet.InternetAddress;
    import javax.mail.internet.MimeMessage;
    import java.util.Properties;
    
    /**
     * @author subei
     * 多线程这种处理就可以称为异步处理
     */
    public class Sendmail extends Thread{
    
        // 用于向客户发送邮件的邮箱
        private String from = "XXXX@qq.com";
        // 用于登陆SMTP服务器的用户名
        private String username = "XXXX@qq.com";
        // 授权码
        private String password = "授权码";
        // 发送邮件的地址
        private String host = "smtp.qq.com";
    
        private User user;
        public Sendmail(User user) {
            this.user = user;
        }
    
        @Override
        public void run() {
            try {
                Properties prop = new Properties();
                // 设置QQ邮件服务器
                prop.setProperty("mail.host", host);
                // 邮件发送协议
                prop.setProperty("mail.transport.protocol", "smtp");
                // 需要验证用户名密码
                prop.setProperty("mail.smtp.auth", "true");
    
                // 关于QQ邮箱,还要设置SSL加密,加上以下代码即可
                MailSSLSocketFactory sf = new MailSSLSocketFactory();
                sf.setTrustAllHosts(true);
                prop.put("mail.smtp.ssl.enable", "true");
                prop.put("mail.smtp.ssl.socketFactory", sf);
    
                // 1.创建定义整个应用程序所需的环境信息的 Session 对象
                // 使用QQ邮箱的时候才需要,其他邮箱不需要这一段代码
                // 获取和SMTP服务器的连接对象
                Session session = Session.getDefaultInstance(prop, new Authenticator() {
                    @Override
                    public PasswordAuthentication getPasswordAuthentication() {
                        // 发件人邮件用户名、授权码
                        return new PasswordAuthentication("XXXX@qq.com", "授权码");
                    }
                });
    
                // 开启Session的debug模式,这样就可以查看到程序发送Email的运行状态
                session.setDebug(true);
    
                // 2.通过session得到transport对象
                Transport ts = session.getTransport();
    
                // 3.使用邮箱的用户名和授权码连上SMTP邮件服务器,即登陆
                ts.connect(host, username, password);
    
                //4、创建邮件对象MimeMessage——点击网页上的写信
                MimeMessage message = new MimeMessage(session);
                message.setFrom(new InternetAddress(username));
                message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
                message.setSubject("用户注册邮件!");
                message.setContent("<p><h2>恭喜注册成功!</h2></p>您的用户名为: <h4>"+user.getUsername()+
                        "</h4><p>您的密码:" + user.getPassword() +
                        "</p><p>请妥善保管您的密码,如有问题请及时联系网站客服,再次欢迎您的加入!!</p>", "text/html;charset=UTF-8");
    
                // 5.发送邮件——在网页上点击发送按钮
                ts.sendMessage(message, message.getAllRecipients());
    
                // 6.关闭连接对象,即关闭服务器上的连接资源
                ts.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
  • 编写Servlet.java
    package com.servlet;
    
    import com.github.pojo.User;
    import com.github.util.Sendmail;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    public class RegisterServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 1.接收用户填写的表单数据
            String username = req.getParameter("username");
            String password = req.getParameter("password");
            String email = req.getParameter("email");
            System.out.println(username+password+email);
    
            // 2.向用户邮箱发送邮件,注意发送邮件很耗时,所以我们启动一个子线程去做这件事,而用户则是直接跳转注册成功页面,以免降低用户体验
            User user = new User(username,password,email);
            // 获取子线程对象
            Sendmail sendmail = new Sendmail(user);
            // 启动子线程
            new Thread(sendmail).start();
    
            // 3.视图跳转
            req.setAttribute("message","注册成功!我们已经向您的邮箱发送了邮件,请您及时进行查收。由于网络原因,您收到邮件的时间存在延迟,敬请谅解~");
            req.getRequestDispatcher("info.jsp").forward(req,resp);
    
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req, resp);
        }
    }
    
  • web.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        
        <servlet>
            <servlet-name>RegisterServlet</servlet-name>
            <servlet-class>com.github.servlet.RegisterServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>RegisterServlet</servlet-name>
            <url-pattern>/RegisterServlet.do</url-pattern>
        </servlet-mapping>
    
    </web-app>
    

  • 运行测试,遇到如下报错。
  • 在这里插入图片描述

  • 检查Artifacts;
  • 在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

  • 再次运行,还是报错500;
    • 将activation-1.1.jar、mail-1.4.7.jar导入到Tomcat中的lib文件夹中。
  • 在这里插入图片描述

  • 再次运行。
  • 在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值