🐀Java后端经典三层架构
分层 | 对应包 | 说明 |
---|---|---|
web层 | com.zzw.furns.web/servlet/controller/handler | 接受浏览器发送数据; 调用相关的service;根据执行结果,返回页面数据 |
service层 | com.zzw.furns.service | Service接口包 |
com.zzw.furns.service.impl | Service接口实现类 | |
dao持久层 | com.zzw.furns.dao | Dao接口包 |
com.zzw.furns.dao.impl | Dao接口实现类 | |
实体bean对象 | com.zzw.furns.pojo/entity/domain/bean | JavaBean类 |
工具类 | com.zzw.furns.utils | 工具类 |
测试包 | com.zzw.furns.test | 完成对dao/service测试 |
🐇MVC模型
MVC全称: Model模型, View试图, Controller控制器
MVC最早出现在JavaEE三层中的Web层, 它可以有效地指导WEB层代码如何有效地分离, 单独工作
- View试图: 只负责数据和界面的显示, 不接受任何与显示数据无关的代码, 便于程序员和美工的分工与合作(Vue/Jsp/Thymeleaf/Html)
- Controller控制器: 只负责接收请求, 调用业务层的代码处理请求, 然后派发给页面, 是一个"调度者"的角色
- Model模型: 将与业务逻辑相关的数据封装为具体的JavaBean类, 其中不掺杂任何与数据处理相关的代码(JavaBean/Domain/Pojo)
解读
- model 最早期就是javabean, 就是早期的jsp+servlet+javabean
- 后面业务复杂度越来越高, model逐渐分层化/组件化(service+dao)
- 后面又出现了持久化技术(service+dao+持久化技术(hibernate / mybatis / mybatis-plus))
- MVC依然是原来的mvc, 只是变得更加强大
🐇开发环境搭建
1.新建Java项目 jiaju_mall
2.导入jar包
3.项目的结构
4.下载资源, 拷贝到web路径下
html转成jsp, 并把页面调整也以下目录
views/member/login.jsp
views/member/register_ok.jsp
views/member/register_fail.jsp
views/member/login_ok.jsp
web/index.jsp
1)html页面转为jsp页面要做的处理
将页面内应用的.html页面改成.jsp
将页面内的相对路径重新修改
如果有需要, 在页面顶部引入c标签
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
如果是html页面, base标签不能使用el表达式, 要这样写:
5.Rebuild project, 让项目识别到这些资源, 然后再启动Tomcat
6.对于复杂的前端页面, 要学会打开当前页面的结构, 提高工作效率
🐇会员注册
🍉前端JS校验
1.验证用户名:必须字母,数字下划线组成,并且长度为 6 到 10 位 => 正则表达式
2.验证密码:必须由字母,数字下划线组成,并且长度为 6 到 10 位
3.邮箱格式验证:常规验证即可
4.验证码:后面实现
代码实现
1.修改web/views/member/login.html
, 增加(script在src属性引文件)
<script type="text/javascript" src="../../script/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$(function () {//页面加载完毕后执行 function
$("#sub-btn").click(function () {
//采用过关斩将法
//正则表达式验证用户名
var usernameValue = $("#username").val();
var usernamePattern = /^\w{6,10}$/;
if (!usernamePattern.test(usernameValue)) {
$("span[class='errorMsg']").text("用户名格式不对, 需要6-10个字符(大小写字母,数字,下划线)");
return false;
}
//验证密码
var passwordValue = $("#password").val();
var passwordPattern = /^\w{6,10}$/;
if (!passwordPattern.test(passwordValue)) {
$("span.errorMsg").text("密码格式不对, 需要6-10个字符(大小写字母,数字,下划线)");
return false;
}
//两次密码要相同
var rePwdValue = $("#repwd").val();
if (passwordValue != rePwdValue) {
$("span.errorMsg").text("两次密码不相同");
return false;
}
//这里仍然采用过关斩将法
//验证邮件
var emailVal = $("#email").val();
//在java中, 正则表达式的转义是\\; 在js中, 正则表达式转义是\
var emailPattern = /^[\w-]+@([a-zA-Z]+\.)+[a-zA-Z]+$/;
if (!emailPattern.test(emailVal)) {
$("span.errorMsg").text("电子邮件的格式不正确, 请重新输入");
return false;
}
//这里暂时不提交=>显示验证通过
$("span.errorMsg").text("验证通过");
return false;
});
})
</script>
2.测试
🍉后端实现
思路分析
- 会员注册信息, 验证通过后
- 提交给服务器, 如果用户名在数据库中已经存在, 后端给出提示信息, 并返回重新注册
- 如果用户名没有在数据库中, 完成注册, 并返回注册成功的页面
程序框架图
代码实现
1.创建数据库和表
-- 创建家居网购需要的数据库和表
-- 删除数据库
DROP DATABASE IF EXISTS home_furnishing;
-- 删除表
DROP TABLE member;
-- 创建数据库
CREATE DATABASE home_furnishing;
-- 切换
USE home_furnishing;
-- 创建会员表
CREATE TABLE member (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32) NOT NULL DEFAULT '' UNIQUE,
`password` VARCHAR(32) NOT NULL,
email VARCHAR(64)
)CHARSET utf8 ENGINE INNODB;
-- 插入数据
INSERT INTO member VALUES(NULL, 'admin', MD5('admin'), '978964140@qq.com');
INSERT INTO member VALUES(NULL, 'zhaozhiwei', MD5('zhaozhiwei'), '978964140@qq.com');
INSERT INTO member(id, username, `password`, email) VALUES(NULL, 'tom', MD5('tom'), 'tom@sohu.com');
-- 查询
SELECT * FROM member;
SELECT id, username, `password`, email FROM member WHERE username = 'admine'
SELECT id, username, `password`, email FROM member WHERE username = 'admin123' AND `password` = MD5('123456');
UPDATE member SET `password`=MD5('admin') WHERE username='admin';
2.创建实体类src/com/zzw/furns/entity/Member.java
满汉楼项目参考
无参构造器和set方法. get方法
public class Member {
private Integer id;
private String username;
private String password;
private String email;
//无参构造器, 底层反射用
public Member() {
}
//提供了有参构造器,必须提供无参构造器
public Member(Integer id, String username, String password, String email) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
}
}
3.从满汉楼项目引入BasicDAO.java, JdbcUtilsByDruid.java, Druid.properties
4.修改Druid配置文件要连接的数据库名, 确保用户名密码正确. url后面是做批处理用的
5.修改JdbcUtilsByDruid的路径
6.配置快捷键
7.测试, 新建src/com/zzw/furns/test/JdbcUtilsByDruidTest.java
@SuppressWarnings({"all"})
public class JdbcUtilsByDruidTest {
@Test
public void getConnection() {
Connection connection = JdbcUtilsByDruid.getConnection();
System.out.println(connection);
JdbcUtilsByDruid.close(null, null, connection);
}
}
8.新建src/com/zzw/furns/dao/MemberDao.java
public interface MemberDAO {
//小伙伴需要自己分析, 需要哪些方法
//提供一个通过用户名返回对应的Member
public Member queryMemberByUsername(String username);
//提供一个 保存Member对象到数据库Member表 的方法
public int saveMember(Member member);
}
9.新建src/com/zzw/furns/dao/impl/MemberDaoImpl.java
public class MemberDAOImpl extends BasicDAO<Member> implements MemberDAO {
/**
* 通过用户名返回对应的Member
* @param username 用户名
* @return 对应的Member, 如果没有该Member返回null
*/
@Override
public Member queryMemberByUsername(String username) {
//现在sqlyog测试, 然后再拿到程序中, 这样可以提高我们的开发效率, 减少不必要的bug
String sql = "SELECT id, username, `password`, email FROM member WHERE username = ?";
Member member = querySingle(sql, Member.class, username);
return member;
}
/**
* 保存一个会员
* @param member 传入一个Member对象
* @return 如果返回-1, 就是失败; 返回其它的数字, 就是受影响的行数
*/
@Override
public int saveMember(Member member) {
//连同单引号一并换成 ? , 它会自动加上单引号
String sql = "INSERT INTO member(id, username, `password`, email) " +
"VALUES(NULL, ?, MD5(?), ?)";
int updateRows = update(sql, member.getUsername(), member.getPassword(), member.getEmail());
return updateRows;
}
}
10.测试, 新建src/com/zzw/furns/test/MemberDAOTest.java
public class MemberDAOTest {
private MemberDAO memberDAO = new MemberDAOImpl();
@Test
public void queryMemberByUsernameTest() {
if (memberDAO.queryMemberByUsername("tome") == null) {
System.out.println("该用户名不存在");
} else {
System.out.println("该用户名存在");
}
}
@Test
public void saveMember() {
Member member =
new Member(null, "king", "king", "king@sohu.com");
if (memberDAO.saveMember(member) == 1) {
System.out.println("添加成功");
} else {
System.out.println("添加失败");
}
}
}
11.新建src/com/zzw/furns/service/MemberService.java
public interface MemberService {
//注册用户
public boolean registerMember(Member member);
//判断用户名是否存在
public boolean isExistsByUsername(String username);
}
12.新建src/com/zzw/furns/service/MemberServiceImpl.java
public class MemberServiceImpl implements MemberService {
//定义MemberDAO属性
private MemberDAO memberDAO = new MemberDAOImpl();
/**
* 判断用户名是否存在
*
* @param username 用户名
* @return 如果存在返回true, 否则返回false
*/
@Override
public boolean isExistsByUsername(String username) {
//小技巧: 如果看某个方法:
// (1)ctrl+b 定位到memberDAO的编译类型中的方法
// (2)如果使用ctrl+alt+b 会定位到实现类的方法
//如果有多个类实现了该方法, 会让你选择
return memberDAO.queryMemberByUsername(username) == null ? false : true;
}
@Override
public boolean registerMember(Member member) {
return memberDAO.saveMember(member) == 1 ? true : false;
}
}
13.测试, 新建src/com/zzw/furns/test/MemberServiceTest.java
public class MemberServiceTest {
private MemberService memberService = new MemberServiceImpl();
@Test
public void isExistsByUsernameTest() {
if (memberService.isExistsByUsername("king")) {
System.out.println("用户名存在");
} else {
System.out.println("用户名不存在");
}
}
@Test
public void registerMember() {
//构建一个用来测试的Member对象
Member member = new Member(null, "tom", "tom", "tom@sohu.com");
if (memberService.registerMember(member)) {
System.out.println("注册用户成功");
} else {
System.out.println("注册用户失败");
}
}
}
14.接通web层, 新建src/com/zzw/furns/web/RegisterServlet.java
public class RegisterServlet extends HttpServlet {
private MemberService memberService = new MemberServiceImpl();
@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 {
String username = request.getParameter("username");
String password = request.getParameter("password");
String email = request.getParameter("email");
Member member = new Member(null, username, password, email);
if (!memberService.isExistsByUsername(member)) {
//用户名可用
if (memberService.registerMember(member)) {
System.out.println("注册成功");
request.getRequestDispatcher("/views/member/register_ok.jsp")
.forward(request, response);
} else {
System.out.println("注册失败");
request.getRequestDispatcher("/views/member/register_fail.jsp")
.forward(request, response);
}
} else {
//用户名不可用
request.setAttribute("msg", "用户名" + username + "不可用");
request.setAttribute("username", username);//回显用户名
request.setAttribute("active", "register_tab");
request.getRequestDispatcher("/views/member/login.jsp")
.forward(request, response);
}
}
}
<servlet>
<servlet-name>RegisterServlet</servlet-name>
<servlet-class>com.zzw.furns.web.RegisterServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>RegisterServlet</servlet-name>
<url-pattern>/register</url-pattern>
</servlet-mapping>
15.前端(错误信息回显), 修改web/views/member/login.jsp
2)cart.jsp, checkout.jsp, order.jsp,order_detail.jsp均可跳转
<!-- Header Logo Start -->
<div class="col-auto align-self-center">
<div class="header-logo">
<a href="index.jsp"><img src="assets/images/logo/logo.png" alt="Site Logo"/></a>
</div>
</div>
<!-- Header Logo End -->
<!-- Header Action Start -->
<div class="col align-self-center">
<div class="header-actions">
<!-- Single Wedge Start -->
欢迎: ${sessionScope.member.username}
<div class="header-bottom-set dropdown">
<a href="orderServlet?action=listOrderByMemberId">订单管理</a>
</div>
<div class="header-bottom-set dropdown">
<a href="memberServlet?action=logout">安全退出</a>
</div>
<!-- Single Wedge End -->
</div>
</div>
<!-- Header Action End -->
3)修改web/views/member/login.jsp
注册表单
<!--会员注册-->
<span class="errorMsg"
style="float: right; font-weight: bold; color: lightgray; font-size: 20pt; margin-left: 10px;">${msg}</span>
<form action="registerServlet" method="post">
</form>
<a id="register_tab" data-bs-toggle="tab" href="#lg2">
<h4>会员注册</h4>
</a>
4)修改loign.jsp
- 注册失败回显信息时, 停留在注册的tab内
$(function () {
//模拟一个点击事件, 选中注册
//决定是显示登陆还是显示注册tab
//如果注册失败, 显示注册tab, 而不能是默认的登录tab
if (${requestScope.active == "register_tab"}) {
$("#register_tab")[0].click();
} else {
$("#login_tab")[0].click();
}
}
<a id="login_tab" data-bs-toggle="tab" href="#lg1">
<h4>会员登录</h4>
</a>
<a id="register_tab" data-bs-toggle="tab" href="#lg2">
<h4>会员注册</h4>
</a>
5)加入register_ok.jsp, register_fail.jsp页面
<a class="active" href="index.jsp">
<h4>注册成功, 返回首页</h4>
</a>
<a class="active" href="views/member/login.jsp">
<h4>注册失败, 重新注册</h4>
</a>
16.删除点击注册后的拦截代码web/views/member/login.jsp
//这里暂时不提交=>显示验证通过
$("span.errorMsg").text("验证通过");
return false;
17.测试
🐇会员登陆
思路分析
1.输入用户名和密码后提交
2.如果输入有误,则给出提示
3.判断会员是否存在
4.会员存在于数据库, 显示登录成功页面
5.否则, 返回登陆页面, 重新登陆
6.要求改进登陆密码为md5加密
程序框架图
代码实现
1.修改src/com/zzw/furns/dao/MemberDao.java
2.修改src/com/zzw/furns/dao/MemberDaoImpl.java
/**
* 根据用户名和密码查询对应的Member对象
* @param username 用户名
* @param password 密码
* @return 对应的Member对象, 如果没有 则返回null
*/
@Override
public Member queryMemberByUsernameAndPassword(String username, String password) {
String sql = "SELECT id, username, `password`, email FROM member WHERE username = ? AND `password` = MD5(?);";
Member member = querySingle(sql, Member.class, username, password);
return member;
}
3.测试(不要忘了测试)
@Test
public void queryMemberByUsernameAndPassword() {
String username = "admin";
String password = "123456";
Member member = memberDao.queryMemberByUsernameAndPassword(username, password);
if (member == null) {
System.out.println("用户名或密码错误");
} else {
System.out.println("登录成功");
}
}
快捷键
4.修改src/com/zzw/furns/service/MemberService.java
public interface MemberService {
/**
* 根据传入的member信息, 返回对应在DB中的member对象
* @param member 是根据用户登录构建一个member
* @return 返回的是对应的DB中的member对象, 如果不存在返回null
*/
public Member login(Member member);
}
5.修改src/com/zzw/furns/service/impl/MemberServiceImpl.java
public class MemberServiceImpl implements MemberService {
//定义MemberDAO属性
private MemberDAO memberDAO = new MemberDAOImpl();
/**
* 判断用户名和密码是否存在
* @param username 用户名
* @param password 密码
* @return
*/
@Override
public Member login(Member member) {
//返回一个对象
return memberDAO.
queryMemberByUsernameAndPassword(member.getUsername(), member.getPassword());
}
}
6.测试(不要忘了测试)
@Test
public void login() {
Member member = new Member(null, "adminx", "admin", null);
Member login = memberService.login(member);
System.out.println("login= " + login);
}
7.修改src/com/zzw/furns/web/LoginServlet.java
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
private MemberService memberService = new MemberServiceImpl();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//如果在登陆界面用户没有输入内容, 后台接收到的是""
String username = request.getParameter("username");
String password = request.getParameter("password");
Member member = new Member(null, username, password, null);
if (memberService.login(member) != null) {//用户存在DB
System.out.println("用户存在, 登陆成功...");
request.getRequestDispatcher("/views/member/login_ok.html")
.forward(request, response);
} else {//用户不存在
System.out.println("登陆失败, 返回登陆页面");
request.setAttribute("username", username);
request.setAttribute("msg", "登陆失败");
request.getRequestDispatcher("/views/member/login.jsp")
.forward(request, response);
}
}
}
配置LoginServlet
<servlet>
<servlet-name>LoginServlet</servlet-name>
<servlet-class>com.zzw.furns.web.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginServlet</servlet-name>
<url-pattern>/login</url-pattern>
</servlet-mapping>
8.修改web/views/member/login.jsp
$("button:first")[0].onclick = function () {
//采用过关斩将法
//正则表达式验证用户名
var usernameValue = $("form:first")[0].childNodes[3].value;
var usernamePattern = /^\w{6,10}$/;
if (!usernamePattern.test(usernameValue)) {
$("span[class='errorMsg2']").text("用户名格式不对, 需要6-10个字符(大小写字母,数字,下划线)");
return false;
}
//验证密码
var passwordValue = $("form:first")[0].childNodes[5].value;
var passwordPattern = /^\w{6,10}$/;
if (!passwordPattern.test(passwordValue)) {
$("span.errorMsg2").text("密码格式不对, 需要6-10个字符(大小写字母,数字,下划线)");
return false;
}
}
<!--会员登录-->
<span class="errorMsg2"
style="float: right; font-weight: bold; color: lightgray; font-size: 20pt; margin-left: 10px;">${msg}</span>
<form action="loginServlet" method="post">
<input type="text" name="username" value="${username}" placeholder="Username"/>
<input type="password" name="password" placeholder="Password"/>
<div class="button-box">
<div class="login-toggle-btn">
<input type="checkbox"/>
<a class="flote-none" href="javascript:void(0)">Remember me</a>
<a href="#">Forgot Password?</a>
</div>
<button type="submit"><span>Login</span></button>
</div>
</form>
10.快捷键学习
11.测试