JavaWeb:Servlet介绍、两个init方法、两个service方法、Mapping映射路径、ServletContext接口、Cookie、Session、MVC、文件传输、邮件发送


Java Web 02

servlet,cookie,session,filter,MVC,JavaBean


一、Servlet基本流程


1,创建一个类,继承HttpServlet

public class HelloServlet extends HttpServlet {
    // 重写了doGet方法,该方法处理get请求
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException{
        // 响应的类型
        response.setContentType("text/html");
        response.setCharacterEncoding("utf-8");
        // 响应的输出流
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Hello World!</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>你好!</h1>");
        out.println("</body>");
        out.println("</html>");
    }
}

2,在web.xml,配置文件中注册servlet

<?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_3_1.xsd"
         version="3.1"
         metadata-complete="true">

    <!--web.xml中是配置web的核心应用-->
    <!--注册servlet-->
    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.heroc.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--一个servlet,对应一个mapping(映射)-->
    <servlet-mapping>
        <!--该映射名称要与servlet对应-->
        <servlet-name>helloServlet</servlet-name>
        <!--请求路径-->
        <url-pattern>/h</url-pattern>
    </servlet-mapping>

</web-app>

一个servlet对应一个servlet映射。

部署好tomcat之后,启动tomcat就可以通过localhost:8080/h访问到HelloServlet类中get请求的处理。

流程:

前端浏览器,输入请求地址,然后定位到/h,找到映射配置,<servlet-name>helloServlet</servlet-name>找到,注册的servlet,就可以找到class,找到class,就可以在该calss中找到对应的请求方法。

为什么需要映射:

我们写的是java程序,浏览器只能访问web服务器,为了与java程序建立通信,就得在web服务器上,注册servlet,还需要给一个浏览器能够访问的路径。因此映射能够在服务器中建立起浏览器与java程序的连接。


二、Servlet

在这里插入图片描述

1、Servlet 接口


1)Servlet简介

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

Sun公司提供了Servlet接口,如果要开发一个servlet程序,步骤:

  • 编写一个类,实现servlet接口
  • 把开发好的程序部署到web服务器中

servlet是运行在web服务器中的小型java程序,通常通过HTTP协议接受和相应来自web客户端的请求。

Sun公司默认为Servlet写了两个实现类,分别是GenericServletHttpServlet

在这里插入图片描述

2)Servlet原理

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

在这里插入图片描述

Servlet容器调用Servlet的过程。调用过程如下 首次访问该Servlet

1、调用init(ServletConfig config) 进行初始化,ServletConfig封装了web.xml中<init-param>配置的初始化参数,它由Servlet容器创建,并通过该方法传递给当前serlvet

2、调用service(ServletRequest req, ServletResponse res)方法处理客户的请求

3、调用destroy()方法销毁给Servlet实例,当然这里只是为了说明完整流程,实际上destroy()方法不是在调用servlet完成后就销毁。

抽象方法
  • public void init(ServletConfig config) throws ServletException;

    该抽象方法的作用是:服务器启动时,就会初始化servlet。全程只会初始化一次

  • public ServletConfig getServletConfig();

    该抽象方法的作用是:该对象包含此 servlet 的初始化和启动参数

  • public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;

    该抽象方法的作用是:由 servlet 容器调用,以允许 servlet 响应某个请求。

  • public String getServletInfo();

    该抽象方法的作用是:返回有关 servlet 的信息,比如作者、版本和版权

  • public void destroy();

    该抽象方法的作用是:销毁servlet,最后进行垃圾回收终止它。


2、GenericServlet 抽象类

GenericServlet抽象类,实现了ServletServletCofig接口

两个init()方法重点
// 1,
public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}

// 2,
public void init() throws ServletException {

}

方法1,调用了方法2,但是方法2什么也没有做。事实上,GenericServlet类可以只写一个init方法,如果要对init方法进行特殊操作的话,就得继承GenericServlet重写init方法,往往在开发中,很容易将最关键的一句话this.config = config;给漏掉,如果漏写,ServletConfig对象就是null,就无法使用ServletConfig对象。因此,为了避免该事情发生,保险起见,就写了两个init方法,一个固定的init方法,去初始化ServletConfig对象,然后调用另一个无参的init方法,我们只需要重写无参的init方法,对初始进行特殊操作,就可以了。

3、HttpServlet 抽象类

HttpServlet抽象类,继承了GenericServlet类


重写方法,不能使用super

继承HttpServlet重写方法时,不能加uper.doXXX(req, resp);不然会报错,无法访问,返回4xx代码

比如doGet(req,resp)方法,如果不删除uper.doGet(req, resp); 访问该url,会调用HttpServlet的doGet方法,而这个doGet方法会返回405错误码 “http.method_get_not_supported” ,源码如下:

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
{
    String protocol = req.getProtocol();
    String msg = lStrings.getString("http.method_get_not_supported");
    if (protocol.endsWith("1.1")) {
        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
    } else {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
    }
}

两个service()方法重点
// 1,
@Override
public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException
{
    HttpServletRequest  request;
    HttpServletResponse response;

    if (!(req instanceof HttpServletRequest &&
          res instanceof HttpServletResponse)) {
        throw new ServletException("non-HTTP request or response"); // 抛出异常
    }

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

    service(request, response);
}

该公共service方法由服务器自动调用,会将前端通过http协议,传递过来的请求,并判断是否是符合http协议,不是则抛出异常,是则进行强制性类型转换,调用本方法的另一个受保护的service方法。

其实该方法,有一个好处,如果出现了其他协议,也可以通过该方法,进行扩展。

因为,这里只是一个类型转换的一个方法,是服务器自动调用的。比如是一个http协议的请求和响应,那么就可以强制转换成HttpServletRequest和HttpServletResponse对象,然后调用对应的重载revice方法。如果出现了另一种协议,其实这里也可以将其转换成符合另一种协议的对象,然后再重载revice方法,进行调用即可。如果后期出现了另一种传输协议,java后期更易于去扩展该协议。

// 2,
protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
{
    String method = req.getMethod();
    if (method.equals(METHOD_GET)) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
            doGet(req, resp);
        } else {
            long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            if (ifModifiedSince < lastModified) {
                maybeSetLastModified(resp, lastModified);
                doGet(req, resp);
            } else {
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }
    } else if (method.equals(METHOD_HEAD)) {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);
    } else if (method.equals(METHOD_POST)) {
        doPost(req, resp);
    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);
    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);
    } else if (method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);
    } else if (method.equals(METHOD_TRACE)) {
        doTrace(req,resp);
    } else {
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}

该受保护的service方法,通过获取请求的请求方式,来调用对应的执行方法,如果是get请求,那么就调用doGet()方法。所以我们完全可以重写受保护的 service() 方法来代替 doPost() 和 doGet() 方法。


4、Mapping 映射路径


1)servlet与mapping映射路径的多个情况
  • 一个servlet有一个映射路径

    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.heroc.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    

    浏览器可通过/hello找到HelloServlet类的doget处理方法

  • 一个servlet有多个映射路径

    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.heroc.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello1</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello2</url-pattern>
    </servlet-mapping>
    

    浏览器可通过/hello/hello1/hello2多个映射路径去访问一个servlet

  • 一个servlet指定一个通用映射路径

    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.heroc.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello/*</url-pattern>
    </servlet-mapping>
    

    浏览器可通过/hello/任意字符都可以访问到HelloServlet处理后的结果

  • 默认请求路径

    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.heroc.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    

    如果直接映射路径使用/*访问localhost:8080/不会访问index.html默认的主页面,而是访问/*对应注册的servlet内容。由此见,映射路径/*优先级较高

  • 指定后缀

    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.heroc.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    

    浏览器访问的路径只有是.do结尾的才可以访问到对应注册的servlet。注意,指定后缀*符号前面不能加/,否则会报错。


2)映射路径的优先级
<!--指定映射路径-->
<servlet>
    <servlet-name>helloServlet</servlet-name>
    <servlet-class>com.heroc.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>helloServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

<!--通配符映射路径-->
<servlet>
    <servlet-name>error</servlet-name>
    <servlet-class>com.heroc.servlet.Error</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>error</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

以上例子,虽然/hello是属于/*的范围中,但是通过浏览器访问/hello,会跳转到/hello注册的servlet,这里可以看出,固有的路径(指定的路径)优先级大于通配符的路径


5、ServletContext 接口对象

javax.servlet Interface ServletContext 接口,就是servlet的容器,是web应用程序的上下文。SerletContext是全局唯一的,初始项目的时候就创建了该对象。

在这里插入图片描述

因此ServletContext接口可以实现多个Servlet之间的数据交流。定义一组方法,servlet 使用这些方法与其 servlet 容器进行通信。


常用方法
  • public Object getAttribute(String name);

    返回具有给定名称的 servlet 容器属性,如果不具有该名称的属性,则返回 null

  • public void setAttribute(String name, Object object);

    在servlet上下文中,给定一个键值对。如果servlet上下文中,已经存在某个属性,那么此方法将使用最新的属性值。

  • public void removeAttribute(String name);

    将servlet上下文中的,某个属性删除。

  • public String getInitParameter(String name);

    获得web.xml中配置的参数信息

  • public RequestDispatcher getRequestDispatcher(String path);

    请求转发(浏览器的路径不会发生变化)。可以通过该方法,转发到其他servlet中。

  • public InputStream getResourceAsStream(String path);

    读取资源文件,返回流对象

以下ServletContext的操作基本上不会使用,功能都会被其他类代替。

数据共享

public class HelloServlet01 extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException{
        ServletContext context = this.getServletContext();
        context.setAttribute("username","heroC"); // 向上下文中放入了一个节点
    }
}


public class HelloServlet02 extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException{
        ServletContext context = this.getServletContext(); // 获得全局唯一ServletContext对象
        String name = (String)context.getAttribute("username"); // 从中获取指定节点
        response.getWriter().write(name); // 向响应目的输出节点中的值
    }
}

// 以上两个servlet就通过ServletContext对象实现了数据交互

获取参数

<!--web.xml-->
<context-param>
    <param-name>urlname</param-name>
    <param-value>jdbc:mysql://localhost:8080/db1</param-value>
</context-param>
<!--配置了一个名为urlname的参数信息-->
public class HelloServlet01 extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException{
        ServletContext context = this.getServletContext();
        String urlname = context.getInitParameter("urlname"); // 获取web.xml中的参数配置信息
    }
}

请求转发

public class HelloServlet01 extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException{
        ServletContext context = this.getServletContext();
        context.getRequestDispatcher("/hello").forward(request, response); 
        // 通过浏览器访问到该servlet,该servlet处理中,会转发到“/hello”请求路径的servlet进行处理,将request, response都传递过去进行处理
    }
}

读取资源文件

public class HelloServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException{
        response.setCharacterEncoding("utf-8");
        ServletContext context = this.getServletContext();
        // 获取配置文件并将内容输入流
        InputStream resource = 
                        context.getResourceAsStream("/WEB-INF/classes/test.properties");
        Properties prop = new Properties();
        prop.load(resource); // 加载到properties集合类中
        String username = prop.getProperty("username");
        String password = prop.getProperty("password");

        response.getWriter().print(username+":"+password);
    }
}
<?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_3_1.xsd"
         version="3.1"
         metadata-complete="true">

    <!--web.xml中是配置web的核心应用-->
    <!--注册servlet-->
    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.heroc.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

</web-app>


请求转发与重定向的区别 重点

resource为servlet中的任意操作

转发请求:

在这里插入图片描述

A需要访问resource,但是只能通过B去得到resource,B得到了resource,再将resource返回给A。转发请求的请求路径不会发生变化。

重定向:

在这里插入图片描述

A需要访问resource,但resource是B中的资源,A通过访问B,B告诉了A怎么去访问resource,A直接去访问resource。通过重定向,请求路径会发生变化。

相同点:

  • 页面都会实现跳转

不同点:

  • 转发不会改变请求的URL (ServletContext和HttpServletRequest都可以实现转发) ;状态码 307
  • 重定向会改变请求的URL ; 状态码 302
  • 转发可以给HttpServletRequest设置一个节点,可以传递参数;而重定向只能转发页面,不能传递参数

6、HttpServletRequest 接口

在这里插入图片描述

在这里插入图片描述

获取前端的参数
String userNname = request.getParameter("username");
String[] likes = request.getParameterValues("like");

// 第一个获取请求URL中username的值
// 第二个获取like中的多个值,可能like代表的是一个多选框,返回多个值

请求转发

除了ServletContext可以实现转发,HttpServletRequest也可实现转发

request.getRequestDispatcher("/code").forward(request,response);

7、HttpServletResponse 接口

在这里插入图片描述

在HttpServletResponse接口中定义了很多状态码。以及对请求做出响应的一些get/set方法,以及给浏览器发送信息的一些方法。

在这里插入图片描述

浏览器下载文件
resp.setHeader("Content-Disposition",
               "attachment;filename="+URLEncoder.encode(fileName,"UTF-8"));
// 重点,给浏览器一个头信息,让浏览器执行下载文件

Download.java

public class Download extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 当我们重写doGet方法时,不能加入这句话,因为父类doGet方法会返回405错误码
        // super.doGet(req, resp);

        //ServletContext context = this.getServletContext();
        // String realPath = context.getRealPath("/1000x.mp3"); // 获取不到资源
        // System.out.println(realPath);
        String realPath = "E:\\idea-workspace\\javaweb\\src\\main\\resources\\1000x.mp3";
        String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
        
        // 让浏览器下载文件的头信息 
        // Disposition处置 attachment附件
        resp.setHeader("Content-Disposition",
                       "attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));
        InputStream in = new FileInputStream(realPath);

        ServletOutputStream out = resp.getOutputStream();
        int len = 0;
        byte[] buffer = new byte[1024];
        while ((len=in.read(buffer)) != -1){
            out.write(buffer,0,len); // 向客户端写文件
        }

        in.close();
        out.close();
    }
}

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_3_1.xsd"
         version="3.1"
         metadata-complete="true">

    <servlet>
        <servlet-name>download</servlet-name>
        <servlet-class>com.heroc.download.Download</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>download</servlet-name>
        <url-pattern>/downMusic</url-pattern>
    </servlet-mapping>

</web-app>

验证码实现

验证码实现方式:

  • 前端实现
  • 后端实现,需要用到图片类,生产一个图片
public class SafeCode extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置浏览器刷新时间
        resp.setHeader("refresh","5");

        // 通过图片类,创建一个长为80,宽为20,使用BGR颜色格式
        BufferedImage image = new BufferedImage(80, 20, BufferedImage.TYPE_3BYTE_BGR);
        // 通过图片类,获取一个画笔
        Graphics pen = image.getGraphics();
        pen.setColor(Color.white); // 给画笔设置一个颜色
        pen.fillRect(0,0,80,20); // 用画笔在图片(画布)上从坐标(0,0)开始填充整个图片

        // 将生成的随机数,画到图片上
        String randNum = randomNum();
        pen.setColor(Color.blue);
        pen.setFont(new Font("宋体",Font.BOLD,20));
        pen.drawString(randNum,0,20);

        // 告诉浏览器,格式为图片,并且此Servlet响应的数据关闭缓存
        resp.setContentType("image/jpeg");
        resp.setDateHeader("expires",-1);
        resp.setHeader("Cache-Control","no-cache");
        resp.setHeader("Program","no-cache");

        // 将图片通过图片流写出去
        ServletOutputStream out = resp.getOutputStream();
        ImageIO.write(image,"jpg",out);
        out.close();
    }

    public static String randomNum(){
        Random random = new Random();
        String num = random.nextInt(9999999)+""; // 生成0~9999999的随机整数
        StringBuffer buffer = new StringBuffer(num);
        for (int i = 0; i < 7 - buffer.length() ; i++) { 
            // 如果生成的随机数不足以7位,就用0来补位
            buffer.append(0);
        }
        return buffer.toString();
    }
}


// 生成随机字母
Random random = new Random();
int num = 65 + random.nextInt(26); // [0,26)
System.out.print((char)num);

重定向 (重要)
public class RedirectTest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*
        重定向实则实现了以下代码 /javaweb 是配置tomcat时,给的一个默认访问起始路径
        resp.setHeader("Location","/javaweb/code");
        resp.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); // 状态码302
         */
        resp.sendRedirect("/javaweb/code");
    }
}

在这里插入图片描述

三、Cookie 类

在这里插入图片描述

import javax.servlet.http.Cookie; cookie中文为饼干

cookie的作用是用于辨别客户端的身份。cookie是存在客户端的。每个网站都有自己的cookie。

cookie就如同服务端给客户端的一个信件、标识,服务端可以通过这个来辨别客户端是否访问过该服务端。

客户端请求服务器,服务器可以通过请求对象获取客户端的cookie,从cookie中可以找到这个服务端给客户端的信件,如果没有给这个客户端设置过cookie或者找不到这个服务端为客户端设置的cookie,那么就给客户端新建一个cookie,以便于下次这个客户端访问这个服务器时的一个身份辨别。

cookie是由服务器给客户端的信息,由客户端(浏览器)存储。

在这里插入图片描述

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

        Cookie[] cookies = req.getCookies(); // 获取客户端的cookie
        if(cookies!=null){
            for (int i = 0; i < cookies.length; i++) {
                if(cookies[i].getName().equals("heroC")){
                    // 如果有过这个服务端设置的cookie,就做一些业务操作
                    cookies[i].getValue(); // 获取cookie值
                }
            }
        }else {
            Cookie cookie = new Cookie("heroC", "该服务器给你的其中一条cookie");
            cookie.setMaxAge(24*60*60); // 给cookie设置一个有效期,如果为0,那么有效期就为0秒
            resp.addCookie(cookie); // 本服务端给客户端的一个cookie
        }
    }

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

各个浏览器的cookie大小、个数限制


四、Session 接口 (重点)

在这里插入图片描述

javax.servlet.http.HttpSession 接口

Session是“会话控制”,Session对象存储特定用户会话所需的属性及配置信息。

session中存储的信息是由服务器存储,发送给浏览器一个session的ID,一个session的ID对应存储在服务器的一个session对象。

session针对用户,一个用户一个session。

session由服务器创建。

在这里插入图片描述

在这里插入图片描述

作用:

  • session可运用于购物车
public class SessionT 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,如果没有该用户的session就创建一个
        HttpSession session = req.getSession(); 

        // 给session设置键值对,值可以是一个对象,键为String类型
        session.setAttribute("name","heroC");

        if(session.isNew()){
            resp.getWriter().write("创建了一个session "+session.getId());
        }else {
            resp.getWriter().write("已经存在一个session " + session.getId());
        }

        // session.invalidate(); // 将这个session,设置为过期
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
<session-config>
    <!--设置session的过期时间,单位为分钟-->
    <session-timeout>1</session-timeout>
</session-config>

Cookie 与 Session 的区别与联系 重点

Cookies是属于Session对象的一种。但有不同,Cookies不会占服务器资源,是存在客服端内存或者一个cookie的文本文件中;而“Session”则会占用服务器资源。所以,尽量不要使用Session,而使用Cookies。但是我们一般认为cookie是不可靠的,session是可靠地,但是目前很多著名的站点也都以来cookie。有时候为了解决禁用cookie后的页面处理,通常采用url重写技术,调用session中大量有用的方法从session中获取数据后置入页面。


Session 与 ServletContext 的区别

  • Session针对用户而言,一个用户有自己的Session的ID,而其他用户没有他session的ID,因此无法访问这个用户的session,只能访问自己的session。

  • ServletContext是所有用户都可以访问的一个全局对象。可用于统计访问人数等。


URLEncoder 和 URLDecoder

import java.net.URLDecoder;
import java.net.URLEncoder;

// 解决乱码问题
URLEncoder.encode("str","utf-8"); // 将字符串,按照utf-8进行编译
URLDecoder.decode("str","utf-8"); // 将字符串,按照utf-8进行解析

五、JavaBean

JavaBean就是实体类,用于封装数据库中,查询到的数据记录的类

JavaBean特定写法:

  • 必须有无参构造器
  • 属性必须私有化
  • 必须有对应的get和set方法

一般用于和数据库的字段做映射,ORM:对象关系映射

推荐放在以下命名的包中:

  • com.xxx.pojo
  • com.xxx.entity
  • com.xxx.vo
  • com.xxx.dto

六、MVC三层架构

MVC:model view controller 模型/视图/控制器

模型就如同javabean,与数据库字段一一对应的实体类

视图就如同前端页面

控制器就如同servlet
在这里插入图片描述


七、Filter 接口 (重要)

javax.servlet Interface Filter 过滤器接口,用于过滤网站的数据

抽象方法

  • public void init(FilterConfig filterConfig)
  • public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  • public void destroy()

实现Filter的步骤:

  1. 创建一个类并实现Filter接口
  2. 重写Filter接口中的抽象方法
  3. 在web.xml中配置过滤路径

MyFilter.java

public class MyFilter implements Filter {
    // 过滤器初始化:服务器启动时初始化
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("MyFilter初始化");
    }

    // 过滤器,指定路径进行过滤
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 由于在每一个servlet上都会设置编码,所以可以在过滤器里设置编码,减少冗余代码
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html");
        
        // 固定写法,一定要通过chain调用doFilter,让请求继续往下走
        chain.doFilter(request,response);
    }

    // 过滤器销毁:服务器关闭时销毁
    @Override
    public void destroy() {
        System.out.println("MyFilter销毁");
    }
}

ServletFilter.java

public class ServletFilter extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter out = resp.getWriter();
        out.write("你好,过滤器");
    }

    @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_3_1.xsd"
         version="3.1"
         metadata-complete="true">

    <servlet>
        <servlet-name>helloFilter</servlet-name>
        <servlet-class>com.heroc.filter.ServletFilter</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloFilter</servlet-name>
        <url-pattern>/filter/show</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>helloFilter</servlet-name>
        <!--走该路径不会过滤-->
        <url-pattern>/show</url-pattern> 
    </servlet-mapping>

    <filter>
        <filter-name>MyFilter</filter-name>
        <filter-class>com.heroc.filter.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>MyFilter</filter-name>
        <!--指定路径下的请求走过滤器进行过滤-->
        <url-pattern>/filter/*</url-pattern>
    </filter-mapping>

</web-app>

结果:

访问localhost:8080/filter/show
走过滤器,过滤器会进行编码,页面正常显示结果

访问localhost:8080/show
不走过滤器,页面显示乱码

案例:用户登录注销 拦截

<!--/login.html-->
<html>
<body>
<h1>登录</h1>
<form action="/login" method="post">
    <input type="text" name="username">
    <button type="submit">登录</button>
</form>
</body>
</html>

<!--/error.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>错误</title>
</head>
<body>
<h1>登录失败</h1>
<a href="/index.html">返回登录页面</a>
</body>
</html>

<!--/sys/ok.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>成功</title>
</head>
<body>
<h1>登录成功</h1>
<a href="/loginout">注销</a>
</body>
</html>
// 处理/login请求
public class Login extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = req.getParameter("username");
        if(username.equals("admin")){
            req.getSession().setAttribute("USER_SESSION",req.getSession().getId());
            resp.sendRedirect("/sys/ok.html");
        }else {
            resp.sendRedirect("/error.html");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
// 处理/loginout请求
public class LoginOut extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Object session = req.getSession().getAttribute("USER_SESSION");
        if(session!=null){
            req.getSession().removeAttribute("USER_SESSION");
            resp.sendRedirect("/index.html");
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
// 过滤器
public class LoginFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

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

        if(req.getSession().getAttribute("USER_SESSION") == null){
            resp.sendRedirect("/error.html");
        }

        chain.doFilter(req,resp);
    }

    @Override
    public void destroy() {}
}
<servlet>
    <servlet-name>login</servlet-name>
    <servlet-class>com.heroc.login.Login</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>login</servlet-name>
    <url-pattern>/login</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>loginout</servlet-name>
    <servlet-class>com.heroc.login.LoginOut</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>loginout</servlet-name>
    <url-pattern>/loginout</url-pattern>
</servlet-mapping>

<filter>
    <filter-name>loginFilter</filter-name>
    <filter-class>com.heroc.login.LoginFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>loginFilter</filter-name>
    <url-pattern>/sys/*</url-pattern>
</filter-mapping>

八、监听器

javax.servlet.http Interface HttpSessionListener http的session监听器

public class OnlineCountListener implements HttpSessionListener{
    // 创建一个session监听
    // 一但创建session就会触发一次该事件
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        // 获取全局servletContext对象
        ServletContext context = se.getSession().getServletContext();
        Integer onlineCount = (Integer) context.getAttribute("OnlineCount");
        if(onlineCount==null){
            // 如果为null,说明之前没有人在线,所以你登陆了该网站,就是第一个在线的人
            onlineCount = new Integer(1);
        }else {
            // 如果不为null,就在值的位置增1
            int count = onlineCount.intValue();
            onlineCount = new Integer(count++);
        }

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

    // 销毁一个session监听
    // 一但session会话结束,就会触发一次该事件
    @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--);
        }

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

八、文件传输

1、文件上传调优(注意事项)

  • 为保证服务器安全,上传的文件应该放在无法访问的目录之下,比如放在WEB-INF文件之下
  • 保证上传文件的名字唯一,不能重复,防止重名文件而被覆盖(可通过时间戳、UUID、MD5、位运算算法等)
  • 要限制上传文件的大小
  • 可以限制上传文件的类型,判断文件类型是否合法

2、文件上传 案例

fileupload.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Layui</title>
    <meta name="renderer" content="webkit">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
</head>
<body>
<!--文件上传必须有enctype="multipart/form-data"属性-->
<form action="/upload" method="post" enctype="multipart/form-data">
    <p>用户名:<input type="text" name="username"></p>
    <p><input type="file" name="file"></p>
    <p><button type="submit">上传</button> | <button type="reset">重新选择</button></p>
</form>
</body>
</html>

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_3_1.xsd"
         version="3.1"
         metadata-complete="true">
    
    <servlet>
        <servlet-name>fileupload</servlet-name>
        <servlet-class>com.heroc.fileupload.FileUpload</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>fileupload</servlet-name>
        <url-pattern>/upload</url-pattern>
    </servlet-mapping>
    
</web-app>

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

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

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

        <!-- commons-io 与 commons-fileupload 为文件上传的包-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>


    </dependencies>
</project>

FileUpload.java

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

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

        // 判断是否是普通表单还是文件表单
        if(!ServletFileUpload.isMultipartContent(req)){
            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 tmpFile = new File(tmpPath);
        if(!tmpFile.exists()){
            tmpFile.mkdir();
        }

        // 1. 创建DiskFileItemFactory对象,用于处理文件的上传路径或大小限制
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 当文件大于1024*1024这个大小时,上传到临时保存路径中
        factory.setSizeThreshold(1024*1024);
        factory.setRepository(tmpFile);

        // 2. 创建ServletFileUpload对象
        ServletFileUpload upload = new ServletFileUpload(factory);
        // 监听文件上传进度
        upload.setProgressListener(new ProgressListener() {
            @Override
            public void update(long pBytesRead, long pContentLength, int pItems) {
                System.out.println("总大小:"+pContentLength+" 已上传:"+pBytesRead);
            }
        });
        // 处理乱码
        upload.setHeaderEncoding("UTF-8");
        // 设置单个文件的最大值 10M
        upload.setFileSizeMax(1024*1024*10);
        // 设置总共能上传文件的大小 10M
        upload.setSizeMax(1024*1024*10);

        // 3. 处理上传文件
        try {
            // 将表单中的每一项,封装为一个item
            List<FileItem> fileItems = upload.parseRequest(req);
            for (FileItem fileItem : fileItems) {
                if(fileItem.isFormField()){ // 判断文件是普通表单还是带文件的表单
                    // 普通表单
                    String name = fileItem.getFieldName();
                    String value = fileItem.getString("UTF-8");
                    System.out.println(name + ": " + value);
                }else {
                    //===========================处理文件===========================//
                    // 带文件表单,拿到文件名
                    String name = fileItem.getName();
                    // 不符合规范的文件名,则放弃
                    if(name.trim().equals("") || name==null){
                        continue;
                    }
                    // 获得文件名如同:/images/picture.png  需要获取picture.png的文件名
                    String fileName = name.substring(name.lastIndexOf("/") + 1);
                    // 拿到文件的扩展名
                    String fileExtName = name.substring(name.lastIndexOf(".") + 1);
                    System.out.println("文件名:"+fileName+" 扩展名:"+fileExtName);

                    // 使用UUID为了使得文件不重名,避免重名带来的文件覆盖问题
                    String uuid = UUID.randomUUID().toString();

                    //===========================存放地址===========================//
                    String realPath = uploadPath+"/"+ uuid;
                    File realFile = new File(realPath);
                    if(!realFile.exists()){
                        realFile.mkdir();
                    }

                    //===========================文件传输===========================//

                    // 获取该表单项的输入流
                    InputStream inputStream = fileItem.getInputStream();
                    // 创建一个输出流
                    FileOutputStream fileOutputStream = new FileOutputStream(realPath + "/" + fileName);
                    int len = 0;
                    byte[] bytes = new byte[1024];
                    while((len = inputStream.read(bytes)) != -1){
                        fileOutputStream.write(bytes,0,len);
                    }
                    fileOutputStream.close();
                    inputStream.close();
                    System.out.println("文件上传成功!");
                    resp.sendRedirect("/fileupload.html");
                }
            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        }
    }
}

九、邮件发送

MIME 多用途互联网邮件扩展类型。通俗说就是附件、图片。

<!-- javax.mail 与 javax.activation 为发送邮件的包-->
<dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mail</artifactId>
    <version>1.4.7</version>
</dependency>

<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>
</dependencies>

1、单线程邮件发送

// 发送只有文字内容的简单邮件
public class MailT{
    public static void main(String[] args) throws GeneralSecurityException, MessagingException {
        Properties properties = new Properties();
        // key值是写死了的
        properties.setProperty("mail.host","smtp.qq.com"); // QQ邮件服务器
        properties.setProperty("mail.transport.protocol","smtp"); // 发送邮件协议
        properties.setProperty("mail.smtp.auth","true"); // 需要验证用户密码

        // 关于QQ邮箱,需要设置SSL加密
        MailSSLSocketFactory sf = new MailSSLSocketFactory();
        sf.setTrustAllHosts(true);
        properties.put("mail.smtp.ssl.enable","true");
        properties.put("mail.smtp.ssl.socketFactory",sf);

        
        // QQ邮箱才有这段代码创建定义整个应用程序所需的环境信息的Session对象javax.mail.Session;
        Session session = Session.getDefaultInstance(properties, new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                // 发件人邮件用户名,授权码
                return new PasswordAuthentication("herocheung@foxmail.com",
                                                  "shyamjhysyhmbbgc");
            }
        });

        // 开启session的debug模式,这样就可以将日志信息在后台打印出来
        session.setDebug(true);

        
        // 通过session获取transport对象
        Transport transport = session.getTransport();
        // 使用邮箱的用户名和授权码连接上邮箱服务器
        transport.connect("smtp.qq.com","herocheung@foxmail.com", "shyamjhysyhmbbgc");

        
        // 创建邮箱 消息对象MimeMessage
        MimeMessage message = new MimeMessage(session);
        // 指明邮件发件人
        message.setFrom(new InternetAddress("herocheung@foxmail.com"));
        // 指明邮件的收件人,收件人可以是一个数组
        message.setRecipient(Message.RecipientType.TO,
                             new InternetAddress("herocheung@foxmail.com"));
        // 邮件标题
        message.setSubject("只包含文本的简单邮件");
        // 邮件的内容
        message.setContent("你好啊!","text/html;charset=utf-8");
        
        
        // 发送邮件 第一个参数发送的信息对象,第二参数获得收件人
        transport.sendMessage(message,message.getAllRecipients());
        transport.close();
    }
}

MimeMultipart类 将各个模块封装起来的类型,各个类型向下兼容

在这里插入图片描述

MimeMultipart类 是一个封装类,邮件中文本、内嵌资源、附件都是一个一个的MimeBodyPart类,然后统一封装到一个类中,最后添加在message对象中,由transport类进行发送

在这里插入图片描述

// 发送有附件图片的邮件
public static void main(String[] args){
    try {
            Properties properties = new Properties();
            properties.setProperty("mail.host","smtp.qq.com"); // QQ邮件服务器
            properties.setProperty("mail.transport.protocol","smtp"); // 发送邮件协议
            properties.setProperty("mail.smtp.auth","true"); // 需要验证用户密码

            MailSSLSocketFactory factory = new MailSSLSocketFactory();
            factory.setTrustAllHosts(true);
            properties.put("mail.smtp.ssl.enable","true");
            properties.put("mail.smtp.ssl.socketFactory",factory);

            Session session = Session.getDefaultInstance(properties, 
                                                         new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication("herocheung@foxmail.com",
                                                      "shyamjhysyhmbbgc");
                }
            });

            session.setDebug(true);

            Transport transport = session.getTransport();
            transport.connect("smtp.qq.com","herocheung@foxmail.com", 
                              "shyamjhysyhmbbgc");

            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress("herocheung@foxmail.com"));
            message.setRecipient(Message.RecipientType.TO,
                                 new InternetAddress("herocheung@foxmail.com"));
            message.setSubject("这是通过servlet发送含有图片的邮件");
        
        // ========================================================================
            // 存储图片块
            MimeBodyPart img = new MimeBodyPart();
            // 将附件进行数据处理
            DataHandler handler = new DataHandler(new FileDataSource(
                "D:\\照片\\IMG_20191127_181018.jpg"));
            img.setDataHandler(handler);
            img.setContentID("img.jpg"); // 设置资源的cid,后面会用到

            // 存储文本块
            MimeBodyPart text = new MimeBodyPart();
            // 在引入图片时,通过cid:img.jpg形式引入
            text.setContent(
                "这是一封含图片的邮件<img src='cid:img.jpg'>","text/html;charset=utf-8");

            // 将每个块封装到一起
            MimeMultipart mimeMultipart = new MimeMultipart();
            mimeMultipart.addBodyPart(img);
            mimeMultipart.addBodyPart(text);
            mimeMultipart.setSubType("mixed"); // 设置一个封装等级
        // ========================================================================
        
            message.setContent(mimeMultipart); // 在message中添加该封装块
            message.saveChanges(); // 保存

            transport.sendMessage(message,message.getAllRecipients()); // 发送邮件
            transport.close();

        } catch (GeneralSecurityException | NoSuchProviderException e) {
            e.printStackTrace();
        } catch (MessagingException e) {
            e.printStackTrace();
        }
}

2、多线程邮件发送,注册

合理使用多线程,可以增强用户体验感

User.java

该类用于存储注册用户信息

public class User implements Serializable {
    private static final long serialVersionUID = 1130456772345234L;

    private String username;
    private String pwd;
    private String email;

    public User() {
    }

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

    public String getUsername() {
        return username;
    }

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

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public String getEmail() {
        return email;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", pwd='" + pwd + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

MailThread.java

该类为发送邮件的线程

public class MailThread implements Runnable {
    private User user;

    public MailThread() {
    }

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

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

            MailSSLSocketFactory factory = new MailSSLSocketFactory();
            factory.setTrustAllHosts(true);
            properties.put("mail.smtp.ssl.enable", "true");
            properties.put("mail.smtp.ssl.socketFactory", factory);

            Session session = Session.getDefaultInstance(properties, new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication("herocheung@foxmail.com", "shyamjhysyhmbbgc");
                }
            });

            session.setDebug(true);

            Transport transport = session.getTransport();
            transport.connect("smtp.qq.com", "herocheung@foxmail.com", "shyamjhysyhmbbgc");

            MimeMessage message = new MimeMessage(session);
            message.setFrom(new InternetAddress("herocheung@foxmail.com"));
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
            message.setSubject("heroC网站注册 -- 请记住");
            message.setContent("<h1>欢迎注册heroC网站</h1> <h3>您已注册成功!您在本站注册的用户名: "+user.getUsername()+", 密码:"+user.getPwd()+"</h3><h3>谢谢您的注册!</h3>", "text/html;charset=utf-8");

            transport.sendMessage(message, message.getAllRecipients());
            transport.close();


        } catch (GeneralSecurityException | NoSuchProviderException e) {
            e.printStackTrace();
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }
}

MailThreadServlet.java

该类接收前端发送的请求,并开启发送邮箱线程,并向前端发送结果

public class MailThreadServlet extends HttpServlet {
    @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 pwd = req.getParameter("pwd");
        String email = req.getParameter("email");
        User user = new User(username, pwd, email);

        new Thread(new MailThread(user)).start();

        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().write("已注册成功!<br> 邮件已发送,可能由于网络原因,有所延迟,请注意查收!");
    }
}

register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>heroC注册</title>
</head>
<body>
<form action="/register" method="post">
    <p>用户名:<input type="text" name="username"></p>
    <p>邮箱:<input type="text" name="email"></p>
    <p>密码:<input type="password" name="pwd"></p>
    <p><button type="submit">注册</button></p>
</form>
</body>
</html>

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_3_1.xsd"
         version="3.1"
         metadata-complete="true">    
    
	<servlet>
        <servlet-name>register</servlet-name>
        <servlet-class>com.heroc.mail.mailthread.MailThreadServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>register</servlet-name>
        <url-pattern>/register</url-pattern>
    </servlet-mapping>
    
</web-app>

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值