前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 JSP+Servlet 模仿京东页面实现购物车功能

使用 JSP+Servlet 模仿京东页面实现购物车功能

作者头像
wsuo
发布2020-07-30 19:25:48
3.7K2
发布2020-07-30 19:25:48
举报
文章被收录于专栏:技术进阶之路技术进阶之路

可能很多人看到标题都不会点进来,因为 JSP 这种老掉牙的技术很多人根本不学,所以我有些感想写在下面。

这是在学校选课老师让做的实验报告,可能大家会觉得这些东西毫无意义,因为 JSP 早就没人使用了,原因是因为写页面太繁琐,执行速度慢,消耗内存,响应速度慢不能处理高并发等原因;但是我想觉得不能因为他现在被淘汰了就不去学他,更不能抱着轻蔑的态度去学习这门技术,我自己在学习的过程中一直在惊叹 JSP 太强了,真的简化了很多后台开发,还有那些标签技术,真的是独具匠心,发明出这项技术的人真的非常厉害。现在越来越多的 Java 开发相关人员上来直接学习 SpringBoot 等框架,然后快速开发出一个网页,看起来很厉害,但这是不对的,也是错误的,我也学过 Spring 和 SpringBoot 等流行框架,但是现在还是在老老实实的跟着学校的进度学习 JSP,因为我觉得经典的东西一定有学习的价值,只有搞懂基础才能提高境界,这些高大上的框架确实极大的简化了我们的开发,但是有没有想过,如果你一直学习这些别人封装好的框架,其实你根本没有一点核心竞争力,这些东西你会,别人一样也可以学习,而且也是速成,所以我们要掌握基础,学习底层,这样就算别人想超过你也要付出很长时间,久而久之你就有你的核心竞争力了,所以,不要看不起任何一项技术,每一项技术都有他存在的意义。其实仔细想想,我们到底会什么?全都是用的别人封装好的框架,我们只会调用接口,我们写 Java 程序调用 JDK 的接口,然后 Servlet 封装了 JDK,接着 Spring 封装了 Servlet ,简化我们的开发,后来 SpringBoot 进而封装了 Spring 框架,让我们开发网页触手可得······我相信将来还会有一层一层的封装,到最后我们写网页可能是几行代码就搞定了,那个时候可能有的 Java 程序员看似写了一个网页,他可能都不知道 JDK 是什么(夸张的比喻一下),因为封装太多太多层了。换个视角,我甚至觉得人类本来就是调用接口生活,比如我们想吃饭,不想自己做,怎么办?好办,调用美团外卖的接口,传入参数20元,返回一份盖浇饭。说了那么多,就想表明一个观点,就是调用接口虽然方便,但是谁都会调,请问你的核心竞争力是什么?当然我也是个巨菜,没有核心竞争力可言,目前在阅读 JDK 源码和学习算法,感兴趣的朋友可以一起阅读 源码 和算法 交流

下面开始正文。


实验一 Servlet基础操作

先来看一下最终效果:

在这里插入图片描述
在这里插入图片描述

一、基础功能

1、项目结构

首先来看一下项目的整体结构:

image.png
image.png

2、数据初始化

首先是数据的初始化,这里为了使 Servlet 容器能在一开始就加载数据,我选择在注解中进行了如下配置:

代码语言:javascript
复制
@WebServlet(name = "ShoppingCartServlet", 
        urlPatterns = {
            "/shop/products",
            "/shop/details",
            "/shop/addCart",
            "/shop/deleteItem",
            "/shop/clearCart"}, 
        loadOnStartup = 1)

其中 urlPatterns 为匹配的路径。 这样在一开始就可以加载在 init 方法中的数据了。

代码语言:javascript
复制
/**
     * 数据的初始化
     *
     * @param config 配置参数,可以获取应用作用域
     * @throws ServletException 抛出异常
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        // 装载数据
        Product p1 = new Product
                (1, "单反相机", "最没有性价比的单反相机", 3306f);
        Product p2 = new Product
                (2, "双反相机", "最不值的双反相机", 3307f);
        Product p3 = new Product
                (3, "三反相机", "最难看的三反相机", 3308f);
        Product p4 = new Product
                (4, "四反相机", "最花里胡哨的四反相机", 3309f);
        List<Product> list = new ArrayList<>();
        list.add(p1);
        list.add(p2);
        list.add(p3);
        list.add(p4);
        // 保存到应用作用域中
        config
                .getServletContext()
                .setAttribute("products", list);
    }

因为这些数据是所有的应用都会用到的,所以把它放在 context上下文域中,代替了从数据库查询数据。

3、主页

然后我们访问页面,会自动跳转到 productList页面,来看一下 index页面的代码:

代码语言:javascript
复制
<body>
<c:redirect url="shop/productList.jsp"/>
</body>

就只有一行代码,是一个重定向。

访问之后的页面如图:

image.png
image.png

我们可以选择喜欢的商品,也可以查看购物车。

4、商品详情

这里我们先点进去商品详情:

image.png
image.png

请求地址为:

代码语言:javascript
复制
http://localhost:8080/WsShoppingCart/shop/details?id=1

这里拼接了一个 id ,我们可以在 Servlet中取出来,里看代码:

首先是根据请求路径,我们把不同的请求交给不同的方法来处理:

代码语言:javascript
复制
/**
     * 根据请求参数后缀来分别处理请求
     *
     * @param request 请求
     * @param response 响应
     * @throws ServletException Servlet异常
     * @throws IOException IO异常
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String uri = request.getRequestURI();
        if (uri.endsWith("products")) displayProducts(request, response);
        else if (uri.endsWith("details")) displayGoods(request, response);
        else if (uri.endsWith("addCart")) addCart(request, response);
        else if (uri.endsWith("deleteItem")) deleteCard(request, response);
        else if (uri.endsWith("clearCart")) clearCart(request, response);
    }

比如我们这次请求就会转发到 displayGoods方法中去处理,下面来看一下该方法:

代码语言:javascript
复制
/**
     * 根据 ID 查询用户点击的是哪一个商品,然后跳转到商品详情页面
     * 响应请求: /shop/details
     *
     * 注意: 只接受同级目录下的页面请求,所以 ./ 或者不写都可以
     *
     * @param request 请求
     * @param response 响应
     * @throws ServletException Servlet 异常
     * @throws IOException IO 异常
     */
    private void displayGoods(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Integer id = Integer.valueOf(request.getParameter("id"));
        List<Product> products = (List<Product>) getServletContext()
                .getAttribute("products");
        Product product = findById(id, products);
        if (product != null) request.setAttribute("product", product);
        request.getRequestDispatcher("./productDetails.jsp")
                .forward(request, response);
    }

我们首先从请求路径中获取到了请求参数 id,然后从 context中获取到了之前存进去的商品,这里调用了一个 findById 方法,来看一下这个方法:

代码语言:javascript
复制
/**
     * 根据 id 查询相关内容是否在作用域中
     *
     */
    private Product findById(Integer id, List<Product> products) {
        for (Product product : products) {
            if (id.equals(product.getId())) {
                return product;
            }
        }
        return null;
    }

它的作用就是根据 id 查询商品,如果查到了就返回,否则返回为空。

这样我们获取这个对象之后就再把它存放到请求作用域中,然后将请求转发到 productDetails 页面。也就是之前我们看到的页面。

5、添加商品到购物车

然后我们可以在文本框中输入加入购物车的商品的数量:

image.png
image.png

如果我们点击按钮,他会发送一个请求,我们使用这个方法来处理这个请求:

代码语言:javascript
复制
/**
     * 添加到购物车
     * 响应请求: /shop/addCart
     *
     * @param request 请求
     * @param response 响应
     * @throws IOException 异常
     */
    private void addCart(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ServletContext context = getServletContext();
        HttpSession session = request.getSession();
        Integer id = Integer.parseInt(request.getParameter("id"));
        Integer quality = Integer.parseInt(request.getParameter("quality"));
        List<Product> products = (List<Product>) context
                .getAttribute("products");
        // 初始化购物车
        Map<Product, Integer> cart = (Map<Product, Integer>) session
                .getAttribute("cart");
        if (cart == null) {
            cart = new HashMap<>();
        }
        Product product = findById(id, products);
        if (product != null) {
            cart.put(product, quality);
            session.setAttribute("cart", cart);
        }
        displayProducts(request, response);
    }

那他会发送什么请求呢?

代码语言:javascript
复制
<form action="addCart" method="post">
    <input type="hidden" name="id" value="${requestScope.product.id}">

我们可以查看 form 表单,而且我们加了一个隐藏域,这样在 Servlet 中才可以获取商品的 id 然后放入 session 域中。并且重定向到 productList 页面中。

image.png
image.png

我们可以点击查看购物车:

image.png
image.png

那么 cart 页面是怎么获取数据的呢?

这里用到了 jstl 的标签库以及 el 表达式:

代码语言:javascript
复制
<c:forEach items="${sessionScope.cart}" var="cart">
                <tr>
                    <td>?${cart.value}</td>
                    <td>${cart.key.name}</td>
                    <td><span style="color: darkorange">${cart.key.price}</span>?</td>
                    <td><span style="color: darkorange">${cart.key.price * cart.value}</span>?</td>
                    <c:if test="${sessionScope.cart.size() >= 2}">
                        <td>
<%--                            <a href="deleteItem?id=${cart.key.id}">删除?</a>--%>
                            <input type="button" value="删除" onclick="confirmDel(${cart.key.id})">
                        </td>
                    </c:if>
                </tr>
                <c:set var="total" value="${cart.value * cart.key.price + total}"/>
                <c:set var="sum" value="${sum + cart.value}"/>
            </c:forEach>

这样就可以输出到页面上了,而且我们还可以删除商品。

6、从购物车中删除商品

从购物车中删除商品需要 cart 页面发送一个请求,然后在 Servlet 页面中处理请求。

代码语言:javascript
复制
/**
     * 删除商品
     * /shop/deleteItem
     *
     * @param request 请求
     * @param response 响应
     */
    private void deleteCard(HttpServletRequest request, HttpServletResponse response) throws IOException {
        Integer id = Integer.parseInt(request.getParameter("id"));
        ServletContext context = getServletContext();
        HttpSession session = request.getSession();
        List<Product> products = (List<Product>) context
                .getAttribute("products");
        Map<Product, Integer> cart = (HashMap) session
                .getAttribute("cart");

        Product product = findById(id, products);
        if (product != null) {
            cart.remove(product);
            session.setAttribute("cart", cart);
        }
        response.sendRedirect("./cart.jsp");
    }

我们从 session 域中获取对象之后再删除该对象,因为它本身是一个 Map 集合,最后重新存到 session 域中,然后重定向到他自己实现刷新效果。

删除之后:

image.png
image.png

7、总体的流程演示

下面通过 GIF 演示一下:

2020_04_20.gif
2020_04_20.gif

二、扩展功能

下面实现拓展功能:

1、清空购物车

cart 页面:

代码语言:javascript
复制
<a href="./clearCart">清空购物车❗</a>

Servlet 中的代码:

代码语言:javascript
复制
/**
     * 清空购物车
     * 响应请求: /shop/clearCart
     *
     * @param request
     * @param response
     */
    private void clearCart(HttpServletRequest request, HttpServletResponse response) throws IOException {
        request.getSession().removeAttribute("cart");
        response.sendRedirect("./cart.jsp");
    }

就是从会话作用域中移除掉所有数据。

2、显示购物车中的商品种类数量和商品总数量

我们先在 forEach 循环中设置一个值 sum 和 total,用于记录商品数量与总数量。

代码语言:javascript
复制
	<c:set var="total" value="${cart.value * cart.key.price + total}"/>
	<c:set var="sum" value="${sum + cart.value}"/>
</c:forEach>


<tr>
                <td>✍总计 <span style="color: darkorange">${total}</span>?</td>
                <td>总数量 ${sum}</td>
                <td></td>
                <td></td>
                <td><a href="./clearCart">清空购物车❗</a></td>
</tr>
image.png
image.png

3、购物车为空的提示

使用 jstl 实现,使用相关标签:

代码语言:javascript
复制
<c:choose>
    <c:when test="${sessionScope.cart != null}">
	</c:when>
    	<c:otherwise>
    	    <h2>购物车中没有商品 ?</h2>
	</c:otherwise
</c:choose>

这样一旦购物车为空就会进入 otherwise 语句。

image.png
image.png

4、删除时提示是否确认删认

使用 JavaScript 实现:

代码语言:javascript
复制
<script>
        function confirmDel(param) {
            if (window.confirm("您确定要删除这件美丽的商品吗?")) {
                document.location = "deleteItem?id=" + param;
            }
        }
</script>

<input type="button" value="删除" onclick="confirmDel(${cart.key.id})">
image.png
image.png

5、在输入数量时,如果不是数字,要提示

使用 JavaScript 的正则表达式实现:

代码语言:javascript
复制
<script>
        test = function () {
            var quality = document.getElementById("quality");
            var reg = /^[0-9]*$/g;
            if (!reg.test(quality.value)) {
                alert("请输入数字");
            }
        };
</script>

<input type="text" name="quality" id="quality" onkeyup="test()">

这样就可以了。

image.png
image.png

三、关于数据源

由于我们没有使用数据库,所以自己造了数据,但是不太真实也很麻烦,所以我后来使用爬虫爬了京东的数据,然后模仿他的页面写了一个 jsp:

首先来看一下如何爬取数据? 我这里使用的是 jsoup 包,代码如下:

代码语言:javascript
复制
private void initProduct(String keyWords) {
        String url = "https://search.jd.com/Search?keyword=" + keyWords + "&enc=utf-8";
        List<Product> list = new ArrayList<>();
        try {
            Document parse = Jsoup.parse(new URL(url), 30000);
            Element element = parse.getElementById("J_goodsList");
            Elements li = element.getElementsByTag("li");
            for (Element el : li) {
                count++;
                String img = el.getElementsByTag("img").eq(0).attr("source-data-lazy-img");
                String price = el.getElementsByClass("p-price").eq(0).text();
                String title = el.getElementsByClass("p-name").eq(0).text();
                Product product =
                        new Product(count, title, img, Float.valueOf(price.split("¥")[1]));
                list.add(product);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        this
                .getServletContext()
                .setAttribute("products", list);
    }

然后写一个接口,当有请求过来的时候就可以从请求中获取关键字,然后查询,再重定向到首页,实现展示商品的功能:

代码语言:javascript
复制
/**
     * 搜索功能
     * /shop/search
     *
     */
    private void doSearch(
            HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        String keyWords = request.getParameter("keyword");
        System.out.println("数据初始化");
        this.getServletContext().removeAttribute("products");
        initProduct(keyWords);
        System.out.println("转发到商品首页");
        displayProducts(request, response);
    }

感兴趣的同学可以去 Github 上面查看相关代码。

四、总结

  • 通过这次实验,巩固了 Servlet 的基本操作,以及 JSP 的操作,体会了 JSP 的页面强大之处,JSP 太强了!!!(逃
  • 还使用了 jstl 的表达式,用起来很方便,就算不会 java 的人也能轻松实现 java 服务端和客户端代码的编写,太强了!
  • 各个域之间的存储数据,让我更清楚的明白了域的区别的与联系,适合什么样的场景就用什么样的域。

相关源码已上传至 Github 地址

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-04-23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 实验一 Servlet基础操作
  • 一、基础功能
    • 1、项目结构
      • 2、数据初始化
        • 3、主页
          • 4、商品详情
            • 5、添加商品到购物车
              • 6、从购物车中删除商品
                • 7、总体的流程演示
                • 二、扩展功能
                  • 1、清空购物车
                    • 2、显示购物车中的商品种类数量和商品总数量
                      • 3、购物车为空的提示
                        • 4、删除时提示是否确认删认
                          • 5、在输入数量时,如果不是数字,要提示
                          • 三、关于数据源
                          • 四、总结
                          相关产品与服务
                          容器服务
                          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档