一、知识点
1、@WebServlet注解的使用
@WebServlet注解是Servlet 3.0引入的一个特性,它允许开发者在Servlet类上使用注解来声明Servlet的一些属性,从而避免在web.xml文件中进行配置。这种方式简化了Servlet的配置过程,使得代码更加简洁,易于理解和维护。
1)示例代码
使用@WebServlet注解非常简单,只需要在Servlet类上方添加注解,并指定URL模式。例如,要映射到/login.do的URL,可以这样写:
@WebServlet(urlPatterns = {"/login"})
public class LoginServlet extends HttpServlet {
// ... 类的实现
}
这个注解告诉Servlet容器,当请求到达/login.do时,应该使用LoginServlet类来处理该请求。
2)重要注意事项
在使用**@WebServlet**注解时,有几个重要的注意点需要遵守:
-
确保在web.xml中设置metadata-complete="false",这样Servlet容器才会扫描注解。
-
如果使用了注解方式,就不应该再在web.xml中对同一个Servlet进行配置。
-
在注解的URL模式中,不要忘记前面的斜杠/。
2、销毁session时,removeAttribute()和invalidate()有何区别?
做我们在用户登陆功能时,用户退出时用到的是session.invalidate()
而在比对验证码时,用到的是session.removeAttribute("某个名字")。
invalidate时销毁,作废的意思。
1)二者比较
session.removeAttribute("") 用于清空session中某个特定的属性值
session.livadate() 则是清除当前整个session
1、removeAttribute() 就是从session删除指定名称的绑定对象,也就是说调用此方法后再调用getAttribute(Stringname)时,不能获取指定名称的绑定对象,但是session还存在。
2、invalidate() 就是销毁此session对象,session对象中绑定的那些对象值也都不存在了。
session.invalidate()是销毁跟用户关联session,例如有的用户强制关闭浏览器,而跟踪用户的信息的session还存在,可是用户已经离开了。
虽然session 生命周期浏览默认时间30分,但是在30分钟内别的用户还可以访问到前一个用户的页面,需销毁用户的session。
session.removeAttribute()移除session中的某项属性。
==>在用户登陆中,用户退出时执行的是 request.getSession().removeAttribute("userSession");
session被销毁了,注销用户,可以使session失效。
==>验证验证码时,获取到session中的验证码后,用的是removeAttribute
是清空当前session中指定的属性,下个request中的sessionID是不变的
也就是说,session还是原来的session
(参考链接:https://blog.csdn.net/u012471009/article/details/79702145)
3、什么是三层架构?
1)分析
我们往往使用一个Dao接口隐藏持久化操作的细节,业务对象不需要了解底层的数据库持久化知识。使得业务逻辑与持久化逻辑分离,业务逻辑通常关系的是应用程序的核心流程和业务规则,持久化逻辑关注的是如何访问和操作持久化数据。
表示层: JSP/Servlet
业务逻辑层: 业务规则
持久化层: 主要包装持久化的逻辑
分层主要目的是为了好管理,能更好的适应需求的变换,能够更好的进行人员分工。
如图所示:
2)通俗理解为什么要使用三层架构:
假如你要开一家奶茶店,用开奶茶店的过程来类比三层架构会很容易理解。整个三层架构就像是店里不同分工的工作人员,共同协作完成顾客购买奶茶的任务。
表现层 (UI 层,User Interface):
这一层就好比奶茶店的前台工作人员。顾客走进店里,和前台交流,告诉前台自己想要什么口味的奶茶,要大杯还是小杯,加不加珍珠、椰果等配料。前台工作人员把顾客的这些要求记录下来,然后将做好的奶茶递给顾客,让顾客看到并拿到最终的产品。在软件系统里,表现层就是用户能直接看到和操作的部分,比如手机 APP 的界面、电脑网页的页面等,负责接收用户的输入,比如点击按钮、填写信息,再把系统处理后的结果展示给用户,像显示查询到的信息、操作的反馈等。
业务逻辑层(BLL 层,Business Logic Layer):
相当于奶茶店的制作团队负责人。当前台工作人员把顾客的需求传达过来后,制作团队负责人要根据店里的配方和流程来安排制作。比如顾客要一杯波霸奶茶,负责人要确定珍珠的量、奶茶的配比,还要考虑店里的原料库存够不够,如果不够要及时补货等。在软件系统中,业务逻辑层接收表现层传递过来的用户请求,按照设定好的业务规则进行处理。比如在一个电商系统中,计算商品折扣、判断用户的订单是否符合优惠条件、处理退货退款等逻辑,都是业务逻辑层的工作。它会调用数据访问层获取或保存数据,再把处理结果返回给表现层。
数据访问层(DAL 层,Data Access Layer):
就像是奶茶店的仓库管理员。制作团队需要原料时,仓库管理员负责从仓库中取出相应的原料给他们;当采购了新的原料回来,仓库管理员要把原料妥善存放到仓库里。在软件系统中,数据访问层主要负责和数据库打交道。当业务逻辑层需要获取数据,比如查询用户信息、商品信息时,数据访问层就从数据库中把数据取出来;当业务逻辑层需要保存数据,比如新用户注册信息、新订单信息时,数据访问层就把这些数据存入数据库。它为业务逻辑层提供了数据访问的功能,让业务逻辑层不用关心具体怎么连接数据库、怎么执行 SQL 语句等细节。
通过这种三层架构的分工,整个系统就像奶茶店一样,各个部分各司其职,既方便维护和管理,也能更高效地满足用户的需求。
3)dao层和service层的区别和联系
在常见的三层架构中,DAO(Data Access Object,数据访问对象)层和 Service(业务逻辑)层是两个重要的组成部分。
职责区别:
DAO 层:这一层主要负责与数据库进行交互,就像图书馆里专门管理藏书仓库的工作人员。它的任务是执行对数据库的增、删、改、查操作。比如在图书馆管理系统中,当需要添加一本新书的信息到系统中,DAO 层会执行向数据库插入新数据的操作;如果要查询某本图书的借阅记录,DAO 层会从数据库中查询相关数据并返回。它不关心这些数据在业务上的具体用途,只专注于如何准确地从数据库获取数据或者将数据保存到数据库。
Service 层:它更像是图书馆的借阅服务台工作人员,负责处理业务逻辑。比如在图书馆管理系统中,当读者要借阅一本书时,Service 层会先调用 DAO 层查询这本书当前是否在库、读者是否有超期未还的书籍等情况,然后根据这些条件判断是否允许借阅。如果允许,再调用 DAO 层更新书籍的借阅状态和读者的借阅记录。Service 层会综合考虑多个因素和业务规则,将不同的 DAO 层操作组合起来,以完成一个完整的业务功能。
依赖关系:
Service 层依赖于 DAO 层。Service 层在处理业务逻辑的过程中,需要获取或保存数据时,会调用 DAO 层提供的方法。就像借阅服务台工作人员在处理借阅业务时,需要从管理藏书仓库的工作人员那里获取书籍状态和读者借阅记录等信息。而 DAO 层并不依赖 Service 层,它只专注于数据库操作,为上层提供数据访问服务。
设计目的区别:
DAO 层的设计目的是为了实现数据访问的封装,使得数据库操作与业务逻辑分离,提高数据访问的可维护性和可复用性。比如,当数据库从 MySQL 换成 Oracle 时,只需要修改 DAO 层的代码,而不影响业务逻辑。Service 层的设计目的是为了实现业务逻辑的封装,将复杂的业务流程进行抽象和组织,提高业务逻辑的可扩展性和可维护性。例如,当图书馆新增一种借阅规则时,只需要在 Service 层修改相应的业务逻辑代码即可。
综上所述,DAO 层和 Service 层在三层架构中相互协作,DAO 层为 Service 层提供数据支持,Service 层利用 DAO 层完成业务逻辑处理,共同保证系统的正常运行。
二、模拟管理员登录和退出 小demo
1、login界面代码
<%--
Created by IntelliJ IDEA.
User: 34524
Date: 2024/8/11
Time: 21:40
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/bootstrap.min.css"/>
<script src="${pageContext.request.contextPath}/js/bootstrap.bundle.min.js"></script>
<script src="${pageContext.request.contextPath}/js/jquery-3.7.1.min.js"></script>
<title>Login</title>
<link rel="icon" href="${pageContext.request.contextPath}/img/favicon.ico" sizes="16X16"/>
<style type="text/css">
body {
background-image: url("${pageContext.request.contextPath}/img/login.jpg");
background-repeat: no-repeat;
background-size: cover;
}
#loginform {
border: 2px solid gray;
padding: 20px;
border-radius: 10px;
animation-name: rain;
animation-duration: 600s;
animation-direction: alternate;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
#loginform:hover {
transition: 0.7s;
border: 2px solid white;
}
@keyframes rain {
0%{box-shadow: 0px 0px 25px gold;}
10%{box-shadow: 0px 0px 25px lime;}
20%{box-shadow: 0px 0px 25px deepskyblue;}
30%{box-shadow: 0px 0px 25px blue;}
40%{box-shadow: 0px 0px 25px purple;}
50%{box-shadow: 0px 0px 25px deeppink;}
60%{box-shadow: 0px 0px 25px purple;}
7%{box-shadow: 0px 0px 25px blue;}
80%{box-shadow: 0px 0px 25px deepskyblue;}
90%{box-shadow: 0px 0px 25px lime;}
100%{box-shadow: 0px 0px 25px gold;}
}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-7 pt-5">
<div class="row">
<div class="col-1"></div>
<div class="col-3">
<a href="${pageContext.request.contextPath}/index.jsp">
<img src="${pageContext.request.contextPath}/img/"/>
</a>
</div>
<div class="col-8">
<p style="padding-top: 200px;color: white; font-size: 3em;">用美图分享快乐!</p>
</div>
</div>
</div>
<div class="col-3 pt-5 mt-5">
<form method="post" action="${pageContext.request.contextPath}/login" id="loginform">
<h2 class="text-center text-light">欢迎登录</h2>
<div class="form-group mb-2">
<label for="username" class="text-white">用户名</label>
<input type="text" class="form-control" name="username" id="username" value="${cookie.yhm.value}" aria-describedby="usernameHelp">
<small id="usernameHelp" class="form-text text-light"></small>
</div>
<div class="form-group mb-3">
<label for="password" class="text-white">Password</label>
<input type="password" class="form-control" id="password" name="password" aria-describedby="passwordHelp">
<small id="passwordHelp" class="form-text text-light"></small>
</div>
<div class="form-group form-check mb-2">
<input type="checkbox" class="form-check-input" id="Check1" name="remember" value="1">
<label class="form-check-label text-warning" for="Check1">记住用户名</label>
</div>
<div class="form-group form-check mb-2">
<input type="checkbox" class="form-check-input" id="Check2" >
<label class="form-check-label text-warning" for="Check2">管理员登录</label>
</div>
<div class="row mb-4">
<div class="col-8">
<input type="text" class="form-control" placeholder="填写验证码" name="captcha" id="captcha">
</div>
<div class="col-4">
<!-- TODO:验证码一般是个图片!!!! 也可以是纯文字 -->
<%--<button class="btn btn-info" style="letter-spacing: 0.5em;">45TE</button>--%>
<img id="captchaImg" src="${pageContext.request.contextPath}/captcha" width="100px" height="40px"/>
<!-- 每一次点击的时候,改变路径 用JS-->
</div>
</div>
<div class="row mb-2">
<div class="col-4">
<button type="submit" class="btn btn-primary btn-block">登录</button>
</div>
<div class="col-4">
<a class="btn btn-warning btn-block" href="${pageContext.request.contextPath}/register.jsp">去注册</a>
</div>
<div class="col-4">
<a class="btn btn-secondary btn-block" href="${pageContext.request.contextPath}/index.jsp">返回首页</a>
</div>
</div>
</form>
</div>
<div class="col-2"></div>
</div>
</div>
<!-- 界面:这是一个toast
功能:弹窗,弹出一个弹框,表示你成功登陆了或者是失败登陆了
用法:
<small></small>里边写的是弹出的表示时间的信息
<div></div>后边一个div表示的是弹框里边的文字内容
<div></div>的class里边可以改变toast出现的位置
如何触发toast事件:
要求: 会用就行,主要是知道这么一个东西,知道在哪里可以改变它的样式
-->
<div class="toast-container position-fixed top-0 end-0 p-3">
<div id="liveToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<img src="${pageContext.request.contextPath}/img/favicon.ico" class="rounded me-2" alt="...">
<strong class="me-auto">Illey练习</strong>
<small>现在</small>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<!-- 思路和作用:这里我们应该判断一下,然后再来写div里边的文本数据
其实根本不需要判断,由于你登陆成功或者失败,你肯定得告诉给程序里边得其他对象,所以会把失败信息放到一个作用域里边
直接取这个作用域里边的文本,再来判断这个toast显示不显示就行了
<--------取作用域的文本时,使用javascript
-->
<div class="toast-body">
${param.m==8?"重新登陆":" "}
${requestScope.msg}${param.t==0?"已安全退出":''}
${param.t==9?"注册成功请登录!":" "}
</div>
</div>
</div>
<!-- 要实现功能:
判断toast-body里边的text文本究竟是不是存在,然后再判断是否显示toast
入参:无入参
方法体:1.用$()占位符获取toast-body
2.使用text()函数,获得里边的文本变量
3.判断文本内容是否为空或者大于0,如果里边有文本,说明就是登陆失败
4.先获取livetoast对象
5.然后再通过 new bootstrap.toast 来获取相应的toast变量
6.最后用那个toast的show方法,呈现toast的形式和内容
-->
<script>
/* 两个都弹了,说明没有问题*/
$(function(){
/*$(".可以识别的 没问题 ")*/
let text=$(".toast-body").text();
if (text!=null && text.length>0) {
let toastLiveExample = document.getElementById("liveToast");
/* 大小写 写错了 livetoast liveToast */
let toast = new bootstrap.Toast(toastLiveExample);
toast.show();
}
let old =$("#captchaImg").attr("src");
//每当点击图片时,修改 src路径 浏览器就会发出新的请求
$("#captchaImg").click(function () {
let n=Math.random();
$("#captchaImg").attr("src",old+"?n="+n);
/*console.log($("#captchaImg").attr("src"))*/
})
}
)
$("#Check2").change(function(){
if($("#Check2").prop("checked")){
$("#loginform").attr("action","${pageContext.request.contextPath}/login2");
}else{
$("#loginform").attr("action","${pageContext.request.contextPath}/login");
}
})
</script>
</body>
</html>
登录界面展示:
在这里的登录我是利用了JavaScript让提交的时候action随之改变
勾选管理员登录之前:
勾选管理员登录之后:
2、/login2 的servlet代码处理
package com.illley.controller;
import com.illley.bean.Manager;
import com.illley.service.ManagerService;
import com.illley.service.impl.ManagerServiceImpl;
import org.springframework.dao.EmptyResultDataAccessException;
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 javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet(urlPatterns = {"/login2"})
public class Login2Controller extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//get 处理 退出
HttpSession session = req.getSession();
//就是要把session会话里边的manager的attribute使他失效
//然后再转发页面
session.removeAttribute("manager");
session.invalidate();
resp.sendRedirect(req.getContextPath()+"/login.jsp?t=0");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//什么管理员显示什么页面 所以还要查询管理员的角色 而且每一个角色对应一个资源动作 有多表关联
//管理员表 角色表 操作资源 角色和资源 多对多
String username =req.getParameter("username");
String password =req.getParameter("password");
//管理员登陆
ManagerService ms =new ManagerServiceImpl();
try {
Manager success = ms.login(username,password);
//System.out.println(success); //这里打印到控制台就中文乱码,然后登录到管理员界面也都是乱码
//打印没问题 就应该把它放到会话域中
HttpSession session = req.getSession();
session.setAttribute("manager",success);
resp.sendRedirect(req.getContextPath()+"/main.jsp"); //当前项目下的 main.jp页面
} catch (EmptyResultDataAccessException e) {
//System.out.println("用户名和密码错误");
req.setAttribute("msg","用户名或密码错误");
req.getRequestDispatcher("/login.jsp").forward(req, resp);
}
}
}
其实大家要学习的就是doPost和doGet里边是如何处理用户登录和退出的就可以了。
3、其他操作代码
之后还有如何连接数据库,然后利用sql语句进行查找判断该账户是不是在管理员里边,还有javaBean的构建,这里就不详细展示了,等我把这个javaWeb项目学完,会开源这个项目的,大概春节前后左右吧。
三、处理中文乱码问题
其实是我的两个管理员展示页面最前面忘了加这么一句话
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
这样最后展示的页面就是正常的中文
如图所示: