欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【JSP+Servlet】实现登录和注册

程序员文章站 2022-05-06 13:33:12
...
  • JSP+SerVlet,对Servlet和Dao层进行优化,抽取出共同代码

  • 只实现了简单的登录和注册,用户名查重、记住用户名和密码、验证码、表单校验并没有实现

  • ???? GitHub源码地址



前端界面

【JSP+Servlet】实现登录和注册【JSP+Servlet】实现登录和注册

代码结构

【JSP+Servlet】实现登录和注册【JSP+Servlet】实现登录和注册

1. 建立登录注册相关模型(Bean层)

  • 建立用户表user

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(100) DEFAULT NULL,
  `password` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
  • 建立对应的User类

/**
 * 对应数据库中的user表
 */
public class User {

    private Integer id;
    private String username;
    private String password;

    public User(Integer id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;

    }
    // 无参构造函数必须得有
    public User() {
        super();
    }

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



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


2. 建立Utils层获取数据库连接

使用 JDBC + c3p0 获取数据库连接

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等。

导包并右键 add as library
【JSP+Servlet】实现登录和注册
JDBCUtils

/**
 * 获取数据库连接的工具类
 *
 * @author smallbeef
 *
 */
public class JDBCUtils {
	// 此处要与c3p0的配置文件中的name一致
    private static ComboPooledDataSource dataSource = new ComboPooledDataSource(
            "c3p0Config");

    private JDBCUtils() {
    }

    /**
     * 获取数据库连接
     *
     * @return 如果获取连接成功,返回数据的连接对象。<br/>
     *         如果获取数据库连接失败,则返回null
     */
    public static Connection getConnection() {
        Connection connection = null;
        try {
            // 从c3p0中获取数据库连接
            connection = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return connection;
    }

    /**
     * 释放数据库连接
     */
    public static void closeConnection(Connection conn) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        getConnection();
    }
}

c3p0-config.xml

<c3p0-config>
	<!-- 用户自定义配置 -->
	<named-config name="c3p0Config">
			<property name="driverClass">com.mysql.jdbc.Driver</property>
			<property name="jdbcUrl">jdbc:mysql://localhost:3306/userandbook</property>
			<property name="user">root</property>
			<property name="password">root</property>
			<!-- 最大连接数 -->
			<property name="maxPoolSize">10</property>
			<!-- 最小连接数 -->
			<property name="minPoolSize">5</property>
			<!-- 初始化连接数,连接池创建初始化连接数 -->
			<property name="initialPoolSize">3</property>
			<!-- 多长时间检测一下链接的可用性,以秒做单位-->
			<property name="idleConnectionTestPeriod">3600</property>
			<!-- 如果连接处不够用,一次性创建多少链接 -->
			<property name="acquireIncrement">5</property>
			<!-- 链接的最大空闲时间,以分钟做为单位 -->
			<property name="maxIdleTime">50</property>
	</named-config>	

</c3p0-config>

应当把c3p0-config.xml 放在 src 下,不然 new ComboPooledDataSource(“configName"); 可能会找不到 c3p0-config.xml 文件

数据库连接测试

public class JDBCUtilTest {

	@Test
	public void testGetConnection() {
		Connection conn = JDBCUtils.getConnection();
		System.out.println(conn);
		JDBCUtils.closeConnection(conn);
	}
	
}

【JSP+Servlet】实现登录和注册
连接成功!


3. 建立Dao层操作数据库数据

  • 建立BaseDao, 专门被其他Dao继承,BaseDao定义一些基础方法(泛型)

    导包:DButils
    使用 DBUtils的QueryRunner.update / query 方法实现增删改查

    DbUtils是一种 JDBC Utility Component (翻译过来大概就是:JDBC实用部件),故名思意,和数据库操作有关。官网上的简介也称之为 JDBC helper library ,由此可知,DbUtils是一个工具类库,用来进行数据库通信的
    ???? 【JavaWeb】DbUtils入门之QueryRunner

    BaseDao

public class BaseDao<T> {
    // 需要获取实际的type
    private Class<T> type;

    private QueryRunner queryRunner = new QueryRunner();

    public BaseDao(){
        // 获取父类的类型,父类是带参数的
        ParameterizedType superclass = (ParameterizedType) this.getClass().getGenericSuperclass();
        System.out.println(superclass.getClass());
        // 获取泛型中的具体的类型的class
        type = (Class<T>) superclass.getActualTypeArguments()[0];
    }

    /**
     * 获取一个对象
     * @param sql  sql语句
     * @param params 可变参数
     * @return
     */
    public T getBean(String sql, Object...params){
        Connection connection = JDBCUtils.getConnection();
        T query = null;
        try {
            // 查询一个数据,BeanHandler封装一个对象
            query = queryRunner.query(connection,sql,new BeanHandler<>(type),params);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeConnection(connection);
        }
        return query;
    }

    /**
     * 获取对象的集合
     * @return
     */
    public List<T> getBeanList(String sql, Object...params){
        Connection connection = JDBCUtils.getConnection();
        List<T> query = null;
        try {
            // 查询一组数据,BeanListHandler封装一组对象
            query = queryRunner.query(connection,sql,new BeanListHandler<>(type),params);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeConnection(connection);
        }

        return query;
    }

    
    /**
     * 执行增删改
     */
    public int update(String sql, Object ...params){
        int count = 0;
        Connection connection = JDBCUtils.getConnection();
        try {
            count = queryRunner.update(connection, sql, params);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeConnection(connection);
        }
        return count;
    }


}

  • UserDao: 继承BaseDao操作User表

    面向接口编程 : 定义UserDao接口UserDaoImpl 实现
/**
 * UserDAO具体的实现类
 *
 * @author smallbeef
 *
 */
public class UserDaoImp extends BaseDao<User> implements UserDao {

    /**
     * 按照用户名和密码查询信息
     * @param user
     * @return
     */
    @Override
    public User getUserByUserNameAndPassWord(User user) {
        String sql = "select id,username,password from user where username = ? and password = ?";
        User bean = this.getBean(sql, user.getUsername(), user.getPassword());
        return bean;
    }

    /**
     * 注册 保存用户
     * @param user
     * @return
     */
    @Override
    public boolean registUser(User user) {
        String sql = "insert into user(`id`,`username`,`password`) values(null, ?, ?)";
        int update = this.update(sql, user.getUsername(), user.getPassword());
        if(update>0){
            return true;
        }else
            return false;
    }
}

4. 建立service层

  • UserService 完成用户的登录注册功能

public class UserServiceImp implements UserService {
    private UserDao ud = new UserDaoImp();

    @Override
    public User login(User user) {
        return ud.getUserByUserNameAndPassWord(user);
    }

    @Override
    public boolean regist(User user) {
        return ud.registUser(user);
    }
}

5. 建立servlet层:处理用户请求

一般是建立 LoginServletRegistServlet , 注册和登录界面表单分别调用不同的servlet。

此处我们希望一个UserServlet就能够处理注册和登录请求,让用户的注册和登录请求都经过同一个Userservlet,而不是分别经过loginservlet和registservlet。

实现方法:给form表单带上method参数请求UserServlet,UserServlet通过这个参数判断是注册请求还是登录请求

<form action="/UserServlet?method=login" method="post">

<form action="/UserServlet?method=regist" method="post">

【JSP+Servlet】实现登录和注册
UserServelt

@WebServlet(name = "UserServlet", urlPatterns = "/UserServlet")
public class UserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String method = request.getParameter("method");
        System.out.println(method);
        if("regist".equals(method)){
            //注册
            regist(request,response);
        }
        if("login".equals(method)){
            //登录
            login(request,response);
        }
    }

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

    private UserService us = new UserServiceImp();

    protected void regist(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        boolean b = us.regist(new User(null, username, password));
        if(b){
            //注册成功,返回成功页面,重定向
            request.getRequestDispatcher("/pages/regist_success.html").forward(request,response);

        }else{
            //注册失败,返回失败页面,转发
            response.sendRedirect(request.getContextPath()+"/pages/error.jsp");
        }
    }

    protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        User user = us.login(new User(null, username, password));
        if(user == null){
            //登录失败,返回登录页面,转发(换界面)
            request.getRequestDispatcher("/pages/error.jsp").forward(request,response);
        }else{
            //登录成功,返回成功界面 重定向(直接刷新界面)
            response.sendRedirect(request.getContextPath() + "/pages/login_success.html");
        }
    }
}

为了不陷入层层的if判断,我们利用反射来优化此代码

反射:你指定一个方法名,我找到这个方法并直接调用)

public class UserServlet extends HttpServlet {   
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取要调用的方法
        String method = request.getParameter("method");
        System.out.println(method);
        // getDeclaredMethod(方法名,参数列表(传的是各个参数的类))

        // 通过反射找到该方法并调用
        try {
            Method declaredMethod = this.getClass().getDeclaredMethod(method, HttpServletRequest.class, HttpServletResponse.class);
            // 把方法权限设大
            declaredMethod.setAccessible(true);
            // invoke(对象,参数) 执行该方法
            declaredMethod.invoke(this,request,response);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
//        if("regist".equals(method)){
//            //注册
//            regist(request,response);
//        }
//        if("login".equals(method)){
//            //登录
//            login(request,response);
//        }
    }

所有的servlet都是通过反射来调用相应的方法,所以我们可以抽取出一个BaseServlet

抽取之后Userservlet只需要编写相关逻辑代码就可以了,以后定义的任何方法只需要在form表单上带上方法名即可

【JSP+Servlet】实现登录和注册

BaseServlet

public class BaseServlet extends HttpServlet {
//    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doPost(request, response);
    }

//    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 解决post乱码问题,设置请求的参数字符集为UTf-8
        request.setCharacterEncoding("UTF-8");
        // 获取要调用的方法
        String method = request.getParameter("method");
        System.out.println(method);

        // 通过反射找到该方法并调用
        // getDeclaredMethod(方法名,参数列表(传的是各个参数的类))
        try {
            Method declaredMethod = this.getClass().getDeclaredMethod(method, HttpServletRequest.class, HttpServletResponse.class);
            // 把方法权限设大
            declaredMethod.setAccessible(true);
            // invoke(对象,参数) 执行该方法
            declaredMethod.invoke(this,request,response);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }
}

UserServlet

@WebServlet(name = "UserServlet" ,urlPatterns = "/UserServlet")
public class UserServlet extends BaseServlet {


    private UserService us = new UserServiceImp();

    protected void regist(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");

        boolean b = us.regist(new User(null, username, password));
        if(b){
            //注册成功,返回成功页面,重定向
            request.getRequestDispatcher("/pages/regist_success.html").forward(request,response);

        }else{
            //注册失败,返回注册页面,转发
            response.sendRedirect(request.getContextPath()+"/pages/error.html");
        }
    }

    protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        User user = us.login(new User(null, username, password));
        if(user == null){
            //登录失败,返回登录页面,转发(换界面)
            request.getRequestDispatcher("/pages/error.html").forward(request,response);
        }else{
            //登录成功,返回成功界面 重定向(直接刷新界面)
            response.sendRedirect(request.getContextPath() + "/pages/login_success.html");
        }
    }
}

一连串的request.getParameter取参过程其实也可以抽取出来,此处就不做了


注意修改域名为默认:
【JSP+Servlet】实现登录和注册