Java_web入门

Java_web全记录

Java_web概述

Java_web应用由一组Servlet、Html页、类以及其它可绑定的资源构成。它可以在各种供应商提供的Servlet规范的Servlet容器中运行。

Java_web包含以下内容:

  • Servlet

  • JSP

  • 实体类

  • 静态文档如HTML,图片等

  • 描述web应用的信息web.xml

Servlet与Servlet容器

 

 

Java_web目录结构

 

Servlet

Servlet功能

  • 创建并返回基于客户请求的动态HTML页面

  • 创建可嵌入到现有HTML页面中的部分HTML页面

  • 与其他服务器资源(如数据库或基于Java的应用程序)进行通信

 

Servlet容器响应客户请求

 

第一个servlet及其在web.xml中的配置

package com.why.study2.servlet;

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

public class HelloServlet implements Servlet {
   @Override
   public void init(ServletConfig servletConfig) throws ServletException {
       System.out.println("init");
  }

   @Override
   public ServletConfig getServletConfig() {
       System.out.println("getServletConfig");
       return null;
  }

   @Override
   public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
       System.out.println("service");
  }

   @Override
   public String getServletInfo() {
       System.out.println("getServletInfo");
       return null;
  }

   @Override
   public void destroy() {
       System.out.println("destroy");
  }

   public HelloServlet() {
       System.out.println("HelloServlet构造器");
  }
}
<!--    配置和映射Servlet-->
   <servlet>
<!--       Servlet注册名-->
       <servlet-name>helloServlet</servlet-name>
<!--       Servlet全类名-->
       <servlet-class>com.why.study2.servlet.HelloServlet</servlet-class>
   </servlet>
   <servlet-mapping>
<!--       需要和某一节点的servlet-name保持一致-->
       <servlet-name>helloServlet</servlet-name>
<!--       映射具体的访问路径-->
       <url-pattern>/helloServlet</url-pattern>
   </servlet-mapping>

同意Servlet可映射多个servletmapping,即多个servletmapping的<servlet-name>可相同

带扩展名/不能加*,带 *不能加扩展名,即/ *.html不合法

Servlet容器

  1. 创建Servlet,并调用Servlet的相关生命周期方法

  2. 运行Servlet,JSP,Filter,Listener,Tag等的软件环境

Servlet 生命周期的方法

以下方法都是由Servlet容器负责调用:

  1. 构造器:只有第一次请求Servlet时创建Servlet的实例,调用构造 器,Servlet是单实例的,非线程安全

  2. init方法:对象只被调用一次,创建好实例后立即被调用,用于初 始化Servlet

  3. service方法:每次请求都会调用service方法,实际用于响应请求

  4. destroy方法:只被调用一次,web应用被卸载前调用,释放Servlet占用的资源

load-on-startup,指定Servlet被创建的时机

<!--    配置和映射Servlet-->
   <servlet>
<!--       Servlet注册名-->
       <servlet-name>helloServlet</servlet-name>
<!--       Servlet全类名-->
       <servlet-class>com.why.study2.servlet.HelloServlet</servlet-class>
<!--       可以指定Servlet被创建的时机,数字越小越早该Servlet先被创建-->
       <load-on-startup>1</load-on-startup>
   </servlet>

ServletConfig对象

封装Servlet的配置信息,并且可以获取ServletContext对象

  1. 配置Servlet的初始化参数

    <!--        配置Servlet初始化参数,要放在<load-on-startup>之前-->
           <init-param>
    <!--           初始化参数名-->
               <param-name>password</param-name>
    <!--           初始化参数值-->
               <param-value>1230</param-value>
           </init-param>
           <init-param>
               <!--           初始化参数名-->
               <param-name>user</param-name>
               <!--           初始化参数值-->
               <param-value>why</param-value>
           </init-param>
  2. 获取初始化参数

     

    • getInitParameter(String name):获取指定参数名的初始化参数

    • getInitParameterNames():获取参数名组成的Enumeration对象

    String user = servletConfig.getInitParameter("user");
    System.out.println("名字"+user);
    
    String password = servletConfig.getInitParameter("password");
    System.out.println("密码"+password);
    
    Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();
    while (initParameterNames.hasMoreElements()){
        String s = initParameterNames.nextElement();
        System.out.println(s);
    }
  3. 获取Servlet配置名称

    1. String servletName = servletConfig.getServletName();
       System.out.println(servletName);

ServletContext接口

 

  1. 可由ServletConfig获取

  2. 该对象代表当前web应用,获取当前web应用的各种信息

  3. 获取当前web应用的初始化参数

    配置当前web应用的初始化参数

    <!--    配置当前web应用的初始化参数-->
        <context-param>
            <param-name>driver</param-name>
            <param-value>com.mysql.jdbc.Driver</param-value>
        </context-param>
        <context-param>
            <param-name>jdbcUrl</param-name>
            <param-value>jdbc:mysql:///atguigu</param-value>
        </context-param>

    获取参数

    //获取ServletContext对象
    ServletContext servletContext = servletConfig.getServletContext();
    String driver = servletContext.getInitParameter("driver");
    System.out.println(driver);
    
    Enumeration<String> initParameterNames1 = servletContext.getInitParameterNames();
    while (initParameterNames1.hasMoreElements()){
        String s = initParameterNames1.nextElement();
        System.out.println(s);
  4. 获取当前某一web应用的服务器的绝对路径

    //获取当前web应用服务器上发布的的绝对路径
    String realPath = servletContext.getRealPath("HelloServlet.java");
    System.out.println(realPath);
  5. 获取当前web应用的名称

    //获取当前web应用服务器上发布的的绝对路径
    String realPath = servletContext.getRealPath("HelloServlet.java");
    System.out.println(realPath);
  6. 获取当前web应用对应的某一个文件的输入流

    //获取文件输入流
    InputStream resourceAsStream = servletContext.getResourceAsStream("WEB-INF/classes/a.properties");
    System.out.println(resourceAsStream);
  7. attribute相关的方法

HTTP协议 GET & POST 请求

 

 

  • GET请求

    在浏览器输入某个url地址或点击一个超链接时,浏览器一定是get请求

    <form>”表单可设置get请求

    使用get请求有限制,一般为1kb以下

  • POST请求

    form表单指定为post请求时才可为post请求

    文件上传必须使用post请求

ServletRequest,封装请求信息

其中主要代码写于service中,它用于应答每次的请求

  • 根据提交参数返回提交的内容

    String user = servletRequest.getParameter("user");
    String password = servletRequest.getParameter("password");
    System.out.println("user "+user+" password "+password);
  • 根据请求参数名,返回请求参数返回对应的字符串

    String[] interestings = servletRequest.getParameterValues("interesting");
    System.out.println(Arrays.toString(interestings));

    用于返回提交的多选框的内容

  • 返回参数名对应的Enumeration对象,类似于ServletConfig(或ServletContext)

    Enumeration<String> parameterNames = servletRequest.getParameterNames();
    System.out.println(parameterNames);
  • 返回请求参数的键值对,key——参数名,value——参数值,string数组类型

    Map<String, String[]> parameterMap = servletRequest.getParameterMap();
    System.out.println(parameterMap);

ServletResponse,封装响应信息

  • 打印字符串到浏览器页面

    PrintWriter writer = servletResponse.getWriter();
    writer.println("hello java_web...");
  • 设置响应类型

    返回word类型,需要返回的类型可取tomcat的config目录下的web.xml文件中查找

    servletResponse.setContentType("application/msword");

GenericServlet,定义一个普遍的Servlet,其余Servlet继承此类

由于Servlet接口中的方法很多都不需要,所有我们写一个新的GnericServlet类继承Servlet创建Servlet时只需继承GnericServlet即可

  • GenericServlet.java

    package com.why.servlet;
    
    import javax.servlet.*;
    import java.io.IOException;
    
    public abstract class GenericServlet implements Servlet {
    
        //定义私有属性
        private ServletConfig servletConfig;
        
        public GenericServlet() {
        }
        
        @Override
        public void init(ServletConfig servletConfig) throws ServletException {
        
            this.servletConfig = servletConfig;
        }
        
        @Override
        public ServletConfig getServletConfig() {
        
            //得到servletConfig
            return this.servletConfig;
        }
        
        @Override
        public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException ;
        
        @Override
        public String getServletInfo() {
            return null;
        }
        
        @Override
        public void destroy() {
        
        }
    
    }
  • 自定义的Servlet

    package com.why.servlet;
    
    import javax.servlet.*;
    import java.io.IOException;
    
    public class LoginServlet extends GenericServlet{
    
        public LoginServlet() {
        }
    
    
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            String username = servletRequest.getParameter("username");
            String password = servletRequest.getParameter("password");
    
            String username1 = getServletConfig().getServletContext().getInitParameter("username");
            String password1 = getServletConfig().getServletContext().getInitParameter("password");
    
            if (username!=null&&username.equals(username1)&&password!=null&&password.equals(password1)){
                servletResponse.getWriter().println(username);
                servletResponse.getWriter().println(password);
                servletResponse.getWriter().println("欢迎回来"+username+"!");
            }else {
                servletResponse.getWriter().println("用户名或密码有误,请重新登录!");
            }
        }
    }

    HttpServlet

    传统servlet不能很好区别post和get方法

    package com.why.study2.servlet;

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

    @WebServlet(name = "LoginServlet1")
    public class LoginServlet1 extends HttpServlet {
       protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
           String user = request.getParameter("user");
           String password = request.getParameter("password");
           String[] interestings = request.getParameterValues("interesting");

           PrintWriter writer = response.getWriter();
           writer.println(user+"\r\n");
           writer.println(password+"\r\n");
           writer.println(Arrays.toString(interestings));
      }

       protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

      }
    }

JSP

JSP概述

  • 动态网页,希望在浏览器页面中编写Java代码,简化Servlet

  • 举例:

    <%@ page import="java.util.Date" %><%-- Created by IntelliJ IDEA. --%>
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
     <head>
       <title>index</title>
     </head>
     <body>

    <%
       Date date = new Date();
       System.out.println(date);
    %>

    hello java_web
     </body>
    </html>
  • JSP可放置在web应用除了WEB-INF中的任何目录下

  • JSP本质上是一个Servlet

JSP页面的9个隐含对象

  1. PageContext pageContext = null; 配置页面上下文,可从该对象获取到其他八个隐含对象,也可获取到其他信息(自定义标签时使用);

  2. HttpSession session = null;

  3. ServletConfig config = null; 使用时需要映射jsp,同servlet

  4. ServletContext application = null;

  5. JspWriter out = null; 调用out.println(),可把字符串打印在浏览器页面上;

  6. Object page = this; 指向当前JSP对应的Servlet对象的引用,几乎不使用;

  7. request;

  8. response,几乎不会使用。

使用<% %>编写代码在此位置,可用到这八个隐含对象(实际上还有exception隐含对象)

exception使用需要一定的条件,要有声明<%@ page isErrorPage="true" %>

隐含对象不需要声明即可使用

jsp换行可通过<% %>换行,可嵌套至html中

JSP指令

  1. <%@page ... %> 定于一网页依赖属性,比如脚本语言、error页面、缓存需求等

    一个JSP页面可包含多个page指令

    语法格式:

    <%@ page attribute="value" %>

    相关属性:

     

     

  2. <%@ include ... %> 包含其他文件被包含的文件可以是JSP文件、HTML文件或文本文件。包含的文件就好像是该JSP文件的一部分,会被同时编译执行。

    指令格式:

    <%@ include file="文件相对 url 地址" %>
  3. <%@taglib ... %> 引入标签库定义。JSP API允许用户自定义标签,一个自定义标签库就是自定义标签的集合。

    Taglib指令引入一个自定义标签集合的定义,包括库路径、自定义标签。

    指令格式:

    <%@ taglib uri="uri" prefix="prefixOfTag" %>

JSP页面的请求的转发和重定向

本质区别:请求的撞飞只发出一次请求,重定向多次请求

  • 转发

    request.getRequestDispatcher("other.jsp").forward(request, response);

     

  • 重定向

    response.sendRedirect("other.jsp");

JSP域对象及其属性有关的方法

 

解决中文乱码问题

  1. page指令字符编码集要支持中文

  2. post请求request之前设置页面编码格式

    request.setCharacterEncoding("UTF-8");
  3. GET请求tomcat有问题

     

    重新配置tomcat

MVC开发模式

MVC概述

Model-View-Controller,即为MVC。

  1. Model层:包括业务处理层Service和数据访问层Dao。

    Dao数据访问层主要是对数据库中一些操作地封装。

    Service业务处理层主要是用作将从Controller层获取地数据和数据库的数据进行桥接,对复杂的业务逻辑进行处理,比如事务处理

  2. Controller层:指控制部分,是对View层提交的请求为其设置对应的Servlet进行特定功能的处理,充当中介的作用

  3. View层:视图部分,如jsp或html页面

不推荐跨层操作,各层之间耦合度要低,即独立性高

MVC模式开发web应用示例

创建工程,工程目录

 

创建数据库mvcproject

设计user表

 

entity包中创建User实体类

package com.why.study2.entity;

import java.util.Date;
import java.util.Objects;

/**
 * 用户类
 * @author why
 */
public class User {

    /**
     * 用户编号
     */
    private Integer id;
    /**
     * 用户名
     */
    private String username;
    /**
     * 用户密码
     */
    private String password;
    /**
     * 用户电话
     */
    private String phoneNo;
    /**
     * 用户地址
     */
    private String address;
    /**
     * 用户注册日期
     */
    private Date regDate;

    public User(Integer id, String username, String password, String phoneNo, String address, Date regDate) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.phoneNo = phoneNo;
        this.address = address;
        this.regDate = regDate;
    }

    public User() {
    }

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

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

    public String getPassword() {
        return password;
    }

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

    public String getPhoneNo() {
        return phoneNo;
    }

    public void setPhoneNo(String phoneNo) {
        this.phoneNo = phoneNo;
    }

    public String getAddress() {
        return address;
    }

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

    public Date getRegDate() {
        return regDate;
    }

    public void setRegDate(Date regDate) {
        this.regDate = regDate;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", phoneNo='" + phoneNo + '\'' +
                ", address='" + address + '\'' +
                ", regDate=" + regDate +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(id, user.id) &&
                Objects.equals(username, user.username) &&
                Objects.equals(password, user.password) &&
                Objects.equals(phoneNo, user.phoneNo) &&
                Objects.equals(address, user.address) &&
                Objects.equals(regDate, user.regDate);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, username, password, phoneNo, address, regDate);
    }
}

一定有做好注释等工作

添加c3p0需要的jar包和MySQL驱动的jar包

 

c3p0的配置

  1. 在src目录下创建c3p0-config.xml文件

     

  2. 配置 c3p0-config.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <c3p0-config>
    
        <!-- This app is massive! -->
        <named-config name="mysql">
            <!--    连接MySQL基本的配置-->
            <property name="jdbcUrl">jdbc:mysql://localhost:3306/mvcproject?serverTimezone=Asia/Shanghai</property>
            <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
            <property name="user">root</property>
            <property name="password">root</property>
    
            <!--        若数据库中的连接数量不足时,向数据库申请的连接数量-->
            <property name="acquireIncrement">5</property>
            <!--        初始化数据库连接池时链接的数量-->
            <property name="initialPoolSize">10</property>
            <!--        数据库连接池中最小的数据库链接数-->
            <property name="minPoolSize">5</property>
            <!--        数据库连接池中最大的数据库链接数-->
            <property name="maxPoolSize">100</property>
    
            <!-- intergalactoApp adopts a different approach to configuring statement caching -->
            <!--        c3p0数据库连接池可维护的Statement数量-->
            <property name="maxStatements">2</property>
            <!--        每个连接同时可使用的Statement数量-->
            <property name="maxStatementsPerConnection">5</property>
    
        </named-config>
    </c3p0-config>
  3. 在Utils包下创建JdbcUtils工具类

     

  4. 编写JdbcUtils工具类

    package com.why.study2.utils;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    /**
     * jdbc工具类
     *
     * @author why
     */
    public class JdbcUtils {
    
        //数据库连接池,C3P0
        private static DataSource dataSource = null;
        static {//静态代码块只会被执行一次
            dataSource = new ComboPooledDataSource();
        }
    
        /**
         * 获取到mysql的数据库连接对象
         *
         * @return conn
         */
        public static Connection getConnection(){
            Connection conn = null;
            try {
                conn = dataSource.getConnection();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return conn;
        }
        
        /**
         * 通用的关闭数据库连接对象的方法
         * @param conn
         */
        public static void closeConn(Connection conn){
            if (conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

创建测试类JdbcUtilsTest.java

package com.why.study2.test;

import com.why.study2.utils.JdbcUtils;
import org.junit.jupiter.api.Test;

import java.sql.Connection;

import static org.junit.jupiter.api.Assertions.*;

class JdbcUtilsTest {

    @Test
    void getConnection() {
        Connection connection = JdbcUtils.getConnection();
        System.out.println(connection);
    }
}

dao层开发

创建BaseDao.java

实现基本的增删改查操作

package com.why.study2.dao;

import com.why.study2.utils.JdbcUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * 这是一个dao层中的基本类,在于被具体的dao类继承,不能直接new BasesDao()来直接使用
 *
 * @author why
 * @param <T>   针对要操作各章数据包映射到java工程里的java类
 */
public class BaseDao<T> {

    QueryRunner queryRunner = new QueryRunner();

    private Class<T> Clazz;

    /**
     * 反射拿到T.class
     */
    public BaseDao() {
        //用BaseDao的构造器初始化aClass属性
        //拿到User类
        Type superType = this.getClass().getGenericSuperclass();//getGenericSuperclass()拿到调用者的父类的类型
        //拿到User.class
        if (superType instanceof ParameterizedType){
            ParameterizedType pt = (ParameterizedType) superType;
            Type[] tarry = pt.getActualTypeArguments();//返回类型数组,其第一个元素是我们需要的T.class
            if (tarry[0] instanceof Class){
                Clazz = (Class<T>) tarry[0];
            }
        }
    }

    /**
     * 查询数据表,取出结果sql语句的结果集的第一条数据,封装成一个类对象返回,不支持事务
     * 用到dbutils工具类
     * @param sql
     * @param args
     * @return
     */
    public T get(String sql,Object... args){
        Connection conn = null;
        T entity = null;

        //拿到conn
        try {
            conn = JdbcUtils.getConnection();
            entity = queryRunner.query(conn,sql, new BeanHandler<T>(Clazz),args);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.closeConn(conn);
        }
        return entity;
    }

    /**
     * 重写查询数据表方法,取出结果sql语句的结果集的第一条数据,封装成一个类对象返回,支持事务
     * @param sql
     * @param args
     * @return
     */
    public T get(Connection conn,String sql,Object... args){
        T entity = null;
        try {
            entity = queryRunner.query(conn,sql, new BeanHandler<T>(Clazz),args);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

        }
        return entity;
    }

    /**
     * 获取多条记录的通用方法
     * @return
     */
    List<T> getList(String sql,Object... args){
        Connection conn = null;
        List<T> list = null;

        //拿到conn
        try {
            conn = JdbcUtils.getConnection();
            list = queryRunner.query(conn,sql, new BeanListHandler<T>(Clazz),args);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.closeConn(conn);
        }
        return list;
    }

    /**
     * 实现insert,update,delete通用的更新方法
     * @param sql
     * @param args
     * @return
     */
    int update(String sql,Object... args){
        Connection conn = null;
        int rows = 0;

        //拿到conn
        try {
            conn = JdbcUtils.getConnection();
            rows = queryRunner.update(conn,sql,args);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.closeConn(conn);
        }
        return rows;
    }

    /**
     * 通用的sql语句的结果只有一个数值的类型的查询,用户个数或其他计数
     * @param sql
     * @param args
     * @return
     */
    Object getValue(String sql,Object... args){
        Connection conn = null;
        Object obj = null;

        //拿到conn
        try {
            conn = JdbcUtils.getConnection();
            obj = queryRunner.query(conn,sql, new ScalarHandler<>(),args);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.closeConn(conn);
        }
        return obj;

    }
}
创建UserDao接口
package com.why.study2.dao;

import com.why.study2.entity.User;

import java.sql.Connection;
import java.util.List;

/**
 * 接口制定规则,只定义方法但不实现
 * UserDao只定义与User数据表有关的方法
 *
 * @author why
 */
public interface UserDao {
    /**
     * 实现插入一条用户数据
     * @param user
     * @return
     */
    int save(User user);

    /**
     * 根据用户编号删除用户
     * @param id
     * @return
     */
    int deleteUserById(int id);

    /**
     * 根据id更新用户数据
     * @param user
     * @return
     */
    int updateUserById(User user);

    /**
     * 根据用户id获取用户信息,支持事务
     * @param id
     * @return
     */
    User get(int id);

    /**
     * 支持事务
     * @param conn
     * @param id
     * @return
     */
    User get(Connection conn,int id);
    /**
     * 获取所有用户数据
     * @return
     */
    List<User> getListAll();

    /**
     * 查询指定用户名的人有多少人
     * @param username
     * @return
     */
    long getCountByName(String username);

    /**
     * dao层实现模糊查询的方法
     * @param username
     * @param address
     * @param phoneNo
     * @return
     */
    List<User> query(String username, String address, String phoneNo);
}
UserDao实现类
package com.why.study2.dao;

import com.why.study2.entity.User;

import java.sql.Connection;
import java.util.List;

public class UserDaoImpl extends BaseDao<User> implements UserDao {
    @Override
    public int save(User user) {
        String sql = "INSERT INTO user(`username`,`password`,`phone_no`,`address`,`reg_date`) VALUES(?,?,?,?,?);";
        return super.update(sql,user.getUsername(),user.getPassword(),user.getPhoneNo(),user.getAddress(),user.getRegDate());
    }

    @Override
    public int deleteUserById(int id) {
        String sql = "delete from user where id = ?";
        return super.update(sql,id);
    }

    @Override
    public int updateUserById(User user) {
        String sql = "update user set username = ?,password = ?,phone_no = ?,address = ? where id = ?";
        return super.update(sql,user.getUsername(),user.getPassword(),user.getPhoneNo(),user.getId());
    }

    /**
     * 不支持事务
     * @param id
     * @return
     */
    @Override
    public User get(int id) {
        String sql = "select `id`,`username`,`password`,`phone_no` phoneNo,`address`,`reg_date` regDate FROM user WHERE `id`=?;";
        User user = super.get(sql, id);
        return user;
    }

    /**
     * 支持事务
     * @param conn
     * @param id
     * @return
     */
    @Override
    public User get(Connection conn, int id) {
        String sql = "select `id`,`username`,`password`,`phone_no` phoneNo,`address`,`reg_date` regDate FROM user WHERE `id`=?;";
        return super.get(conn,sql,id);
    }

    @Override
    public List<User> getListAll() {
        String sql = "select `id`,`username`,`password`,`phone_no` phoneNo,`address`,`reg_date` regDate from user";
        return super.getList(sql);
    }

    @Override
    public long getCountByName(String username) {
        String sql = "SELECT COUNT(id) FROM user WHERE username = ?;";
        return (long) super.getValue(sql,username);
    }

    /**
     * 拼接sql语句存在sql注入的风险
     * @param username
     * @param address
     * @param phoneNo
     * @return
     */
    @Override
    public List<User> query(String username, String address, String phoneNo) {
        System.out.println(username);
        System.out.println(address);
        System.out.println(phoneNo);

        String sql = "SELECT id,username,password,phone_no phoneNo,address,reg_date regDate FROM user WHERE 1=1";
        if (username!=null&&!"".equals(username)){
            sql = sql + " AND username like '%"+username+"%'";
        }
        if (address!=null&&!"".equals(address)){
            sql = sql + " AND address like '%"+address+"%'";
        }
        if (phoneNo!=null&&!"".equals(phoneNo)){
            sql = sql + " AND phone_no like '%"+phoneNo+"%'";
        }

        System.out.println(sql);
        return super.getList(sql);
    }
}
创建FactoryDao

降低耦合度,dao层修改service及其他层不用修改

package com.why.study2.dao;

/**
 * 工厂类
 * 降低耦合度
 */
public class FactoryDao {

    /**
     * 返回Dao层对象
     * @return
     */
    public static UserDao getUserDao(){
        return new UserDaoImpl();
    }
}

业务(Service)层开发

创建UserService接口及其实现类

接口Userservice.java

package com.why.study2.service;

import com.why.study2.entity.User;

import java.sql.Connection;
import java.util.List;

public interface UserService {
    /**
     * 实现插入一条用户数据
     * @param user
     * @return
     */
    int save(User user);

    /**
     * 根据用户编号删除用户
     * @param id
     * @return
     */
    int deleteUserById(int id);

    /**
     * 根据id更新用户数据
     * @param user
     * @return
     */
    int updateUserById(User user);

    /**
     * 根据用户id获取用户信息,支持事务
     * @param id
     * @return
     */
    User get(int id);

    /**
     * 支持事务
     * @param id
     * @return
     */
    User getTransaction(int id);
    /**
     * 获取所有用户数据
     * @return
     */
    List<User> getListAll();

    /**
     * 查询指定用户名的人有多少人
     * @param username
     * @return
     */
    long getCountByName(String username);

    /**
     * 这是用户模糊查询的方法
     * @param username
     * @param address
     * @param phoneNo
     * @return
     */
    List<User> query(String username, String address, String phoneNo);
}

实现类UserServiceImpl.java

package com.why.study2.service.impl;

import com.why.study2.dao.FactoryDao;
import com.why.study2.dao.UserDao;
import com.why.study2.dao.UserDaoImpl;
import com.why.study2.entity.User;
import com.why.study2.service.UserService;
import com.why.study2.utils.JdbcUtils;
import jdk.nashorn.internal.runtime.regexp.JoniRegExp;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

public class UserServiceImpl implements UserService {

    UserDao userDao = FactoryDao.getUserDao();

    @Override
    public int save(User user) {
        return userDao.save(user);
    }

    @Override
    public int deleteUserById(int id) {

        return userDao.deleteUserById(id);
    }

    @Override
    public int updateUserById(User user) {

        return userDao.updateUserById(user);
    }

    @Override
    public User get(int id) {
        return userDao.get(id);
    }

    @Override
    public User getTransaction(int id) {
        Connection conn = null;
        User user = null;
        try {
            conn = JdbcUtils.getConnection();
            conn.setAutoCommit(false);
            user = userDao.get(conn,id);
            conn.commit();
        } catch (SQLException e) {
            e.printStackTrace();
            JdbcUtils.rollbackTransaction(conn);
        } finally {
        }
        return user;
    }

    @Override
    public List<User> getListAll() {

        return userDao.getListAll();
    }

    @Override
    public long getCountByName(String username) {

        return userDao.getCountByName(username);
    }

    @Override
    public List<User> query(String username, String address, String phoneNo) {
        return userDao.query(username,address,phoneNo);
    }
}

Controller层开发

实现控制转发的功能

package com.why.study2.service;

import com.why.study2.entity.User;

import java.sql.Connection;
import java.util.List;

public interface UserService {
    /**
     * 实现插入一条用户数据
     * @param user
     * @return
     */
    int save(User user);

    /**
     * 根据用户编号删除用户
     * @param id
     * @return
     */
    int deleteUserById(int id);

    /**
     * 根据id更新用户数据
     * @param user
     * @return
     */
    int updateUserById(User user);

    /**
     * 根据用户id获取用户信息,支持事务
     * @param id
     * @return
     */
    User get(int id);

    /**
     * 支持事务
     * @param id
     * @return
     */
    User getTransaction(int id);
    /**
     * 获取所有用户数据
     * @return
     */
    List<User> getListAll();

    /**
     * 查询指定用户名的人有多少人
     * @param username
     * @return
     */
    long getCountByName(String username);

    /**
     * 这是用户模糊查询的方法
     * @param username
     * @param address
     * @param phoneNo
     * @return
     */
    List<User> query(String username, String address, String phoneNo);
}

View试图层开发

index.jsp

<%@ page import="java.util.Date" %>
<%@ page import="java.io.PrintWriter" %>
<%@ page import="com.why.study2.entity.User" %>
<%@ page import="java.util.List" %><%-- Created by IntelliJ IDEA. --%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>index</title>

    <style type="text/css">
      tr{
        height: 40px;
      }
      td{
        padding: 10px;
      }
    </style>
  </head>
  <body>
  <form action="<%=request.getContextPath()%>/query.udo" method="post">
  <table style="margin: 100px;padding: 50px;border: 1px #ccc solid; width: 400px">
    <tr>
      <td style="text-align: right">用户名:</td>
      <td style="text-align: left"><input type="text" name="username"></td>
    </tr>
    <tr>
      <td style="text-align: right">用户地址:</td>
      <td style="text-align: left"><input type="text" name="address"></td>
    </tr>
    <tr>
      <td style="text-align: right">用户电话:</td>
      <td style="text-align: left"><input type="text" name="phoneNO"></td>
    </tr>

      <tr>
        <td colspan="2" style="text-align:center">
          <input type="submit" value="查询用户">
        </td>
      </tr>
    <tr>
      <td colspan="2" style="text-align:center">
        <a href="<%=request.getContextPath()%>/add.jsp">注册新用户</a>
      </td>
    </tr>
  </table>
  </form>

  <table style="margin-left: 100px; padding: 50px;" border="1" cellpadding="0" cellspacing="0">
    <tr>
      <td>用户ID</td>
      <td>用户姓名</td>
      <td>用户密码</td>
      <td>用户电话</td>
      <td>用户地址</td>
      <td>注册日期</td>
      <td>操作记录</td>
    </tr>
    <%
     List<User> list = (List<User>) request.getAttribute("userList");
      System.out.println("jsp"+list);
     if (list!=null && list.size()>0){
       for (User user : list) {
    %>
    <tr>
      <td><%=user.getId()%></td>
      <td><%=user.getUsername()%></td>
      <td><%=user.getPassword()%></td>
      <td><%=user.getPhoneNo()%></td>
      <td><%=user.getAddress()%></td>
      <td><%=user.getRegDate()%></td>
      <td><a href="<%=request.getContextPath()%>update.udo?id=<%=user.getId()%>">编辑</a> | <a href="<%=request.getContextPath()%>delete.udo?id=<%=user.getId()%>">删除</a></td>
    </tr>
    <%
        }
      }
    %>
  </table>
  </body>
</html>

error.jsp

<%--
  Created by IntelliJ IDEA.
  User: why
  Date: 2020/7/22
  Time: 11:52
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>error</title>
</head>
<body>
出错了,操作失败!!!
</body>
</html>

add.jsp

<%--
  Created by IntelliJ IDEA.
  User: why
  Date: 2020/7/22
  Time: 11:42
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>add</title>
    <style type="text/css">
        tr{
            height: 40px;
        }
        td{
            padding: 10px;
        }
    </style>
</head>
<body>
<form action="<%=request.getContextPath()%>/add.udo" method="post">
    <table style="margin: 100px;padding: 50px;border: 1px #ccc solid; width: 400px">
        <tr>
            <td style="text-align: right">用户名:</td>
            <td style="text-align: left"><input type="text" name="username"></td>
        </tr>
        <tr>
            <td style="text-align: right">用户密码:</td>
            <td style="text-align: left"><input type="text" name="password"></td>
        </tr>
        <tr>
            <td style="text-align: right">用户地址:</td>
            <td style="text-align: left"><input type="text" name="address"></td>
        </tr>
        <tr>
            <td style="text-align: right">用户电话:</td>
            <td style="text-align: left"><input type="text" name="phoneNO"></td>
        </tr>

        <tr>
            <td colspan="2" style="text-align:center">
                <input type="submit" value="注册用户">
            </td>
        </tr>
    </table>
</form>
</body>
</html>

web.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
       http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
          version="3.0">

   <servlet>
       <servlet-name>UserController</servlet-name>
       <servlet-class>com.why.study2.controller.UserController</servlet-class>
   </servlet>
   <servlet-mapping>
       <servlet-name>UserController</servlet-name>
<!--       凡是后缀为udo的请求都会发到这里-->
       <url-pattern>*.udo</url-pattern>
   </servlet-mapping>
</web-app>

Cookie

Cookie概述

会话

把同一浏览器与web服务器的一次一系列的各种交互称为会话

对会话的跟踪

Cookie机制:放在客户端

Session机制:放在服务器端

Cookie运行过程

 

  1. 客户端第一次向服务器端http请求,无cookie

  2. 服务端设置客户端Cookie

  3. 再次访问服务端器 ,带Cookie标志

  4. 针对性响应

服务器通过操作 Cookie类对象

对客户端Cookie进行操作。

response.addCookie(Cookie cookie)向客户端设置Cookie

request.getCookie()获取客户端提交的所有Cookie,以Cookie[]数组形式返回

Cookie属性

//cookie名
private final String name;
//cookie值
private String value;
//cookie版本
private int version = 0;
//该cookie的用处说明
private String comment;
//设置访问域名,若设置为.google.com,则所有以.goole.com结尾的域名都可访问
private String domain;
//cookie失效时间,如果为正数则Cookie在>maxAge秒之后失效,若为负数,关闭浏览器时失效
private int maxAge = -1;
//Cookie的使用路径。如果设置为/sessionWeb/在,则只有contextPath为/sessionWeb的程序可访问,若设置为/,则本域名下contextPath都可访问,注意:最后一个字符必须为/
private String path;
//cookie是否仅使用安全协议传输,默认false
private boolean secure;

这些属性都可用seter&geter方法设置和获取

JS操作Cookie的函数

通过JS控制Cookie的设置,获取和删除

<script type="text/javascript">
   //js对Cookie的操作,设置,获取,删除Cookie

   //设置Cookie,c_expiredays失效时间,单位:天。c_name,c_value,必填,c_expiredays选填
   function setCookie(c_name,c_value,c_expiredays) {
       var expires = "";
       if(c_expiredays != null){
           //获得系统时间
           var  sysDate = new Date();
           //浏览器页面弹窗打印
           alert(sysDate.toUTCString());
           //设置时间当前时间加传送的时间
           sysDate.setDate(sysDate.getDate()+c_expiredays);
           alert(sysDate.toUTCString());
           expires = ";expires="+sysDate.toUTCString();
           alert(expires);
      }

       //设置cookie
       document.cookie = c_name + "=" + escape(c_value) + expires;
  }
   alert(document.cookie);

   //获取cookie,通过Cookie名获取Cookie值
   function getCookie(c_name) {
       //判断是否有cookie
       //cookie形式 ssid = abc
       if (document.cookie.length>0){
           //通过子字符串的截取
           //开始截取的位置索引
           var start = document.cookie.indexOf(c_name + "=");
           start = start + c_name.length+1;
           var end = document.cookie.indexOf(";",start);
           if (end == -1){
               end = document.cookie.length;
          }
           var val = document.cookie.substring(start,end);
           return unescape(val);
      }
  }
   alert(getCookie(ssid));

   //删除cookie
   function deleteCookie(c_name) {
       //设置Cookie失效时间是比当前时间更早的时间删除Cookie
       var sysDate = new Date();
       sysDate.setDate(sysDate.getDate()-1);
       var time = ";expires="+sysDate.toUTCString();
       var val = getCookie(c_name);
       document.cookie = c_name+"="+escape(val)+time;
  }
   deleteCookie(ssid);
</script>

Cookie实现用户登录

Cookie可记录用户登录状态

dao层开发

/**
 * 实现根据用户名和密码查询用户记录的方法
 * @param username
 * @param password
 * @return
 */
User getUserByUp(String username, String password);
@Override
public User getUserByUp(String username, String password) {
   String sql = "SELECT id,username,password,phone_no phoneNo,address,reg_date regDate FROM user WHERE username=? and password=?;";
    User user = super.get(sql, username, password);
    return user;
}

Service开发

/**
 * 判断登录是否成功
 * @param username
 * @param password
 * @return
 */
User login(String username, String password);
@Override
public User login(String username, String password) {
   return userDao.getUserByUp(username,password);
}

工具类开发

CookieUtils.java

package com.why.study2.utils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.MessageDigest;
import java.util.Random;

public class CookieUtils {

    /**
     * 命令浏览器创建Cookie文件的方法
     * @param username : 放到Cookie信息的用户名
     * @param req
     * @param resp : 调用Cookie方法的response对象
     * @param sec : 设置Cookie失效时间,单位是秒
     */
    public static void creatCookie(String username, HttpServletRequest req,HttpServletResponse resp,int sec){
        //创建第一个Cookie,标记是userkey,值为username
        Cookie userCookie = new Cookie("userKey",username);

        //创建第二个Cookie,标记是ssid,值为加密方法,此处用ssid保存加密后的用户名,Controller取出比较验证
        Cookie ssidCookie = new Cookie("ssid",md5Encrypt(username));

        /**
         * 设置有效期
         */
        userCookie.setMaxAge(sec);
        ssidCookie.setMaxAge(sec);

        /**
         * 调用添加Cookie
         */
        resp.addCookie(userCookie);
        resp.addCookie(ssidCookie);
    }

    /**
     * 对明文字符串进行加密
     * @param ss : 被加密的明文
     * @return
     */
    public static String md5Encrypt(String ss) {
        Random random = new Random();
        int i1 = random.nextInt(100);
        System.out.println("随机数:"+i1);
        String str1 = createStr(i1);
        System.out.println("密钥:"+str1);
        ss = ss==null?"":ss+str1;

        //加密字典
        char[] md5Digist = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] ssarr = ss.getBytes();
            md.update(ssarr);//把明文放入加密类MessageDigest的对象实例中去,更新数据
            byte[] digest = md.digest();//加密

            int len = digest.length;
            char[] str = new char[2*len];

            int k = 0;//计数
            for (int i = 0; i <len ; i++) {
                byte b = digest[i];
                //>>> 无符号位移右移n位,其余填充0
                //b >>> 4 & 0xf,此处是b右移四位和0xf做与运算,运算结果是0~15
                str[k++] = md5Digist[b >>> 4 & 0xf];
                str[k++] = md5Digist[b & 0xf];
            }

            System.out.println("密文:"+new String(str));
            return new String(str);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 随机生成密钥
     * @param length
     * @return
     */
    public static String createStr(int length){
        String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(62);
            sb.append(str.charAt(number));
        }

        System.out.println(sb.toString());
        return sb.toString();
    }

}

Controller开发

/**
     * 用户登录方法
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    private void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        //第一步,拿到三个参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String expiredays = req.getParameter("expiredays");

        Cookie[] cookies = req.getCookies();

        boolean login = false;//是否登陆的标记
        String account = null;//登录的账号
        String ssid = null;//这是一个标记,通过cookie拿到的一个判断用户该不该登录成功的标记

        if (cookies!=null && cookies.length>0){
            for (Cookie cookie:
                 cookies) {
                if (cookie.getName().equals("userKey")){
                    account = cookie.getValue();
                }
                if (cookie.getName().equals("ssid")){
                    ssid = cookie.getValue();
                }
            }
        }

        if (account!=null && ssid != null){
            login = ssid.equals(CookieUtils.md5Encrypt(username));
            System.out.println(CookieUtils.md5Encrypt(username));
        }

        //login时false取反变成true,表示用户未登录,第一次访问
        if (!login){
            //查询数据库的用户名和密码,判断用户的用户名和密码是否和输入一致
            User user = userService.login(username,password);//登录成功,返回对应的User对象,登录失败,返回null
            if (user !=null){
                //对的,登陆成功
                //设置失效时间
                expiredays =  expiredays == null?"":expiredays;
                switch (expiredays){
                    case "7"://选择了记住我一周
                        //创建Cookie对象,设置失效时间为7天
                        CookieUtils.creatCookie(username,req,resp,7*24*60*60);
                        break;
                    case "30":
                        //创建Cookie对象,设置失效时间为30天
                        CookieUtils.creatCookie(username,req,resp,30*24*60*60);
                        break;
                    case "100":
                        //创建Cookie对象,设置没有失效时间
                        CookieUtils.creatCookie(username,req,resp,Integer.MAX_VALUE);
                        break;
                    default:
                        //默认时创建Cookie对象,设置失效时间为-1,关闭浏览器就失效
                        CookieUtils.creatCookie(username,req,resp,-1);
                        break;
                }

                //登录成功,准许客户进入到main.jsp页面
                req.getRequestDispatcher("/main.jsp").forward(req,resp);
            }else {
                req.setAttribute("note","用户名或密码错误!!!");
                req.getRequestDispatcher("/login.jsp").forward(req,resp);
            }
        }else {
            req.getRequestDispatcher("/main.jsp").forward(req,resp);
        }
    }
}

View层开发

<%--
 Created by IntelliJ IDEA.
 User: why
 Date: 2020/7/23
 Time: 12:07
 To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
   <title>登录</title>
<%--    用js实现自动登录--%>
   <script type="text/javascript">

       //获取cookie,通过Cookie名获取Cookie值
       function getCookie(c_name) {
           //判断是否有cookie
           //cookie形式 ssid = abc
           if (document.cookie.length>0){
               //通过子字符串的截取
               //开始截取的位置索引
               var start = document.cookie.indexOf(c_name + "=");
               start = start + c_name.length+1;
               var end = document.cookie.indexOf(";",start);
               if (end == -1){
                   end = document.cookie.length;
              }
               var val = document.cookie.substring(start,end);
               return unescape(val);
          }
      }

       window.onload = function () {
           //alert();
           //拿到标签id
           var  form = document.getElementById("loginform");
           var username = document.getElementById("username");

           if (getCookie("userKey")!="" && getCookie("userKey")!=null && getCookie("ssid")!="" && getCookie("ssid")!=null){
               username.value  = getCookie("userKey");
               form.submit();
          }
      }
   </script>
</head>
<body>
<br>
<%
   if (request.getAttribute("note")!=null){
%>
<span style="color: red;font-weight: bolder;">
  <%=
       request.getAttribute("note")
  %>
</span>
<%}%>
<br>
<br>
<form id="loginform" action="<%=request.getContextPath()%>/login.udo" method="post">
  用户名:<input id="username" type="text" name="username" value="">
   <br><br>
  密码:<input type="password" name="password">
   <br><br>
  记住我一周<input type="radio" name="expiredays" value="7">
  记住我一个月<input type="radio" name="expiredays" value="30">
  记住我到永远<input type="radio" name="expiredays" value="100">
   <br><br>
   <input type="submit" value="登录">
</form>
</body>
</html>

Session

HttpSession概述

Session记录客户端状态,是服务器端使用的亿终记录客户端状态的机制。

客户端浏览器与服务器之间一系列交互的动作称为一个session,有持续时间

服务端为客户端所开辟的存储空间,可根据键值对获取key和value

通过HttpServletRequest方法获得session对象,服务器自动创建session对象,生成唯一的session id ,内容保存在服务器种,发送到客户端的只有Session id(通过cookie),默认情况下session依赖于Cookie机制来实现

Session相当于程序在服务器上建立的一份客户档案,会给客户自动分配一个编号id,客户来访时只需提供自己的id就可以查询对应客户的档案内容

request还可使用getSession(boolean create)来获取session。区别:若该客户Session不存在,request.getSession()方法会返回null,而getSession(true)会先创建Session再将Session返回

Servlet必须使用request获取session,而jsp内置了session隐藏对象,可直接使用

多个客户端执行程序时,服务器会保存多个Session,Session机制决定了当前客户只会获取到自己的Session,各客户的Session也彼此独立,互不可见。

Session的使用比cookie方便,但过多的session会对服务器造成压力

只有访问jsp或servlet等程序时才会生成session

在web.xml文件中设置失效时间

 

url地址重写

基于cookie不可用的解决方案,如手机浏览器cookie不可用

url地址重写的原理是将该用户的session的id信息重写到url地址中

<%--url地址重写,如果浏览器支持cookie则用原来的url地址,若不支持则会将sessionid重写到url地址中。添加了;jsessionid=XXX,其中XXX为session的id服务器通过url获得session的id--%>
<a href="<%=response.encodeURL("/testServlet.udo")%>">连接<a>

<%--    如果页面进行重定向,url地址重写可以这样写--%>
<%

       String ueserName=null;
       if (("administrator".equals(ueserName))){
           response.sendRedirect(response.encodeRedirectURL("adminisrator.jsp"));
      }
      %>
<%--
   由于手机浏览器大部分不支持cookie,为兼容电脑和手机浏览器禁止cookie,统一使用url地址重写
--%>

Java web规范支持通过配置的方式禁用cookie

在下列目录打开或建立context。xml文件

 

编辑内容如下:

<?xml version="1.0" encoding="utf-8" ?>
<!--禁用cookie-->
<!--path="/sessionionWeb"目录-->

<Context path="/" cookies="false">

</Context>

HttpSession判断用户登录状态

//登录成功,将username放入Session空间
req.getSession().setAttribute("user",user.getUsername());
req.getSession().setAttribute("user",username);
<%
  String username = (String) request.getSession().getAttribute("user");
  //未登录直接去登录界面
  if (username==null||"".equals(username)){
    response.sendRedirect(request.getContextPath()+"/login.jsp");
  }
%>

Session和Cookie做登出功能

前端页面设计

<tr>
  <td colspan="2" style="text-align:center">
    <a href="<%=request.getContextPath()%>/logout.udo">注销用户</a>
  </td>
</tr>

后台逻辑实现Controller

private void logout(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
    //把记录登录状态的cookie删除
    Cookie[] cookies = req.getCookies();
    if (cookies!=null&&cookies.length>0) {
        for (Cookie cookie:
             cookies) {
            if (cookie.getName().equals("userKey")){
                cookie.setMaxAge(0);
                //更新客户端cookie
                resp.addCookie(cookie);
            }
            if (cookie.getName().equals("ssid")){
                cookie.setMaxAge(0);
                resp.addCookie(cookie);
            }
        }
        //把记录登录状态的session删除
    }
    HttpSession session = req.getSession();
    if (session!=null){
        session.removeAttribute("user");
    }
    //退出登录以后跳转到登录界面login.jsp
    resp.sendRedirect(req.getContextPath()+"/login.jsp");
}

Session实现简单的购物车

前端页面开发

productslist.jsp

<%--
  Created by IntelliJ IDEA.
  User: why
  Date: 2020/9/15
  Time: 19:20
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>产品列表</title>
</head>
<body>
    <a href="<%=request.getContextPath()%>/shopping.pdo?pname='联想拯救者i7'">联想拯救者i7</a>
    <br><br>
    <a href="<%=request.getContextPath()%>/shopping.pdo?pname='联想拯救者i8'">联想拯救者i8</a>
    <br><br>
    <a href="<%=request.getContextPath()%>/shopping.pdo?pname='联想拯救者i9'">联想拯救者i9</a>
    <br><br>
    <a href="<%=request.getContextPath()%>/shopping.pdo?pname='联想拯救者i10'">联想拯救者i10</a>
</body>
</html>

productsditals.jsp

<%--
  Created by IntelliJ IDEA.
  User: why
  Date: 2020/9/15
  Time: 19:21
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>产品详情</title>
</head>
<body>
    <%
        String pname = (String) request.getAttribute("p");
        out.println(request.getAttribute("p"));
    %>
    <br>
    拿到其他该产品的详细参数
    <form action="<%=request.getContextPath()%>/addcart.pdo?pname=<%=pname%>">
        <input type="hidden" name="pname" value="<%=pname%>">
        <br>
        <input type="submit" value="加入购物车" style="width: 60px;height: 30px;background: red;color: #cccccc">
    </form>
</body>
</html>

shoppingcart.jsp

<%@ page import="java.util.List" %><%--
  Created by IntelliJ IDEA.
  User: why
  Date: 2020/9/15
  Time: 19:22
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>购物车</title>
</head>
<body>
    <%
        List<String>products = (List<String>) session.getAttribute("car");

        for (String s: products){
            out.println(s+"<br><br>");
        }
    %>
</body>
</html>

Controller开发

ShopController.java

package com.why.study2.controller;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @Pescription:TODO(实现购物车)
 * @Author why
 * @Date 2020/9/15 19:24
 */

public class ShopController extends HttpServlet {

    /**
     * 序列化机制
     */
    private static final long serialVersionUID = 1L;

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

        System.out.println("这里是Post请求处理的方法");

        /**
         * 设置字符集
         */
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");


        //拿到url地址最后一位,包括“/”
        String mn = req.getServletPath();
        //截取除“/”的后几位
        mn = mn.substring(1);
        mn = mn.substring(0,mn.length()-4);
        System.out.println(mn);

        /**
         * 根据反射调用方法
         */
        System.out.println("根据反射调用方法"+mn);
        try {
            Method method = this.getClass().getDeclaredMethod(mn,HttpServletRequest.class,HttpServletResponse.class);
            method.invoke(this,req,resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private void shopping(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        System.out.println("shopping方法");
        String pname = req.getParameter("pname");
        req.setAttribute("p",pname);
        req.getRequestDispatcher("/productditals.jsp").forward(req,resp);

    }

    /**
     * 添加购物车
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    private void addcart(HttpServletRequest req,HttpServletResponse resp)
            throws ServletException, IOException, InterruptedException {


        System.out.println("添加购物车");
        String pname = req.getParameter("pname");
        //添加购物车
        HttpSession session = req.getSession(true);
       List<String> products = (List<String>)session.getAttribute("car");

       if (products==null){
           products = new ArrayList<>();
       }

       products.add(pname);

       session.setAttribute("car",products);

       resp.sendRedirect(req.getContextPath()+"/shoppingcart.jsp");
    }


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

防止表单重复提交

第一种情况

根本原因:服务器处理请求时,bu会检查是否重复提交的请求。

如果对数据库操作会添加许多无意义的操作数据

解决办法

 

 

<%@ page import="java.util.UUID" %><%--
  Created by IntelliJ IDEA.
  User: why
  Date: 2020/9/16
  Time: 15:11
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>表单重复提交</title>
  </head>
  <body>

  <%
    //获取UUID
    String uuid = UUID.randomUUID().toString();
    System.out.println(uuid);

    //uuid放入session
    session.setAttribute("uuid",uuid);
  %>
  <form action="<%=request.getContextPath()%>/SessionTestServlet">

    <input type="hidden" name="token" value="<%=uuid%>">

    <input type="text" name="username" placeholder="用户名">

    <br><br>
    <input type="password" name="password" placeholder="密码">

    <br><br>
    <input type="submit" value="登录">
  </form>
  </body>
</html>
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    System.out.println("doPost方法");
    String token = request.getParameter("token");
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    System.out.println(token+","+username+","+password);

    //取到session中的uuid并将其从session中删除
    HttpSession session = request.getSession();
    String sessionUuid = (String) request.getSession().getAttribute("uuid");
    session.removeAttribute("uuid");

    //比较session中去除的uuid和浏览器传来的uuid是否一致
    if (token.equals(sessionUuid)){
        System.out.println("合法请求,第一次提交表单");
    }else {
        //由于第二次访问未刷新时session中的uuid为空故判断为重复提交
        System.out.println("重复提交表单");
    }
}

第二种情况

网速较慢时用户多次点击提交按钮重复提交

解决办法

通过js让提交按钮点击一次后失效

<script type="text/javascript">
  window.onload=function () {
    var btn = document.getElementById("submitbtn");
    btn.onclick = function () {
      this.disabled = true;//让按钮失效不可用
      this.parentNode.submit();//提交
    }
  }
</script>

HttpSession实现验证码功能

前端页面设计

 

<%@ page import="java.util.UUID" %><%--
  Created by IntelliJ IDEA.
  User: why
  Date: 2020/9/16
  Time: 15:11
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>表单重复提交</title>
    <script type="text/javascript">
      window.onload=function () {
        var btn = document.getElementById("submitbtn");
        btn.onclick = function () {
          this.disabled = true;//让按钮失效不可用
          this.parentNode.submit();//提交
        }
      }
    </script>
  </head>
  <body>

  <%
    //获取UUID
    String uuid = UUID.randomUUID().toString();
    System.out.println(uuid);

    //uuid放入session
    session.setAttribute("uuid",uuid);
  %>
  <form action="<%=request.getContextPath()%>/sessionTest.sdo">

    <input type="hidden" name="token" value="<%=uuid%>">

    <input type="text" name="username" placeholder="用户名">

    <br><br>
    <input type="password" name="password" placeholder="密码">
    
    <br><br>
    <input type="text" value="" name="checkCode" placeholder="验证码">
    <img src="<%=request.getContextPath()%>/drawCheckCode.sdo" alt="验证码">
    <br><br>
    <input type="submit" value="登录" id="submitbtn">
  </form>
  </body>
</html>

Servlet后台响应设计

package com.why.sessionTest;

import com.why.utils.Captcha;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;

/**
 * @Pescription:TODO(防止表单重复提交)
 * @Author why
 * @Date 2020/9/16 15:17
 */
@WebServlet(name = "SessionTestServlet")
public class SessionTestServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

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

        System.out.println("这里是Post请求处理的方法");

        /**
         * 设置字符集
         */
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");


        //拿到url地址最后一位,包括“/”
        String mn = request.getServletPath();
        //截取除“/”的后几位
        mn = mn.substring(1);
        mn = mn.substring(0,mn.length()-4);
        System.out.println(mn);

        /**
         * 根据反射调用方法
         */
        System.out.println("根据反射调用方法"+mn);
        try {
            Method method = this.getClass().getDeclaredMethod(mn,HttpServletRequest.class,HttpServletResponse.class);
            method.invoke(this,request,response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 防止表单重复提交
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void sessionTest(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{
        System.out.println("防止表单重复提交方法");
        String token = request.getParameter("token");
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String ccc = request.getParameter("checkCode");
        System.out.println(token+","+username+","+password+","+ccc);

        //取到session中的uuid并将其从session中删除
        HttpSession session = request.getSession();
        String sessionUuid = (String) request.getSession().getAttribute("uuid");
        session.removeAttribute("uuid");

        //比较session中去除的uuid和浏览器传来的uuid是否一致
        if (token.equals(sessionUuid)){
            System.out.println("合法请求,第一次提交表单");
        }else {
            //由于第二次访问未刷新时session中的uuid为空故判断为重复提交
            System.out.println("重复提交表单");
        }

        String scc = (String) session.getAttribute("checkCode");

        if (ccc.equals(scc)){
            System.out.println("验证码正确");
        }else {
            System.out.println("验证码输入有误");
            request.getRequestDispatcher("/index.jsp").forward(request,response);
        }
    }
    /**
     * 画验证码的方法
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void drawCheckCode(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{

        System.out.println("画验证码方法");
        //设置响应文件的类型
        response.setContentType("image/jpg");

        //设置图片宽高
        int width = 100;
        int height = 30;

        //具体画验证码,调用工具类
        Captcha captcha = Captcha.getInstance();
        captcha.set(width,height);
        String cc = captcha.generaCheckcode();
        HttpSession session = request.getSession();
        session.setAttribute("checkCode",cc);
        OutputStream out = response.getOutputStream();
        ImageIO.write(captcha.generateCheckImg(cc),"jpg",out);
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

画验证码工具类

package com.why.utils;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Objects;
import java.util.Random;

/**
* @Pescription:TODO(画验证码工具类)
* @Author why
* @Date 2020/9/18 9:35
*/
public class Captcha {

   private int width;//验证码宽
   private int height;//高
   private int num;//验证码字符个数
   private String code;//验证码字典
   private static final Random ran = new Random();//取随机数对象
   private static Captcha captcha;//单例模式

   private Captcha() {
       code = "0123456789";
       num = 4;
  }

   public int getWidth() {
       return width;
  }

   public void setWidth(int width) {
       this.width = width;
  }

   public int getHeight() {
       return height;
  }

   public void setHeight(int height) {
       this.height = height;
  }

   public int getNum() {
       return num;
  }

   public void setNum(int num) {
       this.num = num;
  }

   public String getCode() {
       return code;
  }

   public void setCode(String code) {
       this.code = code;
  }

   public static Random getRan() {
       return ran;
  }

   public static Captcha getCaptcha() {
       return captcha;
  }

   public static void setCaptcha(Captcha captcha) {
       Captcha.captcha = captcha;
  }

   @Override
   public boolean equals(Object o) {
       if (this == o) return true;
       if (o == null || getClass() != o.getClass()) return false;
       Captcha captcha = (Captcha) o;
       return width == captcha.width &&
               height == captcha.height &&
               num == captcha.num &&
               Objects.equals(code, captcha.code);
  }

   @Override
   public int hashCode() {
       return Objects.hash(width, height, num, code);
  }


   /**
    * 单例模式获取对象的方法
    * @return
    */
   public static Captcha getInstance(){
       if (captcha == null) captcha = new Captcha();
       return captcha;
  }

   @Override
   public String toString() {
       return "Captcha{" +
               "width=" + width +
               ", height=" + height +
               ", num=" + num +
               ", code='" + code + '\'' +
               '}';
  }

   /**
    * 设置验证码宽高数字和字典
    * @param width
    * @param height
    * @param num
    * @param code
    */
   public void set(int width, int height, int num, String code){
       this.width = width;
       this.height = height;
       this.setNum(num);
       this.setCode(code);
  }

   /**
    * 设置宽高
    * @param width
    * @param height
    */
   public void set(int width,int height){
       this.width = width;
       this.height = height;
  }

   /**
    * 生成验证码
    * @return
    */
   public String generaCheckcode(){
       StringBuffer cc = new StringBuffer();
       for (int i = 0; i < num; i++) {
           cc.append(code.charAt(ran.nextInt(code.length())));
      }
       return cc.toString();
  }

   /**
    * 画图
    * @param checkcode
    * @return
    */
   public BufferedImage generateCheckImg(String checkcode){
       //创建一个图片对象
       BufferedImage img = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
       //获取图片对象的画笔
       Graphics2D graphic = img.createGraphics();
       graphic.setColor(Color.WHITE);
       graphic.fillRect(0,0,width,height);
       graphic.setColor(Color.BLACK);
       graphic.drawRect(0,0,width-1,height-1);
       Font font = new Font("宋体",Font.BOLD+Font.ITALIC,(int)(height*0.8));
       graphic.setFont(font);
       for (int i = 0; i < num; i++) {
           graphic.setColor(new Color(ran.nextInt(155),ran.nextInt(255),ran.nextInt(255)));
           graphic.drawString(String.valueOf(checkcode.charAt(i)),i*(width/num)+4,(int)(height*0.8));
      }

       //加一些点
       for (int i = 0; i < (width+height); i++) {
             graphic.setColor(new Color(ran.nextInt(255),ran.nextInt(255),ran.nextInt(255)));
             graphic.drawOval(ran.nextInt(width),ran.nextInt(height),1,1);
      }

       //加一些线
       for (int i = 0; i < 2; i++) {
           graphic.setColor(new Color(ran.nextInt(255),ran.nextInt(255),ran.nextInt(255)));
           graphic.drawLine(0,ran.nextInt(height),width,ran.nextInt(height));
      }

       return img;
  }
}

Java Bean

Java Bean的理解

JavaBean的简单理解

在MVC设计模型中的model,又称模型层,一般的程序中称为数据层。用来设置数据的属性和行为,提供获取属性和设置属性的get/set方法。

JavaBenan就是一个类,是为了和jsp页面传数据化简交互过程而产生的。

使用JavaBean后,又是就是Java的优势,组件技术,代码重用,易于维护。

什么是JavaBean

JavaBean是一个遵循特定写法的Java类,它通常具有如下特点:

  • 这个Java类必须具有一个无参的构造函数

  • 属性必须私有化

  • 私有化的属性必须通过public的方法暴露给其它程序,并且方法遵守一定的命名规范。

主要作用:

区别于传统获取数据的方法:

String username = req.getParameter("username");

现在直接使用

BeanUtils.populate(user, req.getParameterMap());

获取数据

设计思路

前端:

<%--
 Created by IntelliJ IDEA.
 User: why
 Date: 2020/9/18
 Time: 17:01
 To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
 <head>
   <title>首页</title>
 </head>
 <body>
   <form action="<%=request.getContextPath()%>/login.xdo" method="post">
    用户名:<input type="text" name="username">
     <br><br>
    密码:<input type="password" name="password">
     <br><br>
    爱好:<input type="checkbox" name="hobby" value="basketball">篮球
     <input type="checkbox" name="hobby" value="pq">排球
     <input type="checkbox" name="hobby" value="swim">游泳

     <br><br>
     <input type="submit" value="登录">
   </form>
 </body>
</html>

后端:

package com.why.javabean.controller;

import com.why.javabean.entity.User;
import org.apache.commons.beanutils.BeanUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;

/**
* @Pescription:TODO(用户控制层)
* @Author why
* @Date 2020/9/18 17:16
*/
public class UserController extends HttpServlet {

   private static final long serialVersionUID = 1L;

   @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 {
       System.out.println("doPost方法");

       User user = new User();
       System.out.println("分装前user对象:"+user);

       try {
           BeanUtils.populate(user, req.getParameterMap());
      } catch (Exception e) {
           e.printStackTrace();
      }

       System.out.println("封装后User对象:"+user);
  }
}
package com.why.javabean.entity;

import java.util.Arrays;
import java.util.Objects;

/**
* @Pescription:TODO(用户实体类)
* @Author why
* @Date 2020/9/18 17:05
*/
public class User {
   private String username;
   private String password;
   private String[] hobby;


   public User() {
  }

   public User(String username, String password, String[] hobby) {
       this.username = username;
       this.password = password;
       this.hobby = hobby;
  }

   public String getUsername() {
       return username;
  }

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

   public String getPassword() {
       return password;
  }

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

   public String[] getHobby() {
       return hobby;
  }

   public void setHobby(String[] hobby) {
       this.hobby = hobby;
  }

   @Override
   public String toString() {
       return "User{" +
               "username='" + username + '\'' +
               ", password='" + password + '\'' +
               ", hobby=" + Arrays.toString(hobby) +
               '}';
  }

   @Override
   public boolean equals(Object o) {
       if (this == o) return true;
       if (o == null || getClass() != o.getClass()) return false;
       User user = (User) o;
       return Objects.equals(username, user.username) &&
               Objects.equals(password, user.password) &&
               Arrays.equals(hobby, user.hobby);
  }

   @Override
   public int hashCode() {
       int result = Objects.hash(username, password);
       result = 31 * result + Arrays.hashCode(hobby);
       return result;
  }
}

依赖的jar包

 

JavaBean的属性

Java Bean的属性可是任意类型,并且一个JavaBean可以有多个属性。每个属性通常都有setter和getter方法。

setter称为属性修改器,getter称为属性访问器。

一个Java Bean的某个属性可只有set或get方法,这样的属性也称为只写、只读属性。

JavaBean在JSP的使用(只做了解)

JSP技术提供了三个关于JavaBean组件的动作元素,即JSP标签,分别为:

  • jsp:useBean标签,用于JSP页面中查找或实例化一个JavaBean组件

  • jsp:setProperty标签,用于在JSP页面设置一个JavaBean组件的属性

  • jsp:getProperty标签,用于在jsp页面中获取一个JavaBean组建的属性

常用语法

<jsp:useBean id = “beanName” class = "package.class" scope = "page|request|session|application"/>

id = “beanName”:Java类名

class = "package.class":JavaBean的包全名

scope = "page|request|session|application"/:作用范围,默认page,当前页面

标签

EL标签的基本内容介绍

EL(Expression Language)标签,特点:使用方便

主要语法结构:${sessionScope.user.sex}

所有EL标签都是以”${“开始,以”}“结束

此句的意思是:从session范围内取得用户的性别。

原写法:

User user = (User)session.getAttribute("user");
String sex = user.getSex();
out.println(sex);

举例

<%
      User user = new User();
      user.setUsername("why");
      session.setAttribute("user",user);
    %>

<%--    EL标签取出session里的user里的username--%>
    ${sessionScope.user.username}

.与[]运算符

EL提供.和[]两种运算符来盗汗数据,下拉两者表达意义相同:

  • ${sessionScope.user.sex}

  • ${sessionScope.user[“sex”]}

.和[]可以同时混合使用,如下:

${sessionScope.shoppingCart[0].price},意义为:回传结果为shoppingCart中第一项物品的价格。

一下两种两者会有差异:

  1. 当要存取的属性名称包含一些特殊字符,如,. 或 - 等并非字母或数字的符号,就一定要使用[],例如:${user.My-Name}上述是不正确的方式,应改为:${user[“My-Name”]}

  2. 如果user是一个动态的变量用[]实现

EL变量

EL只能取到四大域对象中的值,session,page,request,application

EL存取变量简单,如,${username}.他的意思是取出某一范围在名为username的百年来。由于没有指定范围,它存取的顺序为page,request,session,application,途中找到username直接回传,未找到返回null,但是可以做出优化,页面上显示空白。

 

我们也可以指定范围进行存取

 

 

自动转变类型

特点:自动转变类型

${param.count+20}

加入count是int型,则整个式子为int型,jsp回传的都为String类型

EL隐含对象

 

 

注意:

如果要用EL输出一个常量的话,字符串要加双引号,不然回默认当作一个变量去输出,若在四大域对象中不存在,则输出null

属性(Attribute)和范围(Scope)

 

cookie

 

header和headerValues

 

initParam

 

取出web.xml的初始化参数

pageConteext

 

empty运算符判断是否为空

&{empty requestScope.req}

false表示有值,true表示为空

自定义第一个简单标签

jdk中Tag接口提供自定义标签接口

标签开发时继承SimpleTagSupport类

自定义标签的开发步骤

  1. 编写标签功能的Java类(标签处理器),继承SimpleTagSupport类

    package com.why.javabean.tag;
    
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.tagext.SimpleTagSupport;
    import java.io.IOException;
    
    /**
     * @Pescription:TODO(自定义标签处理器)
     * @Author why
     * @Date 2020/9/19 10:30
     */
    public class MyTag extends SimpleTagSupport {
    
        @Override
        public void doTag() throws JspException, IOException {
            //返回使用我自己定义标签的jsp页面的page域对象
            //通过这个域对象,可以网jsp页面写东西让他显示出来
            getJspContext().getOut().println("Hello Tag!");
        }
    }
  2. 编写标签可描述文件(.tld)

     

    <?xml version="1.0" encoding="UTF-8"?>
    <taglib 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-jsptaglibrary_2_0.xsd"
            version="2.0">
        <!--    标签的信息描述-->
        <description>MyTag library exercising SimpleTag handlers.</description>
        <!--    标签版本-->
        <tlib-version>1.0</tlib-version>
        <!--    标签名-->
        <short-name>MyTag</short-name>
        <!--    标签的id值-->
        <uri>http://mytag.com/core</uri>
        <tag>
            <description>Outputs Hello, World</description>
            <name>HelloWorld</name>
            <!--        包全名-->
            <tag-class>com.why.javabean.tag.MyTag</tag-class>
            <body-content>empty</body-content>
        </tag>
    </taglib>
  3. 在jsp页面导入和使用自定义标签

    首先在jsp引入标签库,格式如下:

    <%--prefix="指定标签前缀" uri="配置文件配置的uri"--%>
    <%@ taglib prefix="mytag" uri="http://mytag.com/core" %>

 

使用标签

<%--    EL标签取出session里的user里的username--%>
    ${sessionScope.user.username}

自定义一个带属性的标签

第一步编写标签库类

Controller控制业务逻辑转发

/**
 * 浏览器上index.do会访问到此方法
 * @param req
 * @param resp
 * @throws ServletException
 * @throws IOException
 */
private void index(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
    Map<String,Integer> map = new HashMap<>();
    map.put("张三",20);
    map.put("李四",21);
    map.put("小白",22);

    //放入request请求域对象
    req.setAttribute("map",map);

    req.getRequestDispatcher("/index.jsp").forward(req,resp);
}

编写标签库

package com.why.tag.tags;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.util.Map;

/**
 * @Pescription:TODO(自定义带属性的标签)
 * @Author why
 * @Date 2020/9/27 17:41
 */
public class MyTag extends SimpleTagSupport {

    private String map;

    public String getMap() {
        return map;
    }

    public void setMap(String map) {
        this.map = map;
    }

    /**
     * 重写dotag方法,实现标签功能
     * @throws JspException
     * @throws IOException
     */
    @Override
    public void doTag() throws JspException, IOException {

        System.out.println(this.map);
        //拿到jsp页面pageContext域对象
        PageContext pageContext = (PageContext) getJspContext();

        //拿到request域对象
        HttpServletRequest hreq = (HttpServletRequest) pageContext.getRequest();

        //此时可获得request中的map
        Map<String,Integer> maps = (Map<String, Integer>) hreq.getAttribute("map");

        //循环遍历map
        for (String key:maps.keySet()
             ) {
            pageContext.getOut().print("<tr>");
            pageContext.getOut().print("<td>");
            pageContext.getOut().print(key);
            pageContext.getOut().print("</td>");
            pageContext.getOut().print("<td>");
            pageContext.getOut().print(maps.get(key));
            pageContext.getOut().print("</td>");
            pageContext.getOut().print("</tr>");
        }
    }
}

第二步配置标签库

<?xml version="1.0" encoding="UTF-8"?>
<taglib 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-jsptaglibrary_2_0.xsd"
        version="2.0">
    <!--    标签的信息描述-->
    <description>MyTag library exercising SimpleTag handlers.</description>
    <!--    标签版本-->
    <tlib-version>1.0</tlib-version>
    <!--    标签短名-->
    <short-name>MyTag</short-name>
    <!--    标签的id值-->
    <uri>http://mytag.com/core</uri>

    <!--    具体的标签-->
    <tag>
        <!--        标签描述-->
        <description>output map</description>
        <!--        标签名-->
        <name>myTag</name>
        <!--        包全名-->
        <tag-class>com.why.tag.tags.MyTag</tag-class>
        <!--        单标签-->
        <body-content>empty</body-content>
        <!--        属性-->
        <attribute>
            <name>map</name>
            <!--            表示此属性必须要-->
            <required>true</required>
            <!--            表示可以接受表达式-->
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>

第三步使用标签

<table border="1" cellpadding="0" cellspacing="0">
  <mt:myTag map="map"/>
</table>

自定义带标签体的标签

 

package com.why.tag.tags;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.IOException;
import java.util.Map;

/**
 * @Pescription:TODO(自定义带属性的标签)
 * @Author why
 * @Date 2020/9/27 17:41
 */
public class MyTag extends SimpleTagSupport {

    private String map;

    public String getMap() {
        return map;
    }

    public void setMap(String map) {
        this.map = map;
    }

    /**
     * 重写dotag方法,实现标签功能
     * @throws JspException
     * @throws IOException
     */
    @Override
    public void doTag() throws JspException, IOException {

        System.out.println(this.map);
        //拿到jsp页面pageContext域对象
        PageContext pageContext = (PageContext) getJspContext();

        //拿到request域对象
        HttpServletRequest hreq = (HttpServletRequest) pageContext.getRequest();

        //此时可获得request中的map
        Map<String,Integer> maps = (Map<String, Integer>) hreq.getAttribute("map");

        //循环遍历map
        for (String key:maps.keySet()
             ) {
            pageContext.setAttribute("name",key);
            pageContext.setAttribute("age",maps.get(key));
            /**
             * 表示不做处理直接写入
             * getJspBody()表示获取整个标签体的所有内容,返回fragment对象,其中invoke()方法就是用于输出整个内容到jsp页面,如果参数为null表示直接输出
             */
            getJspBody().invoke(null);
        }
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<taglib 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-jsptaglibrary_2_0.xsd"
        version="2.0">
    <!--    标签的信息描述-->
    <description>MyTag library exercising SimpleTag handlers.</description>
    <!--    标签版本-->
    <tlib-version>1.0</tlib-version>
    <!--    标签短名-->
    <short-name>MyTag</short-name>
    <!--    标签的id值-->
    <uri>http://mytag.com/core</uri>

    <!--    具体的标签-->
    <tag>
        <!--        标签描述-->
        <description>output map</description>
        <!--        标签名-->
        <name>myTag</name>
        <!--        包全名-->
        <tag-class>com.why.tag.tags.MyTag</tag-class>
        <!--        双标签-->
        <body-content>scriptless</body-content>
        <!--        属性-->
        <attribute>
            <name>map</name>
            <!--            表示此属性必须要-->
            <required>true</required>
            <!--            表示可以接受表达式-->
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>
<table border="1" cellpadding="0" cellspacing="0">
  <mt:myTag map="map111">
    <tr>
      <td>${name}</td>
      <td>${age}</td>
    </tr>
  </mt:myTag>
</table>

属性只能使用基本数据类型,对于复杂的类型,如date等,建议将该对象至于本页共性范围,然后标签处理类可直接获取并做相应的处理

JSTL标签

JSTL标签简介和一般行为

 

 

设置JSTL运行环境

 

 

 

 

JSTL标签的使用

 

自定义标签使用

 

 

JSTL核心标签

 

 

c:out标签

显示输出

 

 

举例:

<c:out value="jstlTag" default="默认值,输出结果为null的时候会输出此值"></c:out>
c:set标签

给变量赋值

语法 :

 

 

 

举例:

<c:set  var="abc" value="字母" scope="session"/>

<%
  out.println(session.getAttribute("abc"));
%>
<c:set target="${user}" property="username" value="小白"/>

target:目标对象

property:目标对象的属性名

c:remove标签

用来一处某个范围变量的内容值

举例:

<c:remove var="user" scope="page"/>
remove之后:<c:out value="${pageScope.user.username}"/>

JSTl与url相关的标签

c:url标签

 

 

语法

 

 

举例

<%--  单标签,构造一个url地址,往往写在href属性中,context=""指定其他工程名--%>
    <c:url value="http://ww.baidu.com" var="url" scope="page" />
    <c:out value="${pageScope.url}"/>

    <br>
<%--  双标签--%>
    <c:url value="${url}" var="uri" scope="page">
<%--      以问号的形式传递参数--%>
      <c:param name="id" value="123"/>
    </c:url>
  <c:out value="${pageScope.uri}"/>

  <br>
  <a href="${url}">百度</a>
c:import标签

 

<%--  导入外部的资源,带入body中--%>
    <c:import url="http://www.baidu.com" charEncoding="utf-8"/>
c:redirect标签——网页重定向

 

<%--  跳转--%>
    <c:redirect url="http://www.baidu.com"/>


    <c:redirect url="http://www.baidu.com">
<%--      跳转带参数--%>
      <c:param name="id" value="123456"/>
    </c:redirect>

JSTL的分支标签

c:if标签

 

举例

<%--  如同Java的if语句,test="${3>2}"表达式判断真假,var="rs"保存test的判断结果--%>
  <c:if test="${3>2}" var="rs"/>
  <c:out value="${rs}"/>

  <br>
  <c:if test="${3>2}" var="rd">
    test结果为true显示此处内容
  </c:if>
c:choose标签 when与otherwise

 

举例

<c:set var="var" value="1"/>
<c:choose>
  <c:when test="${var==1}">
    2>1
  </c:when>
  <c:when test="${var==2}">
    3>2
  </c:when>
  <c:when test="${var==3}">
    4>3
  </c:when>
  <c:otherwise>
    5>4
  </c:otherwise>
</c:choose>

JSTL循环迭代标签

C:forEach标签

语法

 

 

 

用法一:

<%--  items="${requestScope.user}"遍历的对象,var="u"每次遍历后赋值到此--%>
  <c:forEach items="${user}" var="u">
    <c:out value="${u}"/>
    <br><br>
  </c:forEach>
  • var:每次从集合中拿出的数据,放进var指定的变量中

  • items:注入到jsp页面的集合类数据

  • varStatus:status封装了当前遍历状态 ,比如,可从该对象上查看是遍历到第几个元素:${status.count}

     

     

<table border="1" cellspacing="0" cellpadding="0">
  <c:forEach var="s" items="${user}" varStatus="status">
    <tr>
      <td>现在循环到第${status.count}个了</td>
      <td>${s}</td>
    </tr>
  </c:forEach>

 

用法二:

单纯的循环

<c:forEach var="vvv" begin="1" end="10">
  ${vvv}<br>
</c:forEach>

设置begin和end区间

c:forTokens标签

处理字符串

 

用法

<%--    items="google,runoob,taobao",需要处理的字符串--%>
<%--    delims=",",以特定的符号分割--%>
<%--    var="name"分割后的赋值变量--%>
    <c:forTokens items="google,runoob,taobao" delims="," var="name">
      <c:out value="${name}"/>
    </c:forTokens>

JSTL格式化和函数标签

格式化日期

 

用法:

引入标签

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%--    value="${date}",要格式化的日期--%>
<%--    dateStyle="long",年月日样式--%>
<%--    timeStyle="long",时间样式--%>
<%--    pattern="yyyy-MM-dd HH:mm:ss",自定义显示样式--%>
    格式化之后:
    <fmt:formatDate value="${date}" dateStyle="long" timeStyle="long" type="time" pattern="yyyy-MM-dd HH:mm:ss"/>
函数标签

 

引入标签

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

 

 

用法

 

 

 

 

用法

引入标签

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

过滤器Filter

Filter的基本用法

简介

 

 

Filter的实现

 

 

Filter的生命周期

 

Filter实例

新建fiter继承Servlet中的Filter接口

package com.why.filter;

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

/**
 * @Pescription:TODO(Filter过滤器实例)
 * @Author why
 * @Date 2020/10/1 9:25
 */
public class DemoFilter  implements Filter {
    @Override
    public void init(FilterConfig config) throws ServletException {
        System.out.println("init");
        /**
         * 获取初始化值
         */
        String username = config.getInitParameter("username");
        System.out.println(username);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("doFilter");
        
        /**
         * 这句话很关键
         * 是拦截器的请求链放行到服务器或到客户端
         */
        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {
        System.out.println("destroy");
    }
}

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

    <filter>
        <filter-name>DemoFilter</filter-name>
        <filter-class>com.why.filter.DemoFilter</filter-class>
<!--        配置初始化参数-->
        <init-param>
            <param-name>username</param-name>
            <param-value>张三</param-value>
        </init-param>
    </filter>
    
<!-- 哪个在前哪个先执行-->
    <filter-mapping>
        <filter-name>DemoFilter</filter-name>
<!--        所有页面全部拦截-->
        <url-pattern>/*</url-pattern>
<!--        <servlet-name></servlet-name>,拦截某个servlet-->
<!--        指定拦截请求类型,默认request请求,FORWARD转发请求,可指定多种-->

        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>
</web-app>

FilterConfig使用

Filter 的 init 方法中提供了一个 FilterConfig 对象。

类似于Servlet中init中的ServletConfig

<filter>
    <filter-name>LogFilter</filter-name>
    <filter-class>com.runoob.test.LogFilter</filter-class>
    <init-param>
        <param-name>Site</param-name>
        <param-value>why</param-value>
    </init-param>
</filter>
public void  init(FilterConfig config) throws ServletException {
    // 获取初始化参数
    String site = config.getInitParameter("Site"); 
    // 输出初始化参数
    System.out.println("网站名称: " + site); 
}

根据规范建议地各种类型的过滤器

  1. 身份验证过滤器(Authentication Filters)

  2. 数据压缩过滤器(Data compression Filters)

  3. 加密过滤器(Encryption Filters)

  4. 触发资源访问事件过滤器

  5. 图像转换过滤器(Image Conversion Filters)

  6. 日志记录和审核过滤器(Logging and Auditing Filters)

  7. MIME-TYPE 链过滤器(MIME-TYPE Chain Filters)

  8. 标记化过滤器(Tokenizing Filters)

  9. XSL/T 过滤器(XSL/T Filters),转换 XML 内容

HttpFilter

package com.why.filter;

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

/**
 * @Pescription:TODO(HttpFilter实例)
 * @Author why
 * @Date 2020/10/1 10:17
 */
public class DemoHttpFilter extends HttpFilter {

    @Override
    public void init() throws ServletException {
        System.out.println("http init");
    }

    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("http doFilter");
        
        chain.doFilter(request,response);
    }
}

利用过滤器禁用浏览器缓存

 

package com.why.filter;

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

/**
 * @Pescription:TODO(禁用浏览器缓存)
 * @Author why
 * @Date 2020/10/1 10:43
 */
public class HCFilter extends HttpFilter {
    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        response.setHeader("Cache-Control","no-cache");
        response.setHeader("Pragma","no-cache");
        response.setDateHeader("Expires",-1);
        chain.doFilter(request,response);
    }
}

利用Filter实现自动登录

package com.why.study2.filter;

import com.why.study2.utils.CookieUtils;

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

/**
 * @Description TODO自动登录
 * @Author why
 * @Date 2020/10/4 15:57
 * Version 1.0
 **/
public class AutoLoginFilter extends HttpFilter {

    @Override
    public void destroy() {

    }

    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("AutoLoginFilter");
        //自动登录
        //基本思路判断请求中有没有cookie,userKey和ssid是否正确
        //跳转main.jsp页面,同时把userKey的值放入session
        //没有cookie,第一次登陆,跳转转发他的请求,让页面正常执行登录操作

        //拿到所有cookie对象
        javax.servlet.http.Cookie[] cookies = request.getCookies();
        String username = null;
        String ssid = null;
        if (cookies != null && cookies.length>0){
            for (Cookie c:cookies
            ) {
                if (c.getName().equals("userKey")){
                    username = c.getValue();
                }
                if (c.getName().equals("ssid")){
                    ssid = c.getValue();
                }
            }
            if (username != null && ssid != null && ssid.equals(CookieUtils.md5Encrypt(username))){//true:证明用户登陆过,且选择记住我,自动登录
                HttpSession session = request.getSession();
                session.setAttribute("user",username);
                response.sendRedirect(request.getContextPath()+"/main.jsp");//实现了自动登录
            }else {
                chain.doFilter(request,response);
            }
        }else {
            chain.doFilter(request,response);//放行正常执行
        }
    }
}
<filter>
    <filter-name>AutoLoginFilter</filter-name>
    <filter-class>com.why.study2.filter.AutoLoginFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>AutoLoginFilter</filter-name>
    <url-pattern>/login.jsp</url-pattern>
</filter-mapping>

利用Filter控制登录后才能访问的URL

Filter的编写

package com.why.study2.filter;

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

/**
 * @Description TODO 判断是否登录,控制访问url
 * @Author why
 * @Date 2020/10/9 9:58
 * Version 1.0
 **/
public class IsLoginFilter extends HttpFilter {
    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        //拿到地址
        String path = request.getServletPath().substring(1);
        //拿到初始化值
        String authority = getFilterConfig().getInitParameter("authority");
        String[] strArr = authority.split(",");
        String noAuthority = getFilterConfig().getInitParameter("noAuthority");
        String[] noAutoArr = noAuthority.split(",");
        for (String str:noAutoArr){
            if (path.equals(str)){
                chain.doFilter(request,response);
            }
        }
        HttpSession session = request.getSession();
        for (String str: strArr
             ) {
            if (str.equals(path)){
                //权限访问控制
                String username = (String) session.getAttribute("user");
                if (username!=null){
                    chain.doFilter(request,response);
                }else{
                    //不能访问跳转去登录
                    response.sendRedirect(request.getContextPath()+"/login.jsp");
                }
            }
        }
    }

    @Override
    public void destroy() {

    }
}

web.xml的配置

 <filter>
        <filter-name>IsLoginFilter</filter-name>
        <filter-class>com.why.study2.filter.IsLoginFilter</filter-class>
        <init-param>
<!--            配置需要过滤掉页面和地址-->
            <param-name>authority</param-name>
            <param-value>main.jsp,add.jsp,add.udo,query.udo,delete.udo</param-value>
        </init-param>
        <init-param>
            <param-name>noAuthority</param-name>
            <param-value>login.jsp,login.udo,logout.udo,error.jsp,online.udo</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>IsLoginFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Listeener监听器

监听器介绍

image-20201006191455572

使用监听器步骤

image-20201006191527260

image-20201006191546516

image-20201006191603374

监听器常用接口

image-20201006191853411

ServletContex对象的监听器

ServletContextListener

image-20201006192245912

实现类

package com.why.listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

/**
 * @Description TODO 实现ServletContextListener接口监听器的方法
 * @Author why
 * @Date 2020/10/6 19:29
 * Version 1.0
 **/
public class ServletContextListenerImpl implements ServletContextListener {
    /**
     *@Description: contextInitialized方法会在web应用服务器程序启动的时候触发
     *@param: ServletContextEvent sce
     *@return: void
     *@auther: why
     *@date: 2020/10/6 19:30
     */
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("服务器启动...");
    }

    /**
     *@Description: contextDestroyed方法会在web应用服务器程序关闭的时候触发
     *@param: ServletContextEvent sce
     *@return: void
     *@auther: why
     *@date: 2020/10/6 19:33
     */
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("服务器关闭...");
        ServletContext sc = sce.getServletContext();
        //拿到代表整个web应用程序对象的引用
        System.out.println(sc.getServletContextName());
    }
}

web.xml的配置

<!--    注册监听器-->
    <listener>
        <listener-class>com.why.listener.ServletContextListenerImpl</listener-class>
    </listener>
ServletContextAttributeListener

image-20201006195054697

Java类

package com.why.listener;

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;

/**
 * @Description TODO ServletContextAttributeListener实现类
 * @Author why
 * @Date 2020/10/6 19:51
 * Version 1.0
 **/
public class ServletContextAttributeListenerImpl implements ServletContextAttributeListener {
    /**
     *@Description: attributeAdded方法是在ServletContext范围里,添加属性的时刻会被出发
     *@param: ServletContextAttributeEvent scae
     *@return: void
     *@auther: why
     *@date: 2020/10/6 19:52
     */
    @Override
    public void attributeAdded(ServletContextAttributeEvent scae) {
        System.out.println("ServletContext添加了属性");
        //拿到新添加的属性名
        String name = scae.getName();
        System.out.println("新添加的属性名:"+name);
        //新添加的属性值
        Object value = scae.getValue();
        System.out.println("新添加的属性值:"+value);
    }

    /**
     *@Description: attributeRemoved方法是移除ServletContext被触发
     *@param:
     *@return: void
     *@auther: why
     *@date: 2020/10/6 19:55
     */
    @Override
    public void attributeRemoved(ServletContextAttributeEvent scae) {
        System.out.println("ServletContext有键值对被删除");
        //被移除的属性名
        String name = scae.getName();
        System.out.println("被移除的属性名:"+name);
        //被移除的属性值
        Object value = scae.getValue();
        System.out.println("被移除的属性值:"+value);
    }

    /**
     *@Description: attributeReplaced方法是ServletContext里的属性被替换
     *@param:
     *@return: void
     *@auther: why
     *@date: 2020/10/6 19:57
     */
    @Override
    public void attributeReplaced(ServletContextAttributeEvent scae) {
        System.out.println("ServletContext有属性被替换");
        //被替换的属性名
        String name = scae.getName();
        System.out.println("被替换的属性名:"+name);
        //被替换的属性值
        Object value = scae.getValue();
        System.out.println("被替换的属性值:"+value);
    }
}

web.xml

<listener>
    <listener-class>com.why.listener.ServletContextAttributeListenerImpl</listener-class>
</listener>

index

<%
  application.setAttribute("name1","小白111");
  application.removeAttribute("name1");
%>

Request对象的监听器

image-20201006201002625

image-20201006201257252

image-20201006201335864

ServletRequestListener & ServletRequestAttribute

Java实现类

package com.why.listener;

import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;

/**
 * @Description TODO ServletRequestListener实现类
 * 监听器可以同时实现多个接口
 * @Author why
 * @Date 2020/10/6 20:14
 * Version 1.0
 **/
public class ServletRequestListenerImpl implements ServletRequestListener, ServletRequestAttributeListener {
    /**
     *@Description: attributeAdded方法是request域对象里添加了新属性
     *@param:
     *@return: void
     *@auther: why
     *@date: 2020/10/6 20:17
     */
    @Override
    public void attributeAdded(ServletRequestAttributeEvent srae) {
        System.out.println("添加属性");
        System.out.println(srae.getName());
        System.out.println(srae.getValue());
    }

    /**
     *@Description: attributeRemoved方法是request域对象移除了属性
     *@param:
     *@return: void
     *@auther: why
     *@date: 2020/10/6 20:18
     */
    @Override
    public void attributeRemoved(ServletRequestAttributeEvent srae) {
        System.out.println("移除属性");
        System.out.println(srae.getName());
        System.out.println(srae.getValue());
    }

    /**
     *@Description: attributeReplaced方法是request域对象替换了某属性
     *@param:
     *@return: void
     *@auther: why
     *@date: 2020/10/6 20:18
     */
    @Override
    public void attributeReplaced(ServletRequestAttributeEvent srae) {
        System.out.println("替换属性");
        System.out.println(srae.getName());
        System.out.println(srae.getValue());
    }

    /**
     *@Description: requestDestroyed方法是request请求销毁时被触发
     *@param:
     *@return: void
     *@auther: why
     *@date: 2020/10/6 20:17
     */
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("请求销毁...");
    }

    /**
     *@Description: requestInitialized方法是request请求时被触发
     *@param:
     *@return: void
     *@auther: why
     *@date: 2020/10/6 20:16
     */
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("请求监听器启动...");
    }
}

web.xml配置

<listener>
    <listener-class>com.why.listener.ServletRequestListenerImpl</listener-class>
</listener>

jsp页面

<%
 request.setAttribute("name","why");
 request.setAttribute("name","wyy");
 request.removeAttribute("name");
%>

Session对象的监听器

image-20201006203151580

image-20201006203319061

HttpSessionListener & HttpSessionAttributeListener

Java实现类

package com.why.listener;

import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 * @Description TODO HttpSessionListener实现类
 * @Author why
 * @Date 2020/10/6 20:33
 * Version 1.0
 **/
public class HttpSessionListenerImpl implements HttpSessionListener, HttpSessionAttributeListener {
    /**
     *@Description: attributeAdded方法是属性添加
     *@param:
     *@return: void
     *@auther: why
     *@date: 2020/10/6 20:34
     */
    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        System.out.println("添加");
        System.out.println(se.getName());
        System.out.println(se.getValue());
        System.out.println("session"+se.getSession());
    }

    /**
     *@Description: attributeRemoved方法是属性移除
     *@param:
     *@return: void
     *@auther: why
     *@date: 2020/10/6 20:35
     */
    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
        System.out.println("移除");
    }

    /**
     *@Description: attributeReplaced方法是属性替换
     *@param:
     *@return: void
     *@auther: why
     *@date: 2020/10/6 20:35
     */
    @Override
    public void attributeReplaced(HttpSessionBindingEvent se) {
        System.out.println("替换");
    }

    /**
     *@Description: sessionCreated方法是session被创建
     *@param:
     *@return: void
     *@auther: why
     *@date: 2020/10/6 20:35
     */
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("session创建");
        System.out.println(se.getSession().getId());
    }

    /**
     *@Description: sessionDestroyed方法是session失效
     *@param:
     *@return: void
     *@auther: why
     *@date: 2020/10/6 20:36
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        System.out.println("session失效");
    }
}

jsp页面

<%
      request.getSession(true);
//      设置session失效时间
      session.setMaxInactiveInterval(2);
      session.setAttribute("username","why");
      session.setAttribute("username","wyy");
      session.removeAttribute("username");
  %>

用HttpSessionListener实现在线用户显示

监听器设计

package com.why.study2.listener;

import javax.servlet.ServletContext;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.*;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description TODO 用HttpSessionListener实现在线用户统计
 * @Author why
 * @Date 2020/10/7 10:53
 * Version 1.0
 **/

@WebListener
public class OnlineListener implements HttpSessionAttributeListener, HttpSessionListener {
    @Override
    public void attributeAdded(HttpSessionBindingEvent event) {
        System.out.println("添加session");
        //拿到session
        HttpSession session = event.getSession();
        session.setMaxInactiveInterval(100);
        //拿到全局域对象
        ServletContext application = session.getServletContext();

        Map<String,String> online = (Map<String, String>) application.getAttribute("online");
        if (online==null){
            online = new HashMap<>();
        }
        //map中放入session id和用户user
        online.put(session.getId(), session.getAttribute("user").toString());
        application.setAttribute("online",online);
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent event) {

    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent event) {

    }

    @Override
    public void sessionCreated(HttpSessionEvent event) {
        System.out.println("建立会话");
        HttpSession session = event.getSession();
        ServletContext application = session.getServletContext();
        Map<String,String> online = (Map<String, String>) application.getAttribute("online");
        if (online==null){
            online = new HashMap<>();
        }
        String username = (String) session.getAttribute("username");
        username = username==null?"游客":username;
        //map中放入session id和用户user
        online.put(session.getId(), username);
        application.setAttribute("onlin",online);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        System.out.println("会话销毁");
        HttpSession session = event.getSession();
        session.setMaxInactiveInterval(100);
        ServletContext application = session.getServletContext();
        Map<String,String> online = (Map<String, String>) application.getAttribute("online");
        if (online!=null){
            online.remove(session.getId());
            //更新后的map重新设置到application域空间中
            application.setAttribute("online",online);
        }
    }
}

view层设计

<table border="1" cellpadding="2" cellspacing="2" style="width: 80%;margin: 0 auto;">
  <tr>
    <td>在线用户ssid</td>
    <td>用户名</td>
  </tr>
  <%
    //拿到application中的online
    Map<String,String> online = (Map<String, String>) application.getAttribute("online");
    if (online!=null){
      for (String str:online.keySet()) {
  %>
  <tr>
    <td><%=str%></td>
    <td><%=online.get(str)%></td>
  </tr>
  <%
      }
    }
  %>
</table>

用监听器实现精确的在线用户显示

dao层

OnlineDao接口

package com.why.study2.dao;

import com.why.study2.entity.Online;

import java.util.List;

public interface OnlineDao {
    /**
     * 去除所有在线访问者的信息
     * @return
     */
    public List<Online>  getAllOnline();

    /**
     * 插入一条Online信息
     * @param online
     */
    public void insertOnline(Online online);

    /**
     * 更新保存的Online信息
     * @param online
     */
    public void updateOnline(Online online);

    /**
     * 根据session id删除下线的用户
     * @param ssid
     */
    public void deleteExpiresOnline(String ssid);

    /**
     * 根据ssid获取一条在线信息
     * @param ssid
     * @return
     */
    public Online getOnlineBySsid(String ssid);
}

OnlineDaoImpl实现类

package com.why.study2.dao.impl;

import com.why.study2.dao.BaseDao;
import com.why.study2.dao.OnlineDao;
import com.why.study2.entity.Online;

import java.util.List;

/**
 * @Description TODO OnlineDao接口实现类
 * @Author why
 * @Date 2020/10/7 15:12
 * Version 1.0
 **/
public class OnlineDaoImpl extends BaseDao<Online> implements OnlineDao {
    @Override
    public List<Online> getAllOnline() {
        System.out.println("getAllOnline()");
        String sql = "SELECT * FROM `online`";
        return super.getList(sql);
    }

    @Override
    public void insertOnline(Online online) {
        String sql = "INSERT `online` SET `ssid` = ?,`username` = ?,`page` = ?,`ip` = ?,`time` = ?";
        super.update(sql,online.getSsid(),online.getUsername(),online.getPage(),online.getIp(),online.getTime());
    }

    @Override
    public void updateOnline(Online online) {
        String sql = "UPDATE `online` SET `username` = ?,`page` = ?,`ip` = ?,`time` = ? WHERE ssid=?";
        super.update(sql,online.getUsername(),online.getPage(),online.getIp(),online.getTime(),online.getSsid());
    }

    @Override
    public void deleteExpiresOnline(String ssid) {
        String sql = "DELETE FROM `online` WHERE `ssid` = ?";
        super.update(sql,ssid);
    }

    @Override
    public Online getOnlineBySsid(String ssid) {
        String sql = "SELECT * FROM `online` WHERE ssid = ?";
        return super.get(sql,ssid);
    }
}

Service层

OnlineService

package com.why.study2.service;

import com.why.study2.entity.Online;

import java.util.List;

public interface OnlineService {
    /**
     * 去除所有在线访问者的信息
     * @return
     */
    public List<Online> getAllOnline();

    /**
     * 插入一条Online信息
     * @param online
     */
    public void insertOnline(Online online);

    /**
     * 更新保存的Online信息
     * @param online
     */
    public void updateOnline(Online online);

    /**
     * 根据session id删除下线的用户
     * @param expiresUserList
     */
    public void deleteExpiresOnline(List<Online> expiresUserList);

    /**
     * 根据ssid获取一条在线信息
     * @param ssid
     * @return
     */
    public Online getOnlineBySsid(String ssid);
}

实现类

package com.why.study2.service.impl;

import com.why.study2.dao.FactoryDao;
import com.why.study2.dao.OnlineDao;
import com.why.study2.entity.Online;
import com.why.study2.service.OnlineService;

import java.util.List;

/**
 * @Description TODO OnlineService实现类
 * @Author why
 * @Date 2020/10/7 15:36
 * Version 1.0
 **/
public class OnlineServiceImpl implements OnlineService {

    //通过工厂类拿到OnlineDao的对象
    OnlineDao onlineDao = FactoryDao.getOnlineDao();

    @Override
    public List<Online> getAllOnline() {

        return onlineDao.getAllOnline();
    }

    @Override
    public void insertOnline(Online online) {
        System.out.println("insertOnline"+online);
        onlineDao.insertOnline(online);
    }

    @Override
    public void updateOnline(Online online) {
        System.out.println("updateOnline"+online);
        onlineDao.updateOnline(online);
    }

    @Override
    public void deleteExpiresOnline(List<Online> expiresUserList) {
        System.out.println("deleteExpiresOnline"+expiresUserList);
        //遍历集合,实施删除操作
        if (expiresUserList!=null && expiresUserList.size()>0){
            for (Online ol:expiresUserList
                 ) {
                onlineDao.deleteExpiresOnline(ol.getSsid());
            }
        }
    }

    @Override
    public Online getOnlineBySsid(String ssid) {
        System.out.println("getOnlineBySsid"+ssid);
        System.out.println(onlineDao.getOnlineBySsid(ssid));
        return onlineDao.getOnlineBySsid(ssid);
    }
}

Controller层

UserController

package com.why.study2.controller;

import com.why.study2.entity.Online;
import com.why.study2.entity.User;
import com.why.study2.service.FactoryService;
import com.why.study2.service.OnlineService;
import com.why.study2.service.UserService;
import com.why.study2.utils.CookieUtils;

import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;

/**
 * 处理用户层相关的控制
 * 写相关的函数,在doPost方法内做判断调用不同的方法
 * 具体功能在具体的方法函数中
 */
public class UserController extends HttpServlet {

    /**
     * 根据service工厂类拿到service接口对象
     */
    UserService userService = FactoryService.getUserService();

    OnlineService onlineService = FactoryService.getOnlineService();

    /**
     * 序列化机制
     */
    private static final long serialVersionUID = 1L;

    @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 {
        System.out.println("这里是Post请求处理的方法");

        /**
         * 设置字符集
         */
        req.setCharacterEncoding("utf-8");
        resp.setCharacterEncoding("utf-8");


        //拿到url地址最后一位,包括“/”
        String mn = req.getServletPath();
        //截取除“/”的后几位
        mn = mn.substring(1);
        mn = mn.substring(0,mn.length()-4);
        System.out.println(mn);

        /**
         * 根据反射调用方法
         */
        System.out.println("根据反射调用方法"+mn);
        try {
            Method method = this.getClass().getDeclaredMethod(mn,HttpServletRequest.class,HttpServletResponse.class);
            method.invoke(this,req,resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 注册用户
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    private void add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        User user = new User();
        user.setUsername(req.getParameter("username"));
        user.setPassword(req.getParameter("password"));
        user.setAddress(req.getParameter("address"));
        user.setPhoneNo(req.getParameter("phoneNo"));
        user.setRegDate(new Date());

        int rows = userService.save(user);
        System.out.println("受影响行数"+rows);
        if(rows>0){
            resp.sendRedirect(req.getContextPath()+"/main.jsp");
        }else {
            resp.sendRedirect(req.getContextPath()+"/error.jsp");
        }
    }

    /**
     * 实现首页的模糊查询
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    private void query(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        System.out.println("调用query方法");
        String username = req.getParameter("username");
        String address = req.getParameter("address");
        String phoneNo = req.getParameter("phoneNO");

        System.out.println("controller"+username);
        System.out.println(address);
        System.out.println(phoneNo);

        /**
         * 正则表达式过滤特殊符号
         * 防止sql注入攻击
         */

        List<User> list = userService.query(username,address,phoneNo);
        System.out.println(list);

        /**
         * 把结果集放到属性空间
         */
        req.setAttribute("userList",list);
        /**
         * 转发到jsp页面
         */
        req.getRequestDispatcher("main.jsp").forward(req,resp);
    }

    private void delete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        int id = Integer.parseInt(req.getParameter("id"));
        System.out.println(id);
        int rows = userService.deleteUserById(id);
        if(rows>0){
            resp.sendRedirect(req.getContextPath()+"/main.jsp");
        }else {
            resp.sendRedirect(req.getContextPath()+"/error.jsp");
        }
    }

    private void update(){

    }

    /**
     * 用户登录方法
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    private void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        //第一步,拿到三个参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String expiredays = req.getParameter("expiredays");
        System.out.println("username");
        System.out.println(username);
        Cookie[] cookies = req.getCookies();

        boolean login = false;//是否登陆的标记
        String account = null;//登录的账号
        String ssid = null;//这是一个标记,通过cookie拿到的一个判断用户该不该登录成功的标记

        if (cookies!=null && cookies.length>0){
            for (Cookie cookie:
                 cookies) {
                if (cookie.getName().equals("userKey")){
                    account = cookie.getValue();
                }
                if (cookie.getName().equals("ssid")){
                    ssid = cookie.getValue();
                }
            }
        }

        if (account!=null && ssid != null){
            login = ssid.equals(CookieUtils.md5Encrypt(username));
            System.out.println(CookieUtils.md5Encrypt(username));
        }

        //login时false取反变成true,表示用户未登录,第一次访问
        if (!login){
            //查询数据库的用户名和密码,判断用户的用户名和密码是否和输入一致
            User user = userService.login(username,password);//登录成功,返回对应的User对象,登录失败,返回null
            if (user !=null){
                //对的,登陆成功
                //设置失效时间
                expiredays =  expiredays == null?"":expiredays;
                switch (expiredays){
                    case "7"://选择了记住我一周
                        //创建Cookie对象,设置失效时间为7天
                        CookieUtils.creatCookie(username,req,resp,7*24*60*60);
                        break;
                    case "30":
                        //创建Cookie对象,设置失效时间为30天
                        CookieUtils.creatCookie(username,req,resp,30*24*60*60);
                        break;
                    case "100":
                        //创建Cookie对象,设置没有失效时间
                        CookieUtils.creatCookie(username,req,resp,Integer.MAX_VALUE);
                        break;
                    default:
                        //默认时创建Cookie对象,设置失效时间为-1,关闭浏览器就失效
                        CookieUtils.creatCookie(username,req,resp,-1);
                        break;
                }

                //登录成功,将username放入Session空间
                req.getSession().setAttribute("user",user.getUsername());
                //把在线状态Online里的username从游客状态改为真正的username
                HttpSession session = req.getSession();
                Online ol = onlineService.getOnlineBySsid(session.getId());
                if (ol!=null){
                    ol.setUsername(username);
                    onlineService.updateOnline(ol);
                }
                //登录成功,准许客户进入到main.jsp页面
                resp.sendRedirect(req.getContextPath()+"/main.jsp");
            }else {
                req.setAttribute("note","用户名或密码错误!!!");
                req.getRequestDispatcher("/login.jsp").forward(req,resp);
            }
        }else {
            req.getSession().setAttribute("user",username);
            //把在线状态Online里的username从游客状态改为真正的username
            System.out.println("username");
            HttpSession session = req.getSession();
            Online ol = onlineService.getOnlineBySsid(session.getId());
            if (ol!=null){
                ol.setUsername(username);
                onlineService.updateOnline(ol);
            }
            resp.sendRedirect(req.getContextPath()+"/main.jsp");
        }
    }

    private void logout(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        //把记录登录状态的cookie删除
        Cookie[] cookies = req.getCookies();
        if (cookies!=null&&cookies.length>0) {
            for (Cookie cookie:
                 cookies) {
                if (cookie.getName().equals("userKey")){
                    cookie.setMaxAge(0);
                    //更新客户端cookie
                    resp.addCookie(cookie);
                }
                if (cookie.getName().equals("ssid")){
                    cookie.setMaxAge(0);
                    resp.addCookie(cookie);
                }
            }
            //把记录登录状态的session删除
        }
        HttpSession session = req.getSession();
        if (session!=null){
            session.removeAttribute("user");
        }
        //退出登录以后跳转到登录界面login.jsp
        resp.sendRedirect(req.getContextPath()+"/login.jsp");
    }

    /**
     * 统计显示在线人数
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    public void online(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
        List<Online> list = onlineService.getAllOnline();
        req.setAttribute("online",list);
        req.getRequestDispatcher("/online.jsp").forward(req,resp);
    }
}

Listener

package com.why.study2.listener;

import com.why.study2.entity.Online;
import com.why.study2.service.FactoryService;
import com.why.study2.service.OnlineService;

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Date;

/**
 * @Description TODO requestListener实现在线用户监听
 * 基本思路:
 * 步骤一:请求进来时,记录访问者的信息记入数据库
 *        判断数据库中存不存在此用户
 *        存在,更新访问时间,及访问页面
 *        不存在,添加新纪录
 * 步骤二:用户离开时,删除用户在线信息
 *      如何判断离线?
 *      访问来时记录用户访问时间,若用户十分钟没有操作,用户离线,删除用户信息
 *      ServletContextLinstener,web应用启动,每隔5秒钟,检查过期用户,并且执行删除
 * 步骤三:用户登录成功后将游客的用户名改为真正的用户名
 * @Author why
 * @Date 2020/10/7 16:28
 * Version 1.0
 **/
public class OnlineRequestListener implements ServletRequestListener {

    //Service对象申请操作数据库
    OnlineService onlineService = FactoryService.getOnlineService();
    @Override
    public void requestInitialized(ServletRequestEvent event) {
        System.out.println("请求来了");
        /**
         * 请求创建
         */
        //1.通过请求拿到访问者的基本信息
        //session id
        HttpServletRequest request = (HttpServletRequest) event.getServletRequest();
        HttpSession session = request.getSession();
        String ssid = session.getId();
        //获取访问者的ip地址
        String ip = request.getRemoteAddr();
        //获取访问者正在访问的页面
        String page = request.getRequestURI();
        //获取用户名,登录状态,已登录拿到用户名,未登录,用户名==游客
        String username = (String) session.getAttribute("user");
        username = username == null?"游客":username;

        //将以上数据封装到online对象中
        Online ol = new Online();
        ol.setSsid(ssid);
        ol.setUsername(username);
        ol.setPage(page);
        ol.setIp(ip);
        ol.setTime(new Date());
        //链接数据库,把信息更新或者插入一条数据
        //根据ssid来查看数据库是否有记录
        Online online = onlineService.getOnlineBySsid(ssid);
        if (online!=null){
            //有值更新访问时间
            online.setTime(new Date());
            //有值更新访问页面
            online.setPage(page);
            onlineService.updateOnline(online);
        }else {
            //第一次访问构造online插入数据库
            onlineService.insertOnline(ol);
        }
    }

    @Override
    public void requestDestroyed(ServletRequestEvent event) {

    }
}
package com.why.study2.listener;

import com.why.study2.entity.Online;
import com.why.study2.service.FactoryService;
import com.why.study2.service.OnlineService;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

/**
* @Description TODO
* @Author why
* @Date 2020/10/9 9:09
* Version 1.0
**/
public class OnlineServletContextListener implements ServletContextListener {
   //10分钟,用户超过设置的时间毫秒,没有任何操作离线
   public final int MAX_MILLIS = 10*60*1000;

   //拿到onlineService
   OnlineService onlineService = FactoryService.getOnlineService();

   @Override
   public void contextInitialized(ServletContextEvent servletContextEvent) {
       //存放过时的访问者信息
       List<Online> expiresUserList = new ArrayList<>();
       System.out.println("服务器开启监听");
       //定时器,每隔五秒执行一次
       new Timer(5000, new ActionListener() {
           @Override
           public void actionPerformed(ActionEvent event) {
              List<Online> list = onlineService.getAllOnline();
              //判断是否有值
               if (list!=null && list.size()>0){
                   for (Online ol: list
                        ) {
                       //判断系统时间减去数据库时间的长整型是否大于10分钟
                       if ((System.currentTimeMillis()-Long.parseLong(ol.getTime().toString()))>MAX_MILLIS){
                           expiresUserList.add(ol);
                      }
                  }
                   //从数据库中删除掉过时的访问者信息
                   onlineService.deleteExpiresOnline(expiresUserList);
                   //数据库中清理删除了过时的访问者信息,list清空
                   expiresUserList.clear();
              }

          }
      }).start();
  }

   @Override
   public void contextDestroyed(ServletContextEvent servletContextEvent) {
       System.out.println("服务器销毁监听");
  }
}

 

posted @ 2020-11-26 16:55  笔落惊风  阅读(195)  评论(0编辑  收藏  举报