文章目录
十四、书城项目第三阶段——优化
(1)页面jsp动态化
如果想让页面动态化,要将html页面变为jsp
1、首先在所有html页面的顶部加上 page指令
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
2、然后修改文件后缀名 从 .html 变为 .jsp
3、页面内有些代码也要从 .html 变为 .jsp
使用快捷键: ctrl+shift+r 查找替换
如下图
搜索 .html
替换为 .jsp
在Directory中搜索,Directory目录选pages
Replace All 全部替换
(2)抽取页面中相同的内容
A.登录成功的菜单
在如上jsp页面中都有 如下的 登录成功的菜单代码:
在pages目录下,创建一个common目录
在common目录下创建 login_success_menu.jsp
然后将上面的登录成功的菜单代码写进该jsp
login_success_menu.jsp 如下:
然后再将其他页面中的登录成功的菜单代码删除,用下面的静态包含代码替换:
<!--静态包含,登录成功之后的菜单-->
<%@ include file="/pages/common/login_success_menu.jsp"%>
B.base、css、jQuery标签
将所有页面的base、css、jQuery标签提取
在common目录下创建 head.jsp
将以下代码写入:
<base href="http://localhost:8080/09_bookproject/">
<link type="text/css" rel="stylesheet" href="static/css/style.css" >
<script type="text/javascript" src="static/jquery-3.6.0.js"></script>
head.jsp如下:
将pages目录下(除common目录)的所有jsp页面和index.jsp页面的
base、css、jQuery标签用下面的代替:
不管是只有一个css 没有base和jQuery 等情况,都这样做
<!--静态包含,base标签,css标签,jQuery文件-->
<%@ include file="/pages/common/head.jsp"%>
因为所有页面都使用了base,所以要对每个程序中的代码都看看需不需要修改。
login_success_menu.jsp 需要修改为如下(因为所有页面都使用了base):
如果出现错误找不到文件:alt + enter 选set context folder to / for ...
C.每个页面的页脚
页脚都是相同的,在common目录下创建footer.jsp 写入如下代码:
使用如下代码替换:
<%@ include file="/pages/common/footer.jsp" %>
D.manager模块的菜单
manager模块的菜单代码是重复的,多个页面都有
在common目录下创建manager_menu.jsp,写入以下代码:
使用以下代码代替原来的代码:
<%@ include file="/pages/common/manager_menu.jsp"%>
(3)登录、注册错误提示和表单回显
登录或注册失败,需要提示用户错误信息。
登录或注册失败之后,表单中的有些内容是不会被清空的,会保留下来。
针对登录,实现错误信息提示和登陆错误之后保留用户名不被清空:
修改LoginServlet:
然后修改 login.jsp:
提示错误信息:
登录错误之后,保存用户名不被清空,即填写value值:
针对注册,实现错误信息提示(用户名已存在和验证码错误)和注册失败之后 用户名和邮箱不被清空
修改RegisterServlet:
修改regist.jsp:
输出错误提示信息:
注册错误后,保留用户名不被清空,即value:
注册错误后,保留邮箱不被清空,即value:
错误提示信息是写在标签上的,一般使用div或span;
但是错误信息的触发是写在servlet程序中的,通过判断错误,将错误信息保存在request域中,这样jsp页面也就能使用错误信息。
(4)BaseServlet的抽取
A. 代码优化:合并 LoginServlet 和 RegistServlet 程序为 UserServlet
一般在一个模块中,只有一个Servlet程序;
现在只有一个用户模块,但是有两个Servlet程序:LoginServlet和RegisterServlet
现在要把这两个合并为一个Servlet程序叫UserServlet
但是 那两个Servlet程序都是使用的doPost()方法,该怎么合并呢?
在登录和注册表单使用hidden标签,hidden标签在页面中不显示出来,
但是可以给hidden的value设置不同的值来判断是登录还是注册。
创建UserServlet:写web.xml,把LoginServlet和RegisterServlet复制过去
不过为了代码简洁,在UserServlet中创建两个方法:login和register
将LoginServlet复制到login方法中
将RegisterServlet复制到register方法中
package com.atguigu.web;
import com.atguigu.bean.User;
import com.atguigu.service.UserService;
import com.atguigu.service.service_impl.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class UserServlet extends HttpServlet {
//web层只能操作service层,不能直接操作dao层
private UserService userService = new UserServiceImpl();
//处理登录的需求
protected void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、获取请求的参数
String username = req.getParameter("username");
String password = req.getParameter("password");
// 调用 userService.login()登录处理业务
//判断数据库中是否有该用户
User loginUser = userService.login(new User(null, username, password, null));
// 如果等于 null,说明没有该用户,说明登录 失败!
if (loginUser == null) {
//把错误信息 和 回显的表单项信息(这里只回显用户名)保存在request域中
req.setAttribute("msg", "用户名或密码错误!");
req.setAttribute("username", username);
// 跳回登录页面
req.getRequestDispatcher("/pages/user/login.jsp").forward(req, resp);
} else {
// 登录 成功
//跳到成功页面 login_success.jsp
req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req, resp);
}
}
//处理注册的需求
protected void register(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取请求的参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
String code = req.getParameter("code");
//2.检查验证码是否正确(这里的验证码先写死,为abcde)
//equalsIgnoreCase()忽略大小写
if("abcde".equalsIgnoreCase(code)){
//2.1.验证码正确
//3.检查用户名是否可用
if(userService.existUsername(username)){
//3.1.用户名已存在,跳转回注册界面
System.out.println("用户名[" + username + "]已存在!");
//把回显信息保存在request域中
//错误提示信息:
req.setAttribute("msg", "用户名已存在!");
//保留用户名和邮箱信息,在注册失败后不被清空
req.setAttribute("username", username);
req.setAttribute("email", email);
req.getRequestDispatcher("/pages/user/regist.jsp").forward(req, resp);
}else{
//3.2.用户名可用,将注册的用户保存到数据库,注册成功
userService.registerUser(new User(null,username,password,email));
//跳转到注册成功页面
req.getRequestDispatcher("/pages/user/regist_success.jsp").forward(req, resp);
}
}else{
//2.2.验证码错误,跳转回注册页面
System.out.println("验证码[" + code +"]错误");
//把回显信息保存在request域中
//错误提示信息:
req.setAttribute("msg", "验证码错误!");
//保留用户名和邮箱信息,在注册失败后不被清空
req.setAttribute("username", username);
req.setAttribute("email", email);
req.getRequestDispatcher("/pages/user/regist.jsp").forward(req, resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action = req.getParameter("action");
if ("login".equals(action)){
//处理登录的需求
login(req, resp);
}else if ("register".equals(action)){
//处理注册的需求
register(req, resp);
}
}
}
为登录表单添加hidden标签,同时修改form 的action:
action直接写 userServlet也行
为注册表单添加hidden标签,同时修改form 的action:
LoginServelt和RegisterServelt可以删除了,相应的web.xml中的配置也可以删除。
B. 优化代码二:使用反射优化大量 else if
如果还用其他的功能,那么在UserServlet的doPost()方法中就要写更多的else if
如何优化,不写那么多的else if?
使用 反射的 方法,只要知道要调用的方法名,就可以调用相应的方法。
修改UserServlet的doPost()方法如下:
C. 代码优化三:抽取 BaseServlet 程序
现在只有用户模块UserServlet程序,但是以后可能会有更多的模块比如图书模块BookServlet程序。
这些模块都会有一些共同的代码,即doPost()里是一样的,
所以再将doPost()提取出来放进BaseServlet程序中,UserServlet和BookServlet中不再写doPost()
BaseServlet是父类,所以定义为抽象的abstract,并继承HttpServlet
UserServlet和BookServlet只继承BaseServlet即可。
程序运行时会先进入BaseServlet,然后再进入相应的UserServlet或BookServlet。
如果理解不了,可以这样想,UserServlet继承了BaseServlet,就只是把doPost()方法换了一个地方,你可以想成doPost()还在UserServlet中。
表单提交访问的是UserServlet,提交方式是post,所以会看UserServlet中有无doPost()方法,其实是有的,不过写在了BaseServlet里,然后继承了。
web.xml不需要修改。
package com.atguigu.web;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
public class BaseServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//功能的方法名要和表单中的hidden标签的value值一致
//比如:登录表单的hidden标签的value=login,上面的方法名也是login
//获取hidden的value值
String action = req.getParameter("action");
try {
//通过反射机制,调用不同的功能方法(多一个功能,就在上面多写一个方法,doPost里面就不用改了)
//getDeclaredMethod(指明要获取的方法名,指明获取的方法的形参列表)
Method method = this.getClass().getDeclaredMethod(action, HttpServletRequest.class, HttpServletResponse.class);
//调用相应的功能方法
method.invoke(this,req,resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
(5)数据的封装和抽取BeanUtils的使用
在UserServlet的登录和注册操作中,都会获取请求参数,并注入JavaBean(User类)中
就是下面这个:
//1.获取请求的参数
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
User user = new User(null,username,password,email)
可以对获取请求参数进行封装,使用BeanUtils工具类
创建一个新的类:WebUtils:
package com.atguigu.web;
import org.apache.commons.beanutils.BeanUtils;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
public class WebUtils {
//把Map中的值注入到对应的JavaBean属性中
//value为请求参数,以Map键值对的形式出现(比如username=输入的 password=输入的 等)
//bean是javabean对象,这里是User对象
//使用泛型是因为 JavaBean是不一样的,可能是User类,也可能是Book类等等
//使用泛型,就不用强制转换了。
public static <T> T copyParamToBean(Map value, T bean){
try {
System.out.println("注入之前:" + bean);
//将所有的请求参数都注入到user对象中
//第一个参数是JavaBean对象,这里是user对象
//第二个参数是 请求参数(以键值对的方式)
BeanUtils.populate(bean, value);
System.out.println("注入之后:" + bean);
} catch (Exception e) {
e.printStackTrace();
}
//将创建的对象返回
return bean;
}
}
如何调用WebUtils?
User user = WebUtils.copyParamToBean(req.getParameterMap(), new User());
使用BeanUtils可以不写获取请求参数,直接将请求参数注入到JavaBean中
(即直接创建一个user对象)
BeanUtils.populate(bean, value);的底层原理是:
获取到传入的req.getParameterMap() 键值对 形式 的请求参数 之后,
会调用JavaBean类 即User类中的setXXX方法,将键值对的值传进去,从而创建出一个User对象。
但是登录或注册的方法中 下面有的代码还是要用到 请求参数,
所以还是要手动写请求参数,这个WebUtils没啥用啊,我就没写WebUtils,还跟原来一样。
十五、书城项目第四阶段——使用EL表达式修改表单回显
EL的作用是替换 <%= %>
一般都是使用EL 来替换 表达式脚本 <%= %>
使用EL表达式对login.jsp做如下修改:
使用EL表达式对regist.jsp做如下修改:
十六、书城项目第五阶段——编写图书模块
001-MVC概念
002-编写图书模块的数据库表
创建图书表:
id 需要一个id号
name 图书名
price 图书价格 decimal(11,2)--->decimal十进制数,2表示小数的位数,“11”指的是整数部分加小数部分的总长度
author 图书作者
sales 销量
stock 库存量
img_path 图书照片的路径
create table t_book(
`id` int primary key auto_increment,
`name` varchar(100),
`price` decimal(11,2),
`author` varchar(100),
`sales` int,
`stock` int,
`img_path` varchar(200)
);
## 插入初始化测试数据
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'java 从入门到放弃' , '国哥' , 80 , 9999 , 9 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '数据结构与算法' , '严敏君' , 78.5 , 6 , 13 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '怎样拐跑别人的媳妇' , '龙伍' , 68, 99999 , 52 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '木虚肉盖饭' , '小胖' , 16, 1000 , 50 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'C++编程思想' , '刚哥' , 45.5 , 14 , 95 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '蛋炒饭' , '周星星' , 9.9, 12 , 53 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '赌神' , '龙伍' , 66.5, 125 , 535 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'Java 编程思想' , '阳哥' , 99.5 , 47 , 36 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'JavaScript 从入门到精通' , '婷姐' , 9.9 , 85 , 95 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'cocos2d-x 游戏编程入门' , '国哥' , 49, 52 , 62 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'C 语言程序设计' , '谭浩强' , 28 , 52 , 74 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'Lua 语言程序设计' , '雷丰阳' , 51.5 , 48 , 82 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '西游记' , '罗贯中' , 12, 19 , 9999 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '水浒传' , '华仔' , 33.05 , 22 , 88 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '操作系统原理' , '刘优' , 133.05 , 122 , 188 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '数据结构 java 版' , '封大神' , 173.15 , 21 , 81 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'UNIX 高级环境编程' , '乐天' , 99.15 , 210 , 810 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , 'javaScript 高级编程' , '国哥' , 69.15 , 210 , 810 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '大话设计模式' , '国哥' , 89.15 , 20 , 10 , 'static/img/default.jpg');
insert into t_book(`id` , `name` , `author` , `price` , `sales` , `stock` , `img_path`)
values(null , '人月神话' , '刚哥' , 88.15 , 20 , 80 , 'static/img/default.jpg');
## 查看表内容
select id,name,author,price,sales,stock,img_path from t_book;
003-编写图书模块的JavaBean
package com.atguigu.bean;
import java.math.BigDecimal;
public class Book {
private Integer id;
private String name;
private String author;
private BigDecimal price;
private Integer sales;
private Integer stock;
private String imgPath = "static/img/default.jpg";
public Book() {
}
public Book(Integer id, String name, String author, BigDecimal price, Integer sales, Integer stock, String imgPath) {
this.id = id;
this.name = name;
this.author = author;
this.price = price;
this.sales = sales;
this.stock = stock;
//当imgPath为空时,不让它赋值,让它使用默认值;当imgPath不为空时,赋新值。
if (imgPath != null && !imgPath.equals("")){
this.imgPath = imgPath;
}
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getSales() {
return sales;
}
public void setSales(Integer sales) {
this.sales = sales;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public String getImgPath() {
return imgPath;
}
public void setImgPath(String imgPath) {
//当imgPath为空时,不让它赋值,让它使用默认值;当imgPath不为空时,赋新值。
if (imgPath != null && !imgPath.equals("")){
this.imgPath = imgPath;
}
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
", sales=" + sales +
", stock=" + stock +
", imgPath='" + imgPath + '\'' +
'}';
}
}
004-编写图书模块的Dao和测试Dao
BookDao 接口:
package com.atguigu.dao;
import com.atguigu.bean.Book;
import java.util.List;
public interface BookDao {
//增加书
public int addBook(Book book);
//通过id删除书
public int deleteBookById(Integer id);
//修改书的信息
public int updateBook(Book book);
//通过id查询书
public Book queryBookById(Integer id);
//查询所有的书
public List<Book> queryBooks();
}
BookDaoImpl 实现类(继承BaseDao,实现BookDao):
package com.atguigu.dao.dao_impl;
import com.atguigu.bean.Book;
import com.atguigu.dao.BaseDao;
import com.atguigu.dao.BookDao;
import java.util.List;
public class BookDaoImpl extends BaseDao implements BookDao {
@Override
//增加书
public int addBook(Book book) {
String sql = "insert into t_book(`name`,`author`,`price`,`sales`,`stock`,`img_path`) values(?,?,?,?,?,?)";
return update(sql,book.getName(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImgPath());
}
@Override
//通过id删除书
public int deleteBookById(Integer id) {
String sql = "delete from t_book where id = ?";
return update(sql, id);
}
@Override
//修改书的信息
public int updateBook(Book book) {
String sql = "update t_book set `name`=?,`author`=?,`price`=?,`sales`=?,`stock`=?,`img_path`=? where id = ?";
return update(sql,book.getName(),book.getAuthor(),book.getPrice(),book.getSales(),book.getStock(),book.getImgPath(),book.getId());
}
@Override
//通过id查询书
public Book queryBookById(Integer id) {
String sql = "select `id` , `name` , `author` , `price` , `sales` , `stock` , `img_path` imgPath from t_book where id = ?";
return queryForOne(Book.class, sql,id);
}
@Override
//查询所有的书
public List<Book> queryBooks() {
String sql = "select `id` , `name` , `author` , `price` , `sales` , `stock` , `img_path` imgPath from t_book";
return queryForList(Book.class, sql);
}
}
package com.atguigu.test;
import com.atguigu.bean.Book;
import com.atguigu.dao.BookDao;
import com.atguigu.dao.dao_impl.BookDaoImpl;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
class BookDaoImplTest {
private BookDao bookDao = new BookDaoImpl();
@Test
void addBook() {
bookDao.addBook(new Book(null,"国哥为什么这么帅!", "191125", new BigDecimal(9999),1100000,0,null));
}
@Test
void deleteBookById() {
bookDao.deleteBookById(21);
}
@Test
void updateBook() {
bookDao.updateBook(new Book(21,"大家都可以这么帅!", "国哥", new BigDecimal(9999),1100000,0,null));
}
@Test
void queryBookById() {
System.out.println( bookDao.queryBookById(21) );
}
@Test
void queryBooks() {
for (Book queryBook : bookDao.queryBooks()) {
System.out.println(queryBook);
}
}
}
005-编写图书模块的Service和测试Service
BookService接口:
package com.atguigu.service;
import com.atguigu.bean.Book;
import java.util.List;
public interface BookService {
//增加书
public void addBook(Book book);
//通过id删除书
public void deleteBookById(Integer id);
//修改书的信息
public void updateBook(Book book);
//通过id查询书
public Book queryBookById(Integer id);
//查询所有的书
public List<Book> queryBooks();
}
BookServiceImpl实现类:
package com.atguigu.service.service_impl;
import com.atguigu.bean.Book;
import com.atguigu.dao.BookDao;
import com.atguigu.dao.dao_impl.BookDaoImpl;
import com.atguigu.service.BookService;
import java.util.List;
public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl();
@Override
//增加书
public void addBook(Book book) {
bookDao.addBook(book);
}
@Override
//通过id删除书
public void deleteBookById(Integer id) {
bookDao.deleteBookById(id);
}
@Override
//修改书的信息
public void updateBook(Book book) {
bookDao.updateBook(book);
}
@Override
//通过id查询书
public Book queryBookById(Integer id) {
return bookDao.queryBookById(id);
}
@Override
//查询所有的书
public List<Book> queryBooks() {
return bookDao.queryBooks();
}
}
package com.atguigu.test;
import com.atguigu.bean.Book;
import com.atguigu.service.BookService;
import com.atguigu.service.service_impl.BookServiceImpl;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
class BookServiceImplTest {
private BookService bookService = new BookServiceImpl();
@Test
void addBook() {
bookService.addBook(new Book(null,"国哥在手,天下我有!", "1125", new BigDecimal(1000000),
100000000, 0, null));
}
@Test
void deleteBookById() {
bookService.deleteBookById(22);
}
@Test
void updateBook() {
bookService.updateBook(new Book(22,"社会我国哥,人狠话不多!", "1125", new BigDecimal(999999),
10, 111110, null));
}
@Test
void queryBookById() {
System.out.println(bookService.queryBookById(22));
}
@Test
void queryBooks() {
for (Book queryBook : bookService.queryBooks()) {
System.out.println(queryBook);
}
}
}
006-编写图书模块的Web层和页面联调测试
(1)实现获取图书信息功能
创建一个BookServlet类继承BaseServlet
BookServlet类中每一个方法都代表一个功能,那么图书模块需要哪些功能?
添加图书
删除图书
修改图书
图书列表的查询
这里的url-pattern有点不一样,是:/manager/bookServlet (BookServlet的访问地址)
为了后面的权限管理做准备,不是因为有manager目录。
为什么要写为:/manager/bookServlet ?
这里就涉及到了前后台的简单解释:
添加/manager/是为了区分前后台,便于权限检查。
只有先查到了图书列表中的图书,才进行增删改操作,所以先实现 图书列表的查询
图解列表功能流程:
在首页点 后台管理--->点 图书管理 ---> 就可以看到所有图书的信息
图书管理页面就是pages/manager/book_manager.jsp页面(上面图片里写错了),
也是所有图书信息页面。
但是想要在book_manager.jsp页面看到所有图书的信息,首先要从数据库获取所有图书的信息,
book_manager.jsp页面本身是没有任何数据的
所以要先通过BookServlet中的list方法获取所有的图书信息,将图书信息保存在request域中,
再请求转发给book_manager.jsp页面
所以点击图书管理,先跳到 BookServlet程序
所以要修改 manager_menu.jsp 中图书管理的a标签的地址
如下:
注意:因为a标签使用的是get方法,所以需要在BaseServlet程序中再写一个doGet()方法,在doGet()方法中调用doPost()方法即可。否则会出现405错误。
实现BookServlet程序中的list()方法:
在book_manager.jsp中 实现需要实现的内容
(下面图片中不是manager.jsp写错了,应该是是book_manager.jsp):
首先导入JSTL需要的jar包:
JSTL的作用是代替 <% %>
修改book_manager.jsp中的代码:
使用JSTL遍历输出图书信息:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>图书管理</title>
<!--静态包含,base标签,css标签,jQuery文件-->
<%@ include file="/pages/common/head.jsp"%>
</head>
<body>
<div id="header">
<img class="logo_img" alt="" src="static/img/logo.gif" >
<span class="wel_word">图书管理系统</span>
<!--静态包含,后台管理系统的菜单-->
<%@ include file="/pages/common/manager_menu.jsp"%>
</div>
<div id="main">
<table>
<tr>
<td>名称</td>
<td>价格</td>
<td>作者</td>
<td>销量</td>
<td>库存</td>
<td colspan="2">操作</td>
</tr>
<c:forEach items="${requestScope.books}" var="book">
<tr>
<td>${
book.name}</td>
<td>${
book.price}</td>
<td>${
book.author}</td>
<td>${
book.sales}</td>
<td>${
book.stock}</td>
<td><a href="pages/manager/book_edit.jsp">修改</a></td>
<td><a href="#">删除</a></td>
</tr>
</c:forEach>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td><a href="pages/manager/book_edit.jsp">添加图书</a></td>
</tr>
</table>
</div>
<!--静态包含,页脚-->
<%@ include file="/pages/common/footer.jsp" %>
</body>
</html>
(2)实现添加图书功能
添加图书是在上面这个页面中,点击“添加图书”,跳转到book_edit页面。
修改book_edit.jsp:
BookServlet中实现add方法:
点击提交后出现空白,将commons-beanutils-1.9.3 换为 commons-beanutils-1.8.0!
(3)实现删除图书的功能
在上面页面中点击删除,将删除请求发送给服务器,然后刷新页面发现这条数据消失。
修改book_manager.jsp:
将 删除 的 a标签的超链接地址改为:
manager/bookServlet?action=delete&id=${book.id}
点击删除,跳转到BookServlet,
并且告诉BookServlet程序要调用的方法是delete 和 要删除的图书的id号
ctrl + alt + t —> surround with 快捷键 (包括try…catch…)
修改BookServlet程序,实现删除delete功能:
WebUtils中实现数字字符串转换为int类型:
实现功能:在点击删除之后,弹出弹窗确认是否删除。
给表示删除的a标签,绑定单击事件。
修改book_manager.jsp代码:
首先给表示删除的a标签添加一个class属性:
(4)实现修改图书功能
在上面这个页面(book_manager.jsp)点击修改跳转到book_edit.jsp页面
在book_edit.jsp页面显示要修改的那条数据信息
但是book_edit.jsp页面无法显示数据
所以点击 修改 先 跳转到 BookServlet
在BookServlet创建方法 getBook() 获取到要修改的图书的信息
然后再将 这个信息 显示到 book_edit.jsp页面
然后 再 在book_edit.jsp页面 修改 要修改的图书信息
修改完之后,再提交给 BookServlet,通过update()方法 保存到 数据库中
然后 再 请求转发到 book_manager.jsp 显示所有图书列表
首先修改 book_manager.jsp 的 表示修改的 a标签 的地址:
manager/bookServlet?action=getBook&id=${book.id}
点击修改 跳转到 BookServlet
并告诉 BookServlet 通过要修改的图书的id 调用 getBook()方法 ---> 获得要修改的图书的信息
修改 BookServlet :
创建 getBook() 方法 获得要修改的图书的信息
将信息 保存在 request域 中
再 请求转发到 book_edit.jsp
这样 就可以在 book_edit.jsp 中 获取到 要修改的图书的信息
修改 book_edit.jsp : 将要修改的图书的信息 显示到页面上:
修改 BookServlet :
实现update()方法 将修改的内容保存到数据库
注意:这里出现了一个问题:
解决方法一:
在book_manager.jsp中,点击 添加图书 或 修改图书 的 a标签超链接时
附带上 要操作 的 方法;并注入到隐藏域中
解决方法二:
可以判断当前请求参数中是否包含id参数,如果包含id说明是修改操作,如果不包含说明是添加操作。
因为修改的请求参数包括 id:
<a href="manager/bookServlet?action=getBook&id=${book.id}">修改</a>
添加图书的请求参数不包括 id:
<a href="pages/manager/book_edit.jsp">添加图书</a>
只需要对hidden标签的value值做如下修改:
解决方法三:
可以通过判断request域中是否包含 要修改的图书对象,如果没有说明是添加操作,如果有说明是修改操作。
因为点击 添加 会 直接跳到 book_edit.jsp
而点击 修改,会先到BookServlet程序中调用getBook()方法获取要修改的图书对象
只需要对hidden标签的value值做如下修改:
三种解决办法选其中一个,我选第二个。
实现BookServlet中的update方法:
注意:这里出现了一个问题
按照以上方法,bookService.updateBook(book) 方法中 book的id为null
无法修改图书
没有将要修改的图书的id 传过来
所以在book_edit.jsp中再添加一个hidden标签:
十六、书城项目第五阶段——分页
十七、Cookie
001-什么是 Cookie?
1、Cookie 翻译过来是饼干的意思。
2、Cookie 是服务器通知客户端保存键值对的一种技术。
3、客户端有了 Cookie 后,每次请求都发送给服务器。
4、每个 Cookie的大小不能超过 4kb
002-如何创建 Cookie?
003-服务器如何获取 Cookie?
004-Cookie 值的修改
Cookie的值也不能是中文
005-浏览器查看 Cookie
006-Cookie 生命控制
007-Cookie 有效路径 Path 的设置
008-Cookie 练习—免输入用户名登录
十八、Session
001-什么是Session会话
Cookie在浏览器端,Session在服务器端
002-如何创建和获取Session
003-Session域数据的存储和获取
004-Session生命周期控制
如果session设置为永不超时,服务器这边就要越来越多的session,占用内存。
如何让当前session会话马上超时无效?
不是像cookie 设置为0
而是使用另一个API,即另一个方法:
public void invalidate() 让当前 Session 会话马上超时无效
点一下,等3秒,session被销毁;
一秒点一下,timeout一直是3,session无法被销毁。
005-浏览器和 Session 之间是怎么关联的?
问题:在session的超时时长内,关闭浏览器,再打开浏览器,怎么session就没了?
因为一关浏览器 cookie 就没了,那么sesison也就没了,要创建一个新的session
Session 技术,底层其实是基于 Cookie 技术来实现的。
十九、书城项目第六阶段
二十、书城项目第七阶段
二十一、Filter 过滤器
001-什么是Filter 过滤器?
002-基本使用示例
以Filter的作用:权限检查为例
在web目录下创建一个admin目录:有三个文件:a.html,a.jsp,和一张照片。
再在web目录下创建一个登陆页面:login.jsp
如果添加访问权限:访问a.jsp,可以在a.jsp中写以下代码,没有登陆,无法访问,跳转到login.jsp登录页面
但是如果访问html和照片,写不了如下代码,这时就要用到Filter。
注意:Filter类是javax.servlet.Filter
所以不能使用tomcat10 只能使用tomcat8
因为Tomcat10 用jakarta代替了javax
要给当前模块添加tomcat8的lib的jar包
package com.atguigu.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
public class AdminFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
//专门用于拦截请求,过滤响应。
//这里以拦截请求中的权限检查为例子
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpSession session = httpServletRequest.getSession();
Object user = session.getAttribute("user");
//如果等于null,说明没有登录,无法访问目标资源
if (user == null){
//转到login.jsp登录页面
servletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest, servletResponse);
return;
}else{
//登陆了,可以访问目标资源
filterChain.doFilter(servletRequest, servletResponse);
}
}
@Override
public void destroy() {
}
}
003-完整的用户登录和权限检查
以下代码也必须使用javax.servlet.*
否则web-xml中的servlet-class会报错
输出到页面上避免中文乱码,必须写到代码最上方:
resp.setContentType("text/html;charset=UTF-8");
004-Filter的生命周期
005-FilterConfig类介绍
006-FilterChain 过滤器链
如果Filter2中,没有Filterchain.doFilter(),会直接执行后置代码2,不会到目标资源。
007-Filter 的拦截路径
后缀名匹配:后缀名可以随便写,写 .abc 也可以
只要在
<filter-mapping>
<filter-name>Filter2</filter-name>
<url-pattern>target.abc</url-pattern>
</filter-mapping>
地址栏写target.abc就可以访问到