个人云日记项目

原创是乐字节此处就是练手使用,目前jsp使用的少了,不喜勿喷

一.云日记的项目模块总结

二.使用的技术归类

  • JSP
  • Servlet
  • Jquery及插件
  • Ueditor
  • log4j
  • Junit

 三.技术细节

  • 增删改查
  • 免登录:Session 和 Cookie
  • 非法访问:过滤器 Filter
  • 文件上传
  • 分页
  • Ajax:Dom操作
  • 富文本编辑器

 四.开发环境

IDEA + Maven + MySQL8 + Jdk11 + Tomcat

五.使用mysql建表

1.用户表tb_user

 2.类型表tb_note_type

3.云记表tb_note

六.环境搭建

1.新建Maven项目 lezijie-note

 2.修改pom.xml文件增加jar包

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <!-- junit 测试 -->
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <!-- web servlet -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>
    <!-- 日志打印相关的jar -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.30</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.30</version>
    </dependency>
    <!-- mysql 驱动包 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.25</version>
    </dependency>
    <!-- json 依赖 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.78</version>
    </dependency>
    <!-- hutool工具集 -->
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-all</artifactId>
      <version>5.7.11</version>
    </dependency>
    <!-- commons-io 依赖 -->
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.5</version>
    </dependency>
    <!-- 添加百度编辑器ueditor支持 -->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.4</version>
    </dependency>
    <!-- jstl依赖 -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    <!-- lombok插件 -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.20</version>
    </dependency>
  </dependencies>
</dependencies>
    <build>
        <finalName>cloudDiary</finalName>
        <plugins>
            <!-- 编译环境插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <!-- Tomcat插件 -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.1</version>
                <configuration>
                    <port>8080</port> <!-- 启动端口 默认:8080 -->
                    <path>/note</path> <!-- 项目的站点名,即对外访问路径 -->
                    <server>tomcat7</server> <!-- 服务器名称 -->
                </configuration>
           </plugin>
        </plugins>
    </build>
</project>

3.分层思想

4.数据库的连接

配置文件

在 src/main/resources 目录下,新建 db.properties 文件

# 连接MYSQL数据库的配置文件 注:等号的前后不要写空格
# 驱动名
jdbcName=com.mysql.cj.jdbc.Driver
# 数据库连接 ( clouddiary是数据库的名称)
dbUrl=jdbc:mysql://localhost:3306/blogs?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
# 数据库的连接账号 (账号基本上都是root)
dbName=root
# 数据库的连接密码 (每个人的数据库密码可能不一致,需要修改)
dbPwd=rootroot

编写 DBUtil

在 com.jiangqi.note.util 目录下,新建 Java 类

package com.jiangqi.note.util;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class DBUtil {
    //定义配置对象 目的是接收配置文件中的键值对对
    private static Properties properties = new Properties();
    //静态代码块在类加载时就调用, 而且只执行一次, 并且优先于各种代码块以及构造函数。

    /*
     * getResourceAsStream
     * 如果写一个类需要动态的获取某个文件的位置,从而能够获取此文件的资源。
     * 那么,使用Class.getResourceAsStream()方法便可以。
     * 这样就不用每次去修改代码中文件的绝对地址或详细地址了。
     */
    static {
        try {
            //加载配置文件(输入流)
            //获取当前类的类加载器,并根据getResourceAsStrem函数去获取资源(配置文件)
            InputStream inputStream = DBUtil.class.getClassLoader().getResourceAsStream("db.properties");
            //通过load()方法将输入流的内容加载到配置文件对象中
            properties.load(inputStream);
            //通过配置对象的getProperty()方法得到驱动名,加载驱动
            Class.forName(properties.getProperty("jdbcName"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取数据库连接
     */
    public static Connection getConnection(){
        Connection con = null;
        try {
        //得到数据库的连接的相关信息
        String dbUrl = properties.getProperty("dbUrl");
        String daName = properties.getProperty("dbName");
        String dbPwd = properties.getProperty("dbPwd");
        //得到数据库连接 驱动管理器DriverManager
        con = DriverManager.getConnection(dbUrl, daName, dbPwd);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return con;
    }

    /**
     * 关闭资源
     * 当资源对象不为空时,关闭资源
     *
     * @param res 结果集对象
     * @param pre 预编译对象
     * @param con 连接对象
     */
    public static void close(ResultSet res,PreparedStatement pre,Connection con){
        //判断资源对象如果不为空,则关闭
        try {
            if(res != null) {
                res.close();//关闭资源
            }
            if(pre != null){
                pre.close();//关闭资源
            }
            if(con != null){
                con.close();//关闭资源
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

测试数据库

在 test/java 目录的 com.jiangqi.note 包下,新建测试类

package com.lezijie.note;

import com.jiangqi.note.util.DBUtil;
import org.junit.Test;

public class TestDB {
    @Test
    public void getConnection(){
        System.out.println(DBUtil.getConnection());
    }
}

测试成功 com.mysql.cj.jdbc.ConnectionImpl@6737fd8f 绿色的idea提示

5.使用日志

配置文件

在 src/main/resources 目录下,新建 log4j.properties 文件

### 设置###stdout,
log4j.rootLogger = all,D,E,stdout

### 输出信息到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到=D://logs/log.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = D://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG 
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR 
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]

使用方式

package com.lezijie.note;
import com.jiangqi.note.util.DBUtil;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestDB {
    //使用日志工厂类,记录日志
    private Logger logger = LoggerFactory.getLogger(TestDB.class);

    @Test
    public void getConnection(){
        System.out.println(DBUtil.getConnection());
        logger.info("获取数据库连接" + DBUtil.getConnection());
    }
}

结果

七.实现模块

1.用户模块

登陆界面

 前端代码这里就不展示了,点击获取

用户登陆,代码实现思想总结

用户模块
    通过用户行为来区分       actionName
        用户登录           actionName="login"
        进入个人中心        actionName="userCenter"
        加载头像            actionName="userHead"
        验证昵称的唯一性     actionName="checkNick"
        修改用户信息        actionName="updateUser"
        用户退出            actionName="logout"

    在UserServlet的service方法中
        1.获取用户行为
            String actionName = request.getParameter("actionName");
        2. 判断用户行为,调用对应的方法
            if ("login".equals(actionName)) {
                // 用户登录
            } else if ("userCenter".equals(actionName)) {
                // 进入个人中心
            }


一、用户登录
    ==================前端实现==================
        1. 添加表单,设置表单属性
            action="user" method="post" id="loginForm"
        2. 设置表单元素的name属性值,修改id属性值(首字母改小写)
            设置文本框和密码框的那么属性值
            设置复选框的name属性值和value属性值(如果复选框未选中,在获取不到value属性值)
            设置隐藏域,传递用户行为 (name="actionName" value="login")
        3. 在 config.js 文件中,添加按钮的点击事件绑定的函数 checkLogin()
            表单校验
                1. 获取表单元素的值(姓名、密码)
                2. 判断元素的值是否为空(引入util.js文件)
                    如果为空,设置提示信息 (通过设置span标签),并return
                3. 如果不为空,则提交表单

    ==================后端实现==================
整体思路:
    1. 获取参数 (姓名、密码)
    2. 参数的非空校验  (ResultInfo的封装类,用来封装响应结果  状态码、提示信息、返回的对象)
        如果为空
            设置ResultInfo对象的状态码和提示信息
            将ResultInfo对象设置request作用域中
            请求转发跳转到登录页面
            return
    3. 如果不为空,通过用户名查询用户对象
    4. 判断用户对象是否为空
        如果为空
            设置ResultInfo对象的状态码和提示信息
            将ResultInfo对象设置request作用域中
            请求转发跳转到登录页面
            return
    5. 如果用户对象不为空,将数据库中查询到的用户对象的密码与前台传递的密码作比较 (将密码加密后再比较)
        如果密码不正确
            设置ResultInfo对象的状态码和提示信息
            将ResultInfo对象设置request作用域中
            请求转发跳转到登录页面
            return
    6. 如果密码正确
        将用户信息存到session作用域中
        判断用户是否选择记住密码(rem的值是1)
            如果是,将用户姓名与密码存到cookie中,设置失效时间,并响应给客户端
            如果否,清空原有的cookie对象
        重定向跳转到index页面

分层思想:
    Web层:(控制层:接收参数、响应数据)
        1. 获取参数 (姓名、密码)
        2. 调用Service层的方法,返回ResultInfo对象
        3. 判断是否登录成功
            如果失败
                将resultInfo对象设置到request作用域中
                请求转发跳转到登录页面
            如果成功
                将用户信息设置到session作用域中
                判断用户是否选择记住密码(rem的值是1)
                    如果是,将用户姓名与密码存到cookie中,设置失效时间,并响应给客户端
                    如果否,清空原有的cookie对象
                重定向跳转到index页面

    Service层:(业务逻辑层:参数判断、业务逻辑处理)
        1. 判断参数是否为空
            如果为空
                设置ResultInfo对象的状态码和提示信息
                返回resultInfo对象
        2. 如果不为空,通过用户名查询用户对象
        3. 判断用户对象是否为空
                如果为空
                    设置ResultInfo对象的状态码和提示信息
                    返回resultInfo对象
        4. 如果用户对象不为空,将数据库中查询到的用户对象的密码与前台传递的密码作比较 (将密码加密后再比较)
               如果密码不正确
                    设置ResultInfo对象的状态码和提示信息
                    返回resultInfo对象
        5. 如果密码正确
            设置ResultInfo对象的状态码和提示信息
        6. 返回resultInfo对象


    Dao层:(数据访问层:数据库中的增删改查操作)
        通过用户名查询用户对象, 返回用户对象
            1. 获取数据库连接
            2. 定义sql语句
            3. 预编译
            4. 设置参数
            5. 执行查询,返回结果集
            6. 判断并分析结果集
            7. 关闭资源

idea中代码层级结构

 1.web包下创建UserServlet

package com.jiangqi.note.web;
import com.jiangqi.note.po.User;
import com.jiangqi.note.service.UserService;
import com.jiangqi.note.vo.ResultInfo;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/user")
public class UserServlet extends HttpServlet {
    private UserService userService = new UserService();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //接收用户行为
        String actionName = request.getParameter("actionName");
        //判断用户行为,调用对应方法
        if("login".equals(actionName)){
            //用户登陆
            userLogin(request,response);
        }
    }

    private void userLogin(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取参数
        String userName = request.getParameter("userName");
        String userPwd = request.getParameter("userPwd");
        //调用Service层的方法,返回ResultInfo对象
        ResultInfo<User> resultInfo = userService.userLogin(userName, userPwd);
        //判断是否登陆成功
        if(resultInfo.getCode() == 1){//成功
            //将用户信息设置到session作用域中
            request.getSession().setAttribute("user",resultInfo.getResult());
            //判断用户是否选择记住密码(rem的值是1)
            String rem = request.getParameter("rem");
            //如果是,将用户姓名与密码存到cookie中,设置失效时间,并响应给客户端
            if("1".equals(rem)){
                //得到Cookie对象
                Cookie cookie = new Cookie("user",userName + "-" + userPwd);//直接拼接不好取值
                //设置失效时间
                cookie.setMaxAge(3*24*60*60);//失效时间3天
                //相应给客户端
                response.addCookie(cookie);
            }else{
                //如果否,清空原有的cookie对象
                Cookie cookie = new Cookie("user",null);//不冲突上边是session,这个是cookie
                //删除cookie,设置maxage为0;
                cookie.setMaxAge(0);
                //相应给客户端
                response.addCookie(cookie);
            }
            //重定向跳转到index页面
            response.sendRedirect("index.jsp");
        }else{//失败
            //将resultinfo对象设置到request作用域中
            request.setAttribute("resultInfo",resultInfo);
            //请求转发跳转到登陆页面
            request.getRequestDispatcher("login.jsp").forward(request,response);
        }
    }
}

2.service包下创建UserService

package com.jiangqi.note.ç;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.jiangqi.note.dao.UserDao;
import com.jiangqi.note.po.User;
import com.jiangqi.note.vo.ResultInfo;
public class UserService {
    private UserDao userDao = new UserDao();

    public ResultInfo<User> userLogin(String userName,String userpwd){
        ResultInfo<User> resultInfo = new ResultInfo<>();
        //判断参数是否为空
        if (StrUtil.isBlank(userName)||StrUtil.isBlank(userpwd)) {//既可以判断是否为null,也可以判断是否为空||双或只要有一个为true结果为true
            resultInfo.setCode(0);
            resultInfo.setMsg("用户姓名或密码不能为空");
            return resultInfo;
        }
        //如果不为空,通过用户名查询用户对象
        User user = userDao.queryUserByName(userName);
        //判断用户对象是否为空
        if(user == null){
            //如果为空,设置ResultInfo对象的状态码和提示信息
            resultInfo.setCode(0);
            resultInfo.setMsg("改用户不存在!");
            return resultInfo;
        }
        //前台传递的密码使用md5进行加密
        userpwd = DigestUtil.md5Hex(userpwd);
        //判断加密后的密码和数据库的密码是否一至
        if(!userpwd.equals(user.getUpwd())){
            //不正确
            resultInfo.setCode(0);
            resultInfo.setMsg("用户密码不正确!");
            return resultInfo;
        }
        resultInfo.setCode(1);
        resultInfo.setResult(user);
        return resultInfo;
    }
}

3.dao包下创建Userdao

package com.jiangqi.note.dao;
import com.jiangqi.note.po.User;
import com.jiangqi.note.util.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class UserDao {
    public User queryUserByName(String userName){
        User user = null;
        Connection connection = null;
        PreparedStatement preparedStatement=null;
        ResultSet resultSet =null;
        try {
            //1. 获取数据库连接
            connection = DBUtil.getConnection();
            //2. 定义sql语句
            String sql = "select * from tb_user where uname = ?";
            //3. 预编译
            preparedStatement = connection.prepareStatement(sql);
            //4. 设置参数
            preparedStatement.setString(1,userName);
            //5. 执行查询,返回结果集
            resultSet = preparedStatement.executeQuery();
            //6. 判断并分析结果集
            if(resultSet.next()){
                user = new User();
                user.setUserId(resultSet.getInt("userId"));
                user.setUname(userName);
                user.setHead(resultSet.getString("head"));
                user.setMood(resultSet.getString("mood"));
                user.setNick(resultSet.getString("nick"));
                user.setUpwd(resultSet.getString("upwd"));
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //7. 关闭资源
            DBUtil.close(resultSet,preparedStatement,connection);
        }
        return user;
    }
}

4.po包下User类

package com.jiangqi.note.po;
import lombok.*;
import java.io.Serializable;
@Setter
@Getter
public class User  implements Serializable {
    private Integer userId;
    private String  uname;
    private String  upwd;
    private String  nick;
    private String  head;
    private String  mood;
}

5.vo包下ResultInfo

package com.jiangqi.note.vo;

import lombok.Getter;
import lombok.Setter;

/**
 * 封装返回结果的类
 *      状态码
 *          成功=1,失败=0
 *      提示信息
 *      返回对象
 */
@Getter
@Setter
public class ResultInfo<T>{
    private Integer code;
    private String msg;
    private  T result;
}

6.dao层下BaseDao,

有更新,查询一个字段,集合,对象 的方法

package com.jiangqi.note.dao;
import com.jiangqi.note.util.DBUtil;

import javax.print.attribute.standard.JobName;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
/**
 * 基础的JDBC操作类
 *      更新操作 (添加、修改、删除)
 *      查询操作
 *          1. 查询一个字段 (只会返回一条记录且只有一个字段;常用场景:查询总数量)
 *          2. 查询集合
 *          3. 查询某个对象
 */
public class BaseDao {
    /**
     * 更新操作
     *  添加、修改、删除
     *  1. 得到数据库连接
     *  2. 定义sql语句 (添加语句、修改语句、删除语句)
     *  3. 预编译
     *  4. 如果有参数,则设置参数,下标从1开始 (数组或集合、循环设置参数)
     *  5. 执行更新,返回受影响的行数
     *  6. 关闭资源
     *
     *  注:需要两个参数:sql语句、所需参数的集合
     *
     * @param sql
     * @param params
     * @return
     */

    public static int executeUpdate(String sql, List<Object> params){
        int row = 0 ;// 受影响的行数
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            // 得到数据库连接
            connection = DBUtil.getConnection();
            // 预编译
            preparedStatement = connection.prepareStatement(sql);
            // 如果有参数,则设置参数,下标从1开始
            if(params != null && params.size() > 0){
                // 循环设置参数,设置参数类型为Object
                for(int i = 0 ; i < params.size() ; i++){
                    preparedStatement.setObject(i + 1 ,params.get(i));
                }
            }
            // 执行更新,返回受影响的行数
            row = preparedStatement.executeUpdate();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DBUtil.close(null,preparedStatement,connection);
        }
        return row;
    }

    /**
     * 查询一个字段 (只会返回一条记录且只有一个字段;常用场景:查询总数量)
     *  1. 得到数据库连接
     *  2. 定义sql语句
     *  3. 预编译
     *  4. 如果有参数,则设置参数,下标从1开始 (数组或集合、循环设置参数)
     *  5. 执行查询,返回结果集
     *  6. 判断并分析结果集
     *  7. 关闭资源
     *
     *  注:需要两个参数:sql语句、所需参数的集合
     *
     * @param sql
     * @param params
     * @return
     */
    public static Object findSingleValue(String sql,List<Object> params){
        Object object = null;
        ResultSet resultSet = null;
        Connection connection = null;
        PreparedStatement preparedStatement = null;

        try {
            // 获取数据库连接
            connection = DBUtil.getConnection();
            // 预编译
            preparedStatement = connection.prepareStatement(sql);
            // 如果有参数,则设置参数,下标从1开始
            if(params != null && params.size() > 0){
                // 循环设置参数,设置参数类型为Object
                for(int i = 0 ; i < params.size(); i++){
                    preparedStatement.setObject(i+1, params.get(i));
                }
            }
            // 执行查询,返回结果集
            resultSet = preparedStatement.executeQuery();
            // 判断并分析结果集
            if(resultSet.next()){//因为只查询一个字段返回的也是有一个,通过if,下标获取
                object = resultSet.getObject(1);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 关闭资源
            DBUtil.close(resultSet,preparedStatement,connection);
        }
        return object;
    }

    /**
     * 因为不知道是一个什么样的集合,需要告诉我要查一个什么样的bean对象的集合
     * 如何去查?
     *      查询sql语句对应的表中,有哪些字段,拿到表中每一个字段,列名,通过反射,让列名找到与之匹配的属性字段
     *      通过拼接这个set方法。通过反射set方法,变成我们类中指定的set方法,调用我们反射的invoke,将我们的值设置进去
     *      最终我们才能拿到javabean,通过反射拿到结果集的元数据,通过元数据,拿到表的列名
     *
     * 查询集合 (JavaBean中的字段与数据库中表的字段对应)
     *  1. 获取数据库连接
     *  2. 定义SQL语句
     *  3. 预编译
     *  4. 如果有参数,则设置参数,下标从1开始 (数组或集合、循环设置参数)
     *  5. 执行查询,得到结果集
     *  6. 得到结果集的元数据对象(查询到的字段数量以及查询了哪些字段)
     *  7. 判断并分析结果集
     *      8. 实例化对象
     *      9. 遍历查询的字段数量,得到数据库中查询到的每一个列名
     *      10. 通过反射,使用列名得到对应的field对象
     *      11. 拼接set方法,得到字符串
     *      12. 通过反射,将set方法的字符串反射成类中的指定set方法
     *      13. 通过invoke调用set方法
     *      14. 将对应的JavaBean设置到集合中
     *  15. 关闭资源
     * @param sql
     * @param params
     * @param cls
     * @return
     */
    public static List queryRows(String sql,List<Object> params,Class cls){
        List<Object> list = new ArrayList<>();
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            // 获取数据库连接
            connection = DBUtil.getConnection();
            // 预编译
            preparedStatement = connection.prepareStatement(sql);
            // 如果有参数,则设置参数,下标从1开始
            if(params != null && params.size() > 0){
                // 循环设置参数,设置参数类型为Object
                for(int i = 0; i < params.size(); i++){
                    preparedStatement.setObject(i+1, params.get(i));
                }
            }
            // 执行查询,返回结果集
            resultSet = preparedStatement.executeQuery();
            // 得到结果集的元数据对象(查询到的字段数量以及查询了哪些字段)
            ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
            // 得到查询的字段数量
            int fieldNum = resultSetMetaData.getColumnCount();
            // 判断并分析结果集
            while (resultSet.next()){
                // 实例化对象
                Object object = cls.newInstance();
                // 遍历查询的字段数量,得到数据库中查询的每一个列名
                for(int i = 1;i <= fieldNum ; i++){
                    // 得到查询的每一个列名
                    // getColumnLabel():获取列名或别名
                    // getColumnName():获取列名
                    String columnName = resultSetMetaData.getColumnLabel(i);// 如果是tb_user,userId字段
                    // 通过反射,使用列名得到对应的field对象
                    Field field = cls.getDeclaredField(columnName);
                    // 拼接set方法,得到字符串
                    String setMethod = "set" + columnName.substring(0,1).toUpperCase() + columnName.substring(1);
                    //通过反射,将set方法字符串反射成类中对应的set方法
                    Method method = cls.getDeclaredMethod(setMethod, field.getType());
                    // 得到查询的每一个字段对应的值
                    Object value = resultSet.getObject(columnName);
                    // 通过invoke方法调用set方法
                    method.invoke(object, value);
                }
                // 将Javabean设置到集合中
                list.add(object);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //15. 关闭资源
            DBUtil.close(resultSet, preparedStatement, connection);
        }
        return list;
    }

    /**
     * 查询对象
     * @param sql
     * @param params
     * @param cls
     * @return
     */
    public static Object queryRow(String sql ,List<Object> params,Class cls){
        List list = queryRows(sql, params, cls);
        Object object = null;
        // 如果集合不为空,则获取查询的第一条数据
        if(list != null && list.size() > 0){
            object = list.get(0);
        }
        return  object;
    }
}

7. 把 web层下UserServlet修改

重定向跳转到index页面。改成进入到控制器
            //response.sendRedirect("index.jsp");
            response.sendRedirect("index");

8. web层下新建IndexServlet类

请求转发到index.jsp
package com.jiangqi.note.web;

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;

@WebServlet("/index")
public class IndexServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //设置首页动态包含页面
        request.setAttribute("changePage", "note/list.jsp");
        //请求转发到index.jsp
        request.getRequestDispatcher("index.jsp").forward(request, response);
    }
}

9.新建 filter包 EncodingFilter 类  针对登录汉子乱码问题的

这样登录姓名输入汉字后台断点就不会显示汉字乱码了

package com.jiangqi.note.filter;

import cn.hutool.core.util.StrUtil;

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

/**
 * 过滤器拦截登录时的中文乱码
 *  请求乱码解决
 *     乱码原因:
 *         服务器默认的解析编码为ISO-8859-1,不支持中文。
 *     乱码情况:
 *         POST请求
 *             Tomcat7及以下版本    乱码
 *             Tomcat8及以上版本    乱码
 *
 *         GET请求
 *              Tomcat7及以下版本    乱码
 *              Tomcat8及以上版本    不乱码
 *
 *     解决方案:
 *         POST请求:
 *             无论是什么版本的服务器,都会出现乱码,需要通过request.setCharacterEncoding("UTF-8")设置编码格式。(只针对POST请求有效)
 *         GET请求
 *             Tomcat8及以上版本,不会乱码,不需要处理。
 *             Tomcat7及以下版本,需要单独处理。
 *                 new String(request.getParamater("xxx").getBytes("ISO-8859-1"),"UTF-8");
 */

@WebFilter("/*")//过滤所有资源
public class EncodingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //基于http
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        //处理POST请求 (只针对POST请求,GET请求无效)
        request.setCharacterEncoding("UTF-8");

        // 得到请求类型 (GET|POST)
        String method = request.getMethod();
        // 如果是GET请求,则判断服务器版本
        if("GET".equalsIgnoreCase(method)){// 忽略大小写比较
            // 得到服务器版本
            String serverInfo = request.getServletContext().getServerInfo(); Apache Tomcat/7.0.79
            // 通过截取字符串,得到具体的版本号     在斜杠前一个,到点结束
            String version = serverInfo.substring(serverInfo.indexOf("/") + 1, serverInfo.indexOf("."));
            // 判断服务器版本是否是Toncat7级以下
            if(version != null && Integer.parseInt(version) < 8){
                // Tomcat7及以下版本的服务器的GET请求
                MyWapper myRequest = new MyWapper(request);
                // 放行资源
                filterChain.doFilter(myRequest, response);
                return;
            }
        }
        // 放行资源
        filterChain.doFilter(request, response);
    }

    /**
     * 1. 定义内部类 (类的本质是request对象)
     * 2. 继承 HttpServletRequestWrapper包装类
     * 3. 重写getarameterP()方法
     */
    class MyWapper extends HttpServletRequestWrapper {
        // 定义成员变量 HttpServletRequest对象 (提升构造器中request对象的作用域)
        private HttpServletRequest request;/**
         * 带参构造
         *  可以得到需要处理的request对象
         * @param request
         */
        public MyWapper(HttpServletRequest request) {
            super(request);
            this.request = request;
        }

        /**
         * 重写getParameter方法,处理乱码问题
         * @param name
         * @return
         * 也就是我这个地方重写了之后,如果是tomcat7的话,调的就是我的 内部类的request,然后就近原则,就会调用我重写的获取参数的方法
         * 如果不是get请求,也不是tomcat 7 以下还是会调取,系统原来的request,调系统原来的getParameter方法
         */
        @Override
        public String getParameter(String name) {
            // 获取参数 (乱码的参数值)
            String value = request.getParameter(name);
            // 判断参数值是否为空
            if (StrUtil.isBlank(value)) {
                return value;
            }
            // 通过new String()处理乱码
            try {//转成utf-8
                value = new String(value.getBytes("ISO-8859-1"), "UTF-8");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return value;
        }
    }
}

10.filter包下过滤器

package com.jiangqi.note.filter;

import com.jiangqi.note.po.User;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//需要先实现拦截接口
/**
 * 非法访问拦截
 *  拦截的资源:
 *      所有的资源   /*
 *
 *      需要被放行的资源
 *          1. 指定页面,放行 (用户无需登录的即可访问的页面;例如:登录页面login.jsp、注册页面register.jsp等)
 *          2. 静态资源,放行 (存放在statics目录下的资源;例如:js、css、images等)
 *          3. 指定行为,放行 (用户无需登录即可执行的操作;例如:登录操作actionName=login等)
 *          4. 登录状态,放行 (判断session作用域中是否存在user对象;存在则放行,不存在,则拦截跳转到登录页面)
 *          免登录(自动登录)
 *        通过Cookie和Session对象实现
 *
 *    什么时候使用免登录:
 *            当用户处于未登录状态,且去请求需要登录才能访问的资源时,调用自动登录功能
 *
 *        目的:
 *            让用户处于登录状态(自动调用登录方法)
 *
 *        实现:
 *            从Cookie对象中获取用户的姓名与密码,自动执行登录操作
 *                1. 获取Cookie数组  request.getCookies()
 *                2. 判断Cookie数组
 *               3. 遍历Cookie数组,获取指定的Cookie对象 (name为user的cookie对象)
 *                4. 得到对应的cookie对象的value (姓名与密码:userName-userPwd)
 *                5. 通过split()方法将value字符串分割成数组
 *                6. 从数组中分别得到对应的姓名与密码值
 *                7. 请求转发到登录操作  user?actionName=login&userName=姓名&userPwd=密码
 *                8. return
 *
 *       如果以上判断都不满足,则拦截跳转到登录页面
 *
 */
@WebFilter("/*")//过滤器
public class LoginAccessFilter implements Filter {

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

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 基于HTTP
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        // 得到访问的路径
        // 格式:项目路径/资源路径
        String path = request.getRequestURI();
        // 1. 指定页面,放行 (用户无需登录的即可访问的页面;例如:登录页面login.jsp、注册页面register.jsp等)
        if(path.contains("/login.jsp")){
            filterChain.doFilter(request, response);
            return;
        }
        // 2. 静态资源,放行 (存放在statics目录下的资源;例如:js、css、images等)
        if(path.contains("/statics")){
           filterChain.doFilter(request, response);
           return;
        }
        // 3. 指定行为,放行 (用户无需登录即可执行的操作;例如:登录操作actionName=login等)
        if(path.contains("/user")){
            // 得到用户行为
            String actionName = request.getParameter("actionName");
            // 判断是否是登录操作
            if("login".equals(actionName)){
                filterChain.doFilter(request, response);
                return;
            }
        }

        // 4. 登录状态,放行 (判断session作用域中是否存在user对象;存在则放行,不存在,则拦截跳转到登录页面)
        // 获取Session作用域中的user对象
        User user = (User) request.getSession().getAttribute("user");
        // 判断user对象是否为空
        if(user != null){
            filterChain.doFilter(request, response);
            return;
        }

        /**
         * 免登录 (自动登录)
         *      从Cookie对象中获取用户的姓名与密码,自动执行登录操作
         */
        // 1. 获取Cookie数组  request.getCookies()
        Cookie[] cookies = request.getCookies();
        // 2. 判断Cookie数组
        if(cookies != null && cookies.length > 0){
            // 3. 遍历Cookie数组,获取指定的Cookie对象 (name为user的cookie对象)
            for(Cookie cookie: cookies){
                // 4. 得到对应的cookie对象的value (姓名与密码:userName-userPwd)
                String value = cookie.getValue();
                // 5. 通过split()方法将value字符串分割成数组
                String[] val = value.split("-");
                // 6. 从数组中分别得到对应的姓名与密码值
                String userName = val[0];
                String userPwd = val[1];
                // 7. 请求转发到登录操作  user?actionName=login&userName=姓名&userPwd=密码
                //rem=1 记住密码的意思
                String url = "user?actionName=login&rem=1&userName="+userName+"&userPwd="+userPwd;
                request.getRequestDispatcher(url).forward(request, response);
                // 8. return
                return;
            }
        }
        // 拦截请求,重定向跳转到登录页面
        response.sendRedirect("login.jsp");
    }

    @Override
    public void destroy() {

    }
}

11.web包下 UserServlet  用户登录,退出,进入个人中心,加载头像的代码

package com.jiangqi.note.web;
import com.jiangqi.note.po.User;
import com.jiangqi.note.service.UserService;
import com.jiangqi.note.vo.ResultInfo;
import org.apache.commons.io.FileUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
@WebServlet("/user")
public class UserServlet extends HttpServlet {
    private UserService userService = new UserService();



    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置首页导航高亮
        request.setAttribute("menu_page", "user");

        //接收用户行为
        String actionName = request.getParameter("actionName");
        //判断用户行为,调用对应方法
        if("login".equals(actionName)){
            //用户登陆
            userLogin(request,response);
        }else if("logout".equals(actionName)){
            // 用户退出
            userLogout(request,response);

        }else if("user".equals(actionName)){
            //进入个人中心
            userCenter(request,response);
        }else if("userHead".equals(actionName)){
            //加载头像
            userHead(request,response);
        }
    }

    /**加载头像
     * 1. 获取参数 (图片名称)
     * 2. 得到图片的存放路径 (request.getServletContext().getealPathR("/"))
     * 3. 通过图片的完整路径,得到file对象
     * 4. 通过截取,得到图片的后缀
     * 5. 通过不同的图片后缀,设置不同的响应的类型
     * 6. 利用FileUtils的copyFile()方法,将图片拷贝给浏览器
     * @param request
     * @param response
     */
    private void userHead(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //1. 获取参数 (图片名称)
        String head = request.getParameter("imageName");
        //2. 得到图片的存放路径 (request.getServletContext().getealPathR("/"))
        String realPath = request.getServletContext().getRealPath("/WEB-INF/upload/");
        //3. 通过图片的完整路径,得到file对象
        File file = new File(realPath + "/" + head);
        //4. 通过截取,得到图片的后缀
        String pic = head.substring(head.lastIndexOf(".") + 1);
        //5. 通过不同的图片后缀,设置不同的响应的类型
        //equalsIgnoreCase作用一般是用来比较字母的长度和字符是否相同,切不区分大小写
        if("PNG".equalsIgnoreCase(pic)){
            response.setContentType("image/png");
        }else if("JPG".equalsIgnoreCase(pic) || "JPEG".equalsIgnoreCase(pic)){
            response.setContentType("image/png");
        }else if("GIF".equalsIgnoreCase(pic)){
            response.setContentType("image/png");
        }
        //6. 利用FileUtils的copyFile()方法,将图片拷贝给浏览器
        FileUtils.copyFile(file, response.getOutputStream());
    }

    /**
     * 进入个人中心
     *  1. 设置首页动态包含的页面值
     *  2. 请求转发跳转到index.jsp
     * @param request
     * @param response
     */
    private void userCenter(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. 设置首页动态包含的页面值
        request.setAttribute("changePage", "user/info.jsp");
        // 2. 请求转发跳转到index
        request.getRequestDispatcher("index.jsp").forward(request, response);
    }

    /**
     * 用户退出
     *  1. 销毁Session对象
     *  2. 删除Cookie对象
     *  3. 重定向跳转到登录页面
     * @param request
     * @param response
     */

    private void userLogout(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 1. 销毁Session对象
        request.getSession().invalidate();
        // 2. 删除Cookie对象
        Cookie cookie = new Cookie("user", null);
        cookie.setMaxAge(0);
        response.addCookie(cookie);
        // 3. 重定向跳转到登录页面
        response.sendRedirect("login.jsp");
    }

    private void userLogin(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取参数
        String userName = request.getParameter("userName");
        String userPwd = request.getParameter("userPwd");
        //调用Service层的方法,返回ResultInfo对象
        ResultInfo<User> resultInfo = userService.userLogin(userName, userPwd);
        //判断是否登陆成功
        if(resultInfo.getCode() == 1){//成功
            //将用户信息设置到session作用域中
            request.getSession().setAttribute("user",resultInfo.getResult());
            //判断用户是否选择记住密码(rem的值是1)
            String rem = request.getParameter("rem");
            //如果是,将用户姓名与密码存到cookie中,设置失效时间,并响应给客户端
            if("1".equals(rem)){
                //得到Cookie对象
                Cookie cookie = new Cookie("user",userName + "-" + userPwd);//直接拼接不好取值
                //设置失效时间
                cookie.setMaxAge(3*24*60*60);//失效时间3天
                //相应给客户端
                response.addCookie(cookie);
            }else{
                //如果否,清空原有的cookie对象
                Cookie cookie = new Cookie("user",null);//不冲突上边是session,这个是cookie
                //删除cookie,设置maxage为0;
                cookie.setMaxAge(0);
                //相应给客户端
                response.addCookie(cookie);
            }
            //重定向跳转到index页面。改成进入到控制器
            //response.sendRedirect("index.jsp");
            response.sendRedirect("index");
        }else{//失败
            //将resultinfo对象设置到request作用域中
            request.setAttribute("resultInfo",resultInfo);
            //请求转发跳转到登陆页面
            request.getRequestDispatcher("login.jsp").forward(request,response);
        }
    }


    @Override
    public void destroy() {

    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值