话不多说,直接开始
今天是一个用了如标题所说的东西做的一个简单的登录和注册的页面。
第一步,搭建环境,这就不说了。具体看前一篇文章
如果觉得本文路径讲的有问题,或者想仔细了解路径的去看这篇文章:
servlet web项目中,URL、ApplicationContext和PathRelativetoDeploymentRoot 三个之间的关系
其实本人觉得可以不用学原生的servlet了,写起来确实不是很方便,而且页面与逻辑控制类耦合度比较高,也比较过时了。建议用spring系列的来做javaweb项目(安利springboot),这边因为课程作业,我就写了一下创建到实现注册登录,加深一下印象。
先让大家看看本章内容的项目结构吧
Dao:这里面写的就是我们进行数据操作的方法(因为内容简单,这里暂时没有做一个接口来展示方法)
Entity:实体层,这里主要是放具体的实体,比如我们这次的用户,用户的账户和密码则成为这个用户类的属性,便于封装数据,用于dao层查询
Servlet:这就是相当于一个决策层,也有的可能写成controller,主要负责处理前端业务逻辑,并决定进行何种操作。比如当收到前端登录的请求时,servlet层就可以调用dao层和entity层来帮忙一起做。做完后,再返回数据或者页面给前端,再由前端显示出来给用户看见。
Utils:就是工具层,比如过滤器或者连接数据库的一些操作就可以往这边放
(以上全是鄙人想法,大牛跳过,如有错误还请原谅)
至于我的web resource目录的结构,默认的根目录是放在WEB-INF下的,我这边嫌弃就把根目录改到了我自定义的目录jsp下面。详细还请看我的另一篇文章,这边就不进行细讲,为啥是这样的了。你也可以就按照默认的目录结构进行接下来的操作。
继续往下看吧。
先编写好一个登录和注册的页面吧
<html>
<head>
<title>登录</title>
<script type="text/javascript" src="/static/js/jquery-3.4.1.min.js"></script>
</head>
<body>
<h1>用户信息管理系统登录页面</h1>
<hr style="size: A4">
<!--这里的action是关键-->
<form method="post" action="/login">
<table>
<tr>
<td>账号:</td>
<td><input type="text" name="name" id="name" placeholder="用户名"></td>
<td><span id="NError"></span></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password" id="password" placeholder="大于六位数哦"></td>
<td><span id="PError"></span></td>
</tr>
<tr>
<td><input type="submit" id="login" value="登录"></td>
<td><input type="reset" value="重置"></td>
</tr>
</table>
</form>
<a href="register.jsp">点此注册</a>
</body>
</html>
<html>
<head>
<title>注册</title>
</head>
<body>
<h1>注册</h1>
<form method="post" action="/register">
<table>
<tr>
<td>账号:</td>
<td><input type="text" name="name" id="name" placeholder="用户名"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password" id="password" placeholder="大于六位数哦"></td>
</tr>
<tr>
<td><input type="submit" id="login" value="注册"></td>
<td><input type="reset" value="重置"></td>
</tr>
</table>
</form>
</body>
</html>
看一下网页效果图
是有点简陋哈(其实确实是很简陋了),但是吧,作为专注代码功能的程序猿,对于这个网页还是能省则省,效果到位即可,还可以节省时间,如果真要做个好看的网页,那就是专门的时候再弄啦。最开始就先凑合着用吧。
当然,上面的东西是jsp页面发布在tomcat上,显示出来的样子,接下来来看代码分析。
1.登录功能
在实现登录功能时,我们是在form表单中,填写完我们的用户名和密码,然后提交,提交后,被我们的后端给拦截,拦截之后,获取到用户名和密码,然后后端连接数据库,查找是否存在该用户,如果有的话,则登录成功,没有的话则提示用户名或密码错误。
先创建一个UserLoginServlet类,这个类的作用就是用来处理登录逻辑的,让其继承HTTPServlet,并重写doGet()和doPost()方法。
直接看代码吧,具体一定要看注解!
/**
* 这里的@WebServlet是极为重要的,这个就是为什么前端的form表单提交后能被后端写的这个userloginServlet拦截到的重要原因。
* 可以看到我们的login.jsp页面当中,form表单的action属性也是“/login”
* 而我们这里的@WebServlet中的值也是“/login”
* 当然这里的值不一定要是login,你可以随意设置。只要二者一致即可找到
*
* 当然也可以在web.xml当中配置servlet-mapping 来找对应的servlet类
*/
@WebServlet("/login")
public class UserLoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
this.doPost(request,response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/html");
request.setCharacterEncoding("utf-8"); //防止接收到前端的中文数据为乱码
response.setCharacterEncoding("utf-8");//防止前端接收到的中文数据为乱码
//输出到前端页面的一个方法,字符流
PrintWriter out = response.getWriter();
/*获取login.jsp页面提交的账号和密码
* 注意括号内的字符串应该与前端页面input标签内的name属性值相同
* getParameter() 根据表单内name属性的值获取对应参数
* 如果是input之类的,就是获取到一个String
* 如果是多个复选框,单选框之类的,name的属性值可以重复,则获取到的该是一个数组
* 如果想要获取全部的值,可以用map,比如Map<String,String[]>这种的,键值对的形式,也考虑到了一个name属性值,对应多个value
*/
String username = request.getParameter("username");
String password = request.getParameter("password");
//打印看看是否获取到
System.out.println(username+" "+password);
//实例化一个userDao
UserDao userDao = new UserDao();
//调用dao层执行登录sql语句操作,返回为一个user对象
User user = userDao.login(username,password);
//利用gson工具将我们的user封装成json数据格式
Gson gson = new Gson();
//将user对象转换成json格式的String类型
String info=gson.toJson(user);
//输出看一下
System.out.println(info);
//将数据返回前端
out.write(info);
//关闭字符流
out.flush();
out.close();
}
}
HttpServletRequest 是 请求信息,HttpServletResonse是响应信息
这里讲一下HttpServletResponse中的两个输出方法。
一个是getOutputStream(),这是一个字节流,通过response向浏览器返回图片、视频等二进制数据的文件,那么当然也可以返回文本数据(字符流)
还有一个是getWriter(),这是一个字符流,通过response向浏览器返回文本数据
这里来看几个bug吧
如果出现HttpServlet类找不到的问题,那么应该是缺少了servlet-api这个jar包,这个jar包存在于tomcat安装目录里的lib目录下
然后在project structure中的libraries下找到上述目录中的servlet-api.jar包添加进来,步骤如下
至于Gson,这是一个打包成json数据的jar包,同fastjson一样,想要下载jar包的话,可以去maven库里搜索com.google.gson.Gson,是Google开发的。
,然后同上述步骤一样加入到jar包当中。
能用是能用了,但后面执行起来之后还会出错,我们需要将其添加到web resource 下,如下图操作同样点击+号,选择gson即可添加到lib目录中
完成上述步骤后,没错,是时候该去创建userDao和User类了。也能够想到这边是要和数据库打交到的时候了,所以,我们先去创建数据库。我这边是用的mysql,通过navicat可视化平台创建的。
好啦,现在数据库建好了,那么就要进行数据库和我们java平台联系起来,那就需要一个连接的工具——JDBC
导入jdbc的jar包操作步骤和之前的一样。
看代码,一定要认真看注解。
/**
* JDBC连接数据库
*/
public class DBUtils {
/*定义四个需要用到的基础变量*/
//这个是Driver的包名
private static String driver = "com.mysql.jdbc.Driver";
//这里端口号默认是3306,如果你改过的话,就要用你改的那个。后面的javaweb是我的数据库的名称。你的用你自己的才行
private static String url = "jdbc:mysql://localhost:3306/javaweb?useUnicode=true&characterEncoding=UTF-8";
//你创建你的数据库时候的用户和密码
private static String root= "root";
private static String password = "lhyshizhu";
/*定义连接数据库的conn*/
private static Connection conn;
public static Connection getConnection(){
try {
//通过反射机制去找到driver这个包,如果在这一行报错,可能就是你的jdbc包没导入进来。
Class.forName(driver);
conn = DriverManager.getConnection(url,root,password);
System.out.println("数据库链接成功");
//获取到连接数据库的“钥匙”(conn)
return conn;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
我没有写数据库的关闭
注册类同上面的登录类,就不加注解进行解释了
package Servlet;
import Dao.UserDao;
import Entity.User;
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("/register")
public class UserRegisterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*注册这边也是要控制服务端和前端的字符格式一致,统一为UTF-8,因为这个支持全部字符格式,像GB2312只能支持部分,ios不支持中文*/
response.setContentType("text/html;charset=utf-8");
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
/*获取register.jsp页面提交的账号和密码*/
String name = request.getParameter("name");
String password = request.getParameter("password");
UserDao userDao = new UserDao();
User user = new User() ;
user.setName(name);
user.setPassword(password);
//判断是否加入成功
if(userDao.addUser(user)){
System.out.println("即将跳回到登录页面");
request.getRequestDispatcher("login.jsp").forward(request,response);
}else{
request.getRequestDispatcher("failed.jsp").forward(request,response);
}
}
}
然后再创建我们的实体类
package Entity;
public class User {
//这里的两个属性,就是按照我们数据库的字段创建的。当然名字不必一样
private String name;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
再看我们的userDao类
public class UserDao {
//表示服务器与数据库的连接
private Connection connection = null;
//PreparedStatement是表示“已准备”的sql语句
private PreparedStatement preparedStatement=null;
public User login(String username,String password){
User user = null;
//执行sql语句后得到的结果集
ResultSet resultSet;
try {
//建立连接
connection=DBUtils.getConnection();
//这里的name和password是和数据库里的字段一样的
String sql = "select * from people where name=? and password=?";
//实例化已准备好sql语句的PreparedStatement
preparedStatement=connection.prepareStatement(sql);
//这里的username和password是传进来的参数,是带有实际值的,把占位符?号用实际值来替代
preparedStatement.setString(1,username);
preparedStatement.setString(2,password);
//这个resultSet是搜索到的信息的结果集,含有一个指针,用next()来移动,取到是啥,就是啥类型
resultSet = preparedStatement.executeQuery();
if(resultSet.next()){
user = new User();
//这里的name和password指的是数据库里的字段。
user.setName(resultSet.getString("name"));
user.setPassword(resultSet.getString("password"));
//验证一下是否取到
System.out.println(user.getName());
System.out.println(user.getPassword());
System.out.println("登录成功");
}else{
System.out.println("用户名或者密码错误");
}
} catch (SQLException e) {
e.printStackTrace();
}
return user;
}
//这里是注册的sql处理逻辑
public boolean addUser(User user){
try {
connection=DBUtils.getConnection();
String sql="insert into people(name,password) values(?,?);";
preparedStatement=connection.prepareStatement(sql);
preparedStatement.setString(1,user.getName());
preparedStatement.setString(2,user.getPassword());
//用executeUpdate()可以找到是否行数发生变化,在数据库插入一条数据,会出现影响行数,这里就是那个,所以判断是否等于0
if(preparedStatement.executeUpdate()!=0){
System.out.println("创建新用户成功");
return true;
}
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
}
这里讲一下PreparedStatement,resultSet,executeQuery(),executeUpdate()
点击运行后应该就可以了
如果出现bug,这边提两个
总结一下,1.页面如果报404错误,可能就是web resource位置不对,建议重新按照前一篇文章去看看
2.如果页面报500错误,那么就应该是后端代码哪里写错了,照着servlet log 去找一下错误吧
然后这边提两个常见的问题:
1、System.out.println打印的中文信息乱码
如果出现这个错误,我们可以先去看看,setting 里面的file encoding,都设置成utf-8。
然后我们打开edit configuration,在VM options中添加上这一段代码 -Dfile.encoding=utf-8
应该就可以解决了
2、运行servlet没有反应
这个问题就是端口号被占用的问题,你可以看看你是否启动了两个tomcat,并且端口号设置成一样的了。然后关闭其中一个,或者更改其中一个端口号。
如果你自己设置的端口号启动失败了,不知道被什么占用,那么可以用cmd命令行来查找
netstat -ano | findstr “8080”
最后那个数字即是进程号PID,然后打开任务管理器,在详细信息中找到后,右键关闭掉即可