MVC模式
M: Model模型层
V:视图层
C:控制层 Controller Servlet
Pojo
首先创建一个存储数据库中各种变量名,利用注解创建了有参、无参、以及set、get那些方法
package com.atguigu.schedule.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class SysUser implements Serializable {
private Integer uid;
private String username;
private String userPwd;
}
Controller层
BaseController
这个类就是获取输入地址值然后通过/进行分割,取出最后那部分的值
package com.atguigu.schedule.controller;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
public class BaseController extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String requestURI = req.getRequestURI();
String[] split = requestURI.split("/");
String methodName =split[split.length-1];
// 通过反射获取要执行的方法
Class clazz = this.getClass();
try {
Method method=clazz.getDeclaredMethod(methodName,HttpServletRequest.class,HttpServletResponse.class);
// 设置方法可以访问
method.setAccessible(true);
// 通过反射执行代码
method.invoke(this,req,resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
SysUserController
package com.atguigu.schedule.controller;
import com.atguigu.schedule.common.Result;
import com.atguigu.schedule.common.ResultCodeEnum;
import com.atguigu.schedule.pojo.SysUser;
import com.atguigu.schedule.service.SysUserService;
import com.atguigu.schedule.service.impl.SysUserServiceImpl;
import com.atguigu.schedule.util.MD5Util;
import com.atguigu.schedule.util.WebUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import static com.atguigu.schedule.common.ResultCodeEnum.USERNAME_USED;
@WebServlet("/user/*")
public class SysUserController extends BaseController {
SysUserService userService = new SysUserServiceImpl();
/**
* 接收用登录请求,完成的登录业务接口
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1 接收用户名和密码
String username = req.getParameter("username");
String userPwd = req.getParameter("userPwd");
//2 调用服务层方法,根据用户名查询用户信息
SysUser loginUser = userService.findByUsername(username);
if (null == loginUser) {
// 跳转到用户名有误提示页
resp.sendRedirect("/loginUsernameError.html");
} else if (!MD5Util.encrypt(userPwd).equals(loginUser.getUserPwd())) {
//3 判断密码是否匹配
// 跳转到密码有误提示页
resp.sendRedirect("/loginUserPwdError.html");
} else {
//4 跳转到首页
req.getSession().setAttribute("sysUser", loginUser);
resp.sendRedirect("/showSchedule.html");
}
}
/**
* 接收用户注册请求的业务处理方法( 业务接口 不是java中的interface )
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void regist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1 接收客户端提交的参数
String username = req.getParameter("username");
String userPwd = req.getParameter("userPwd");
// 2 调用服务层方法,完成注册功能
//将参数放入一个SysUser对象中,在调用regist方法时传入
SysUser sysUser = new SysUser(null, username, userPwd);
int rows = userService.regist(sysUser);
// 3 根据注册结果(成功 失败) 做页面跳转
if (rows > 0) {
resp.sendRedirect("/registSuccess.html");
} else {
resp.sendRedirect("/registFail.html");
}
}
/**
* SysUserController下,注册时校验用户名是否被占用的业务接口
*
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void checkUsernameUsed(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
SysUser registUser = userService.findByUsername(username);
//封装结果对象
Result result=null;
if(null ==registUser){
// 未占用,创建一个code为200的对象
result= Result.ok(null);
}else{
// 占用, 创建一个结果为505的对象
result= Result.build(null, ResultCodeEnum.USERNAME_USED);
}
// 将result对象转换成JSON并响应给客户端
WebUtil.writeJson(resp,result);
}
}
问题一
Controller层的代码实现,继承了上面那个类,去获取输入地址中最后那个值进行匹配(这里说明一下,开始测试普通方法add时在控制台打印输出是没有问题的,但是改成login和regist方法之后进行响应重定向始终无法实现跳转,只能跳转到localhost:8080/user/login或者localhost:8080/user/regist上面去)
问题二
checkUsernameUsed(),此方法用来测试用户在注册时,用户名是否已经被占用,由于数据库中已经存在zhangsan用户名,我在注册测试的时候应该返回“已占用”,但是用户名之后的span框并没有任何提示
Service层
接口
package com.atguigu.schedule.service;
import com.atguigu.schedule.pojo.SysUser;
public interface SysUserService {
/**
* 根据用户名获得完整用户信息的方法
* @param username 要查询的用户名
* @return 如果找到了返回SysUser对象,找不到返回null
*/
SysUser findByUsername(String username);
/**
* 用户完成注册的业务方法
* @param registUser 用于保存注册用户名和密码的对象
* @return 注册成功返回>0的整数,否则返回0
*/
int regist(SysUser registUser);
}
具体实现类
package com.atguigu.schedule.service.impl;
import com.atguigu.schedule.dao.SysUserDao;
import com.atguigu.schedule.dao.impl.SysUserDaoImpl;
import com.atguigu.schedule.pojo.SysUser;
import com.atguigu.schedule.service.SysUserService;
import com.atguigu.schedule.util.MD5Util;
public class SysUserServiceImpl implements SysUserService {
SysUserDao userDao =new SysUserDaoImpl();
@Override
public SysUser findByUsername(String username) {
// 调用服务层方法,继续查询
return userDao.findByUsername(username);
}
@Override
public int regist(SysUser sysUser) {
// 将用户的明文密码转换为密文密码
sysUser.setUserPwd(MD5Util.encrypt(sysUser.getUserPwd()));
// 调用DAO 层的方法 将sysUser信息存入数据库
return userDao.addSysUser(sysUser);
}
}
Dao层
BaseDao
主要是提供dao层与数据库连接的一些方法
package com.atguigu.schedule.dao;
import com.atguigu.schedule.util.JDBCUtil;
import java.lang.reflect.Field;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
public class BaseDao {
// 公共的查询方法 返回的是单个对象
public <T> T baseQueryObject(Class<T> clazz, String sql, Object ... args) {
T t = null;
Connection connection = JDBCUtil.getConnection();
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
int rows = 0;
try {
// 准备语句对象
preparedStatement = connection.prepareStatement(sql);
// 设置语句上的参数
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i + 1, args[i]);
}
// 执行 查询
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
t = (T) resultSet.getObject(1);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != resultSet) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (null != preparedStatement) {
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
JDBCUtil.releaseConnection();
}
return t;
}
// 公共的查询方法 返回的是对象的集合
public <T> List<T> baseQuery(Class clazz, String sql, Object ... args){
List<T> list =new ArrayList<>();
Connection connection = JDBCUtil.getConnection();
PreparedStatement preparedStatement=null;
ResultSet resultSet =null;
int rows = 0;
try {
// 准备语句对象
preparedStatement = connection.prepareStatement(sql);
// 设置语句上的参数
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i+1,args[i]);
}
// 执行 查询
resultSet = preparedStatement.executeQuery();
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
// 将结果集通过反射封装成实体类对象
while (resultSet.next()) {
// 使用反射实例化对象
Object obj =clazz.getDeclaredConstructor().newInstance();
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnLabel(i);
Object value = resultSet.getObject(columnName);
// 处理datetime类型字段和java.util.Data转换问题
if(value.getClass().equals(LocalDateTime.class)){
value= Timestamp.valueOf((LocalDateTime) value);
}
Field field = clazz.getDeclaredField(columnName);
field.setAccessible(true);
field.set(obj,value);
}
list.add((T)obj);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null !=resultSet) {
try {
resultSet.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (null != preparedStatement) {
try {
preparedStatement.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
JDBCUtil.releaseConnection();
}
return list;
}
// 通用的增删改方法
public int baseUpdate(String sql,Object ... args) {
// 获取连接
Connection connection = JDBCUtil.getConnection();
PreparedStatement preparedStatement=null;
int rows = 0;
try {
// 准备语句对象
preparedStatement = connection.prepareStatement(sql);
// 设置语句上的参数
for (int i = 0; i < args.length; i++) {
preparedStatement.setObject(i+1,args[i]);
}
// 执行 增删改 executeUpdate
rows = preparedStatement.executeUpdate();
// 释放资源(可选)
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (null != preparedStatement) {
try {
preparedStatement.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
JDBCUtil.releaseConnection();
}
// 返回的是影响数据库记录数
return rows;
}
}
接口
package com.atguigu.schedule.dao;
import com.atguigu.schedule.pojo.SysUser;
public interface SysUserDao {
/**
* 根据用户名获得完整用户信息的方法
* @param username 要查询的用户名
* @return 如果找到了返回SysUser对象,找不到返回null
*/
SysUser findByUsername(String username);
/**
* 向数据库中增加一条用户记录的方法
* @param sysUser 要增加的记录的username和user_pwd字段以SysUser实体类对象的形式接收
* @return 增加成功返回1 增加失败返回0
*/
int addSysUser(SysUser sysUser);
}
具体实现类
package com.atguigu.schedule.dao.impl;
import com.atguigu.schedule.dao.BaseDao;
import com.atguigu.schedule.dao.SysUserDao;
import com.atguigu.schedule.pojo.SysUser;
import java.util.List;
public class SysUserDaoImpl extends BaseDao implements SysUserDao {
@Override
public SysUser findByUsername(String username) {
String sql ="select uid,username, user_pwd userPwd from sys_user where username = ?";
List<SysUser> userList = baseQuery(SysUser.class, sql, username);
return null != userList&& userList.size()>0? userList.get(0):null;
}
@Override
public int addSysUser(SysUser sysUser) {
String sql ="insert into sys_user values(DEFAULT,?,?)";
return baseUpdate(sql,sysUser.getUsername(),sysUser.getUserPwd());
}
}
过滤器
package com.atguigu.schedule.filter;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
@WebFilter(urlPatterns = {"/showSchedule.html","/schedule/*"})
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request =(HttpServletRequest) servletRequest;
HttpServletResponse response =(HttpServletResponse) servletResponse;
HttpSession session = request.getSession();
Object sysUser = session.getAttribute("sysUser");
if(null != sysUser){
// session中如果存在登录的用户 代表用户登录过,则放行
filterChain.doFilter(servletRequest,servletResponse);
}else{
//用户未登录,重定向到登录页
response.sendRedirect("/login.html");
}
}
}
问题三
(其实这个也不算是疑惑问题,只是我以为与前两个问题原因一样)
过滤器目的想实现功能就是在用户未登陆情况下自动跳转到登陆页面,结合问题一和问题二,我以为是响应重定向的问题,但是这个过滤器功能却实现了???
工具类
JDBCUtil
package com.atguigu.schedule.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCUtil {
private static ThreadLocal<Connection> threadLocal =new ThreadLocal<>();
private static DataSource dataSource;
// 初始化连接池
static{
// 可以帮助我们读取.properties配置文件
Properties properties =new Properties();
InputStream resourceAsStream = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
try {
properties.load(resourceAsStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/*1 向外提供连接池的方法*/
public static DataSource getDataSource(){
return dataSource;
}
/*2 向外提供连接的方法*/
public static Connection getConnection(){
Connection connection = threadLocal.get();
if (null == connection) {
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
threadLocal.set(connection);
}
return connection;
}
/*定义一个归还连接的方法 (解除和ThreadLocal之间的关联关系) */
public static void releaseConnection(){
Connection connection = threadLocal.get();
if (null != connection) {
threadLocal.remove();
// 把连接设置回自动提交的连接
try {
connection.setAutoCommit(true);
// 自动归还到连接池
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
MD5Util
这个工具类是在注册的时候将明文密码转成密文格式存储到数据库的sys_user表中
package com.atguigu.schedule.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public final class MD5Util {
public static String encrypt(String strSrc) {
try {
char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f' };
byte[] bytes = strSrc.getBytes();
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(bytes);
bytes = md.digest();
int j = bytes.length;
char[] chars = new char[j * 2];
int k = 0;
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
chars[k++] = hexChars[b >>> 4 & 0xf];
chars[k++] = hexChars[b & 0xf];
}
return new String(chars);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("MD5加密出错!!!");
}
}
}
WebUtil
暂时还只用到writeJson方法,就是前后端的一个连接,返回数据
package com.atguigu.schedule.util;
import com.atguigu.schedule.common.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.text.SimpleDateFormat;
public class WebUtil {
private static ObjectMapper objectMapper;
// 初始化objectMapper
static{
objectMapper=new ObjectMapper();
// 设置JSON和Object转换时的时间日期格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
// 从请求中获取JSON串并转换为Object
public static <T> T readJson(HttpServletRequest request,Class<T> clazz){
T t =null;
BufferedReader reader = null;
try {
reader = request.getReader();
StringBuffer buffer =new StringBuffer();
String line =null;
while((line = reader.readLine())!= null){
buffer.append(line);
}
t= objectMapper.readValue(buffer.toString(),clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
return t;
}
// 将Result对象转换成JSON串并放入响应对象
public static void writeJson(HttpServletResponse response, Result result){
response.setContentType("application/json;charset=UTF-8");
try {
String json = objectMapper.writeValueAsString(result);
response.getWriter().write(json);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
前端代码
登陆页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.ht{
text-align: center;
color: cadetblue;
font-family: 幼圆;
}
.tab{
width: 500px;
border: 5px solid cadetblue;
margin: 0px auto;
border-radius: 5px;
font-family: 幼圆;
}
.ltr td{
border: 1px solid powderblue;
}
.ipt{
border: 0px;
width: 50%;
}
.btn1{
border: 2px solid powderblue;
border-radius: 4px;
width:60px;
background-color: antiquewhite;
}
#usernameMsg , #userPwdMsg {
color: gold;
}
.buttonContainer{
text-align: center;
}
</style>
<script>
// 校验用户名的方法
function checkUsername(){
var usernameReg = /^[a-zA-Z0-9]{5,10}$/
var username = document.getElementById("usernameInput");
var namevalue = username.value;
var usernameMsg = document.getElementById("usernameMsg");
if (!usernameReg.test(namevalue)){
usernameMsg.innerText = "用户名必须为5-10数字或字母"
return false
}
usernameMsg.innerText = "ok"
return true
}
// 校验密码的方法
function checkUserPwd(){
var usernameReg = /^[0-9]{6}$/
var userpwd = document.getElementById("userPwdInput");
var pwdvalue = userpwd.value;
var userPwdMsg = document.getElementById("userPwdMsg");
if (!usernameReg.test(pwdvalue)){
userPwdMsg.innerText = "密码必须为6位数字"
return false
}
userPwdMsg.innerText = "ok"
return true
}
//表单提交时统一校验
function checkForm(){
var flag1 = checkUsername();
var flag2 = checkUserPwd();
return flag1&&flag2;
}
</script>
</head>
<body>
<h1 class="ht">欢迎使用日程管理系统</h1>
<h3 class="ht">请登录</h3>
<form method="post" action="/user/login" οnsubmit="return checkForm()">
<table class="tab" cellspacing="0px">
<tr class="ltr">
<td>请输入账号</td>
<td>
<input class="ipt" type="text" id="usernameInput" name="username" οnblur="checkUsername()">
<span id="usernameMsg"></span>
</td>
</tr>
<tr class="ltr">
<td>请输入密码</td>
<td>
<input class="ipt" type="password" id="userPwdInput" name="userPwd" οnblur="checkUserPwd()">
<span id="userPwdMsg"></span>
</td>
</tr>
<tr class="ltr">
<td colspan="2" class="buttonContainer">
<input class="btn1" type="submit" value="登录">
<input class="btn1" type="reset" value="重置">
<button class="btn1"><a href="regist.html">去注册</a></button>
</td>
</tr>
</table>
</form>
</body>
</html>