JavaWeb - 黑马旅游网(1):项目启动

项目课程链接:https://www.bilibili.com/video/BV1CE411E7h4
完整课程连接:https://www.bilibili.com/video/BV1uJ411k7wy

1 案例引述

十一小长假期间在网上找了一个 Web 开发项目公开课:黑马旅游网。一方面,这个项目中涉及到大量 JavaSE、数据库、HTTP协议、Servlet 等基础知识,项目整体综合性较强。另一方面,这个案例尚未涉及现今比较高级热门的后端开发技术栈(SSM、SpringBoot等)以及以在高并发场景下的应对策略。因此比较适合具备一定 Java 语言和网络编程基础但是还未接触高级开发框架的读者进行阅读学习。

整个项目中包含了当今 Web 应用中许多典型的业务场景,例如注册、登陆、收藏等……笔者学习和实践的过程中,在跟随授课老师的讲解完成课上内容开发的基础上,自己也在课后补充了一些其它有趣的功能,同时也解决了项目中一些 BUG,这些在后续的系列博客中都会涉及,也算是对自己学习的检验,以及加深对后端业务逻辑的理解。

项目的第三方依赖维护跟随课上要求:使用 Maven 配置和管理。在实践的过程中也确实体会到 Maven 的强大。另外,笔者使用 Git 建立仓库和维护整个项目开发周期。整个项目代码仓库也已经上传至我的 GitHub 仓库 GitHub 仓库。在此我安利一个高效的 Git GUI 软件:GitKraken,它的可视化界面和交互操作使得分支管理和版本维护更加直观,并且支持关联 GitHub 账户,支持一键 Push 至远程仓库等功能。关于 Git 以及 GitKraken 的使用教学可以参考这个视频教程:Git + GitHub 10分钟完全入门10分钟 Git 进阶教程

在这里插入图片描述
笔者是通过这个简单的练习项目对过往学习的基础知识进行总结和回顾。引述最后,也期待每一位阅读此系列博客的读者在阅读以及课堂学习实践过程中能够加深对基础知识的理解和梳理。我不希望您直接将我的仓库 fork 下来能运行就算了,我的 Git 仓库、博客,甚至 commit message 都可以作为提高学习效率的辅助内容。但是,只有真正的跟着任课老师从零开始,一步一步将整个项目敲出来,并理解其中的每一个业务逻辑和实现细节才能加深对技术的理解,提高分析问题、解决问题的能力。

2 项目架构与技术选型

项目在开发过程中采用经典的三层架构(Web - Service - Dao),且前后端分离。因此,一些实时的前端渲染不再使用 JSP 实现,而通过 AJAX 实现。各层技术选型归纳如下:

2.1 Web 层

  • Servlet:前端控制程序
  • HTML:前端页面(前后端分离架构,不适用JSP)
  • Filter:过滤器
  • BeanUtils:数据封装
  • Jackson:json 序列化工具,前后端数据通用传输格式

2.2 Service 层

  • JavaMail:Java 邮件工具
  • Redis:nosql 内存数据库
  • Jedis:Java 的 Jedis 客户端

2.3 Dao 层

  • mysql:关系数据库
  • druid:数据库连接池
  • Spring Jdbc Template:数据库连接工具

3 代码文件结构

代码文件结构如下图,以下对 java 文件夹下的不同目录中的代码功能做简要介绍
在这里插入图片描述

3.1 domain

com.abe.domain 中定义具体的 Bean 封装类,这些类中的私有 fields 与数据库中对应表的字段一一对应。

3.2 dao

com.abe.dao 和 com.abe.dao.impl 对应项目架构中的 Dao 层,dao 中定义的数据库操作接口,dao.impl 中定义具体的实现类。

3.3 service

com.abe.service 和 com.abe.service.impl 对应项目架构中的 Service 层。service 中定义业务逻辑接口,service.impl 中定义具体的业务逻辑实现类,根据不同的业务需求调用不同的 dao 实现类对象对数据库做 CRUD。

3.4 web

com.abe.web.servlet 和 com.abe.web.filter 对应项目架构中的 Web 层。根据不同的 Web 应用定义相应的 Servlet 代码。项目中依据不同的执行实体分别定义了对应的 Servlet 类。后续开发还将抽取 Servlet 中的通用功能,并单独封装为一个抽象类 BaseServlet,并作为其它 Servlet 的父类。使得代码结构更加整洁,便于维护和扩展。

/**
 * Servlet基类
 *      该Servlet不需要被访问,所以不加虚拟路径注解
 */
public abstract class BaseServlet extends HttpServlet {

    private ObjectMapper mapper = new ObjectMapper();

    /**
     * baseServlet方法分发
     */
    protected void service(HttpServletRequest req, HttpServletResponse resp) {
        // 1.获取请求路径
        String uri = req.getRequestURI();   // 例如:/travel/user/add
        // 2.获取方法名称
        String methodName = uri.substring(uri.lastIndexOf('/') + 1);
        // 3.获取方法对象(反射)
        /*this指向真正的实例化对象,即BaseServlet下面的继承类:com.abe.web.servlet.UserServlet@5fccaa6f*/
        /*this: 谁调用我,我代表谁*/
        try {
            Method method = this.getClass().getMethod(methodName,
                    HttpServletRequest.class, HttpServletResponse.class);
            // 4.执行方法
            method.invoke(this, req, resp);
        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 将输入对象序列化为json,并回写客户端浏览器
     * @param object 输入对象
     * @param response Http响应对象
     */
    public void writeValue(Object object, HttpServletResponse response) throws IOException {
        response.setContentType("application/json;charset=utf-8");
        mapper.writeValue(response.getOutputStream(), object);
    }

    /**
     * 将输入对象序列化为json并返回给调用者
     * @param object 输入对象
     * @return json字符串
     */
    public String writeValueAsString(Object object) throws JsonProcessingException {
        return mapper.writeValueAsString(object);
    }

    /**
     * 向客户端浏览器回传Cookie(持久化登录状态)
     * @param cookie Cookie对象
     * @param maxAge Cookie生命周期
     * @param response Http响应对象
     */
    public void writeCookie(Cookie cookie, int maxAge, HttpServletResponse response) {
        cookie.setMaxAge(maxAge);
        response.addCookie(cookie);
    }

    /**
     * 字符串数字类型转换方法
     *      将数字字符串转换为数字,例如:"5" -> 5
     * @param numStr 数字字符串
     * @return 整型数字
     * @throws ClassCastException
     *      类型转换异常,避免传入的字符串内容不是数字
     */
    protected int parseInt(String numStr, int defaultValue) throws ClassCastException {
        return (numStr != null && numStr.length() > 0 && !"null".equalsIgnoreCase(numStr) && !"NaN".equalsIgnoreCase(numStr))
                ? Integer.parseInt(numStr) : defaultValue;      // 注意:浏览器提交的空值到后台会被识别为<字符串"null">
    }

    /**
     * 非数字字符串参数处理方法
     * @param str 字符串对象
     * @return 处理后的字符串对象
     */
    protected String parseStr(String str) {
        /*tomcat7存在中文乱码问题*/
        if (str != null && str.length() > 0 && !"null".equalsIgnoreCase(str) && !"NaN".equalsIgnoreCase(str)) {
            str = new String(str.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            return str;
        }
        return null;
    }
}

3.5 util

com.abe.util 中定义了许多静态工具类,例如 MySQL 链接、Redis链接、邮件发送等。这些代码复用度很高,且执行时无需专门实例化对象。

4 数据库表设计

课程中直接提供的建立表及添加数据的 sql 代码以及对应的 domain 代码包,按照课程视频逐步操作即可很快实现建库和建表。数据库中所有表格的结构以及相互关系如下图:

在这里插入图片描述
以下对每张表的简要介绍,并给出对应的 Bean 封装类,其中字段的含义说明待写道涉及开发相关功能的博客时在详细介绍。

4.1 tab_user

注册用户信息表

/**
 * 用户实体类
 */
public class User implements Serializable {
    private int uid;                // 用户id
    private String username;        // 用户名,账号
    private String password;        // 密码
    private String name;            // 真实姓名
    private String birthday;        // 出生日期
    private String sex;             // 男或女
    private String telephone;       // 手机号
    private String email;           // 邮箱
    private String status;          // 激活状态,Y代表激活,N代表未激活
    private String code;            // 激活码(要求唯一)
	
	/* setter & getter 方法 */
}

4.2 tab_favorite

关联用户和旅游线路的表格,记录了用户的旅游路线收藏信息

/**
 * 收藏实体类
 */
public class Favorite implements Serializable {
    private Route route;//旅游线路对象
    private String date;//收藏时间
    private User user;//所属用户
	
	/* setter & getter 方法 */
}

4.3 tab_route

旅游线路信息表

/**
 * 旅游线路商品实体类
 */
public class Route implements Serializable {
    private int rid;//线路id,必输
    private String rname;//线路名称,必输
    private double price;//价格,必输
    private String routeIntroduce;//线路介绍
    private String rflag;   //是否上架,必输,0代表没有上架,1代表是上架
    private String rdate;   //上架时间
    private String isThemeTour;//是否主题旅游,必输,0代表不是,1代表是
    private int count;//收藏数量
    private int cid;//所属分类,必输
    private String rimage;//缩略图
    private int sid;//所属商家
    private String sourceId;//抓取数据的来源id
    private Category category;//所属分类
    private Seller seller;//所属商家
    private List<RouteImg> routeImgList;//商品详情图片列表

	/* setter & getter 方法 */
}

4.4 tab_seller

商家信息表

/**
 * 商家实体类
 */
public class Seller implements Serializable {
    private int sid;//商家id
    private String sname;//商家名称
    private String consphone;//商家电话
    private String address;//商家地址
	
	/* setter & getter 方法 */
}

4.5 tab_route_img

记录旅游线路相应页面的图片存储路径信息(项目图片存储路径一定要严格遵循 Maven 的保存规范,或者按照课上讲述存放,否则会导致后期开发过程中图片加载出现问题)。

/**
 * 旅游线路图片实体类
 */
public class RouteImg implements Serializable {
    private int rgid;//商品图片id
    private int rid;//旅游商品id
    private String bigPic;//详情商品大图
    private String smallPic;//详情商品小图
	
	/* setter & getter 方法 */
}

4.6 tab_category

记录网页<header></header>标签中分类栏内容的表格,即:

在这里插入图片描述

/**
 * 分类实体类
 */
public class Category implements Serializable {
    private int cid;//分类id
    private String cname;//分类名称
	
	/* setter & getter 方法 */
}

5 内容小结

本文作为整个综合练习案例系列博客的首篇,首先介绍了笔者的学习目的;而后介绍了项目的技术栈、开发架构、数据库设计等概要性介绍。后续的该系列博客将对其中每一个功能的业务逻辑进行梳理总结并给出关键的实现代码。

我的黑马旅游网项目代码仓库链接:https://github.com/Abexope/ItCast-HeiMa-Travel

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值