乐趣区

深度分析阿里腾讯面试题-SpringBoot整合Spring-MVC

Java 学习总结 SpringBoot 整合 Spring MVC

1.SpringMVC 概述

MVC(Model–view–controller)是软件工程中的一种软件架构模式,基于此模式把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。目的是通过这样的设计使程序结构更加简洁、直观,降低问题的复杂度。其中各个组成部分的职责为:
视图(View)– UI 设计人员进行图形界面设计,负责实现与用户交互。
控制器(Controller)- 负责获取请求,处理请求,响应结果。
模型(Model)– 实现业务逻辑,数据逻辑实现。

我们在软件设计时,通常要遵循一定的设计原则。MVC 架构模式的设计中,首先基于单一职责原则 (SRP-Single responsibility principle) 让每个对象各司其职,各尽所能。
然后再基于“高内聚,低耦合”的设计思想实现相关层对象之间的交互。这样可以更好提高程序的可维护性和可扩展性。
JavaEE 技术体系中,MVC 设计思想的实现,如图 -14 所示:

在上图中,Servlet 充当 MVC 中的 Controller, 负责调用 model 处理业务,负责转发或重定向某个页面,在页面 (view) 上呈现数据。
模块封装了对 Servlet 的技术的应用,简化了程序员对请求和响应过程中数据的处理。Spring MVC 是 Spring 框架中基于 MVC 设计思想实现的一个用于处理 Web 请求的模块。其简易架构分析,如下图所示:

DispatcherServlet:前端控制器, 处理请求的入口。
HandlerMapping:映射器对象, 用于管理 url 与对应 controller 的映射关系。
Interceptors:拦截器, 实现请求响应的共性处理。
Controller:后端控制器 -handler, 负责处理请求的控制逻辑。
ViewResolver:视图解析器, 解析对应的视图关系 (前缀 +viewname+ 后缀)。
备注:假如希望了解 Spring MVC 的详细处理流程可以基于断点调试法进行跟踪。

2. 初始配置

1. 添加 Spring MVC 依赖

编辑 pom.xml 文件, 添加 web 依赖,Thymeleaf 依赖,代码如下:
Web 依赖(提供了 Spring MVC 核心 API,同时会嵌入一个 Tomcat 服务器)

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Thymeleaf 依赖(提供了一个视图解析器对象以及数据绑定机制)

<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

其中:Spring Web Starter 提供 Spring MVC 依赖支持,并会自动添加一个 tomcat 依赖, 作为嵌入式 web 服务器使用.thymeleaf 是一个 html 模板引擎,提供了与 Spring MVC 进行整合的 API,可作为 MVC 架构中 Web 应用的 View 层。

2. 配置 Spring MVC 核心对象

在 application.proper 视 ties 文件中添加图解析器配置(假如没有配置也会默认配置,在默认配置中 prefix 默认值为 classpath:/templates/, 后缀默认为.html)。

spring.thymeleaf.prefix=classpath:/templates/pages/
spring.thymeleaf.suffix=.html

说明: 要基于配置在 src/main/resources 目录下创建 templates/pages 目录

3.Spring MVC 进行入门实践

第一步:编写 GoodsController 类并将其交给 spring 管理。这样的 Controller 在 SpringMVC 规范中通常称之为 Handler(处理器),我们在企业中有时也会将此对象理解为一个后端控制器。

package com.cy.pj.goods.controller;
@Controller
    
@RequestMapping("/goods/")
public class GoodsController {@RequestMapping("doGoodsUI")
public String doGoodsUI() {return "goods";}
}

第二步:需要在 /templates/pages/ 目录下创建 goods.html
第三步:启动服务器(默认项目嵌入的是 tomcat),打开浏览器进行访问测试。
http://localhost:8080/goods/doGoodsUI
API 应用设计, 如图所示:

课堂练习 1: 将数据库中的商品数据查询出来更新到页面上。

查询时序分析:

第一步: 定义 pojo 对象(com.cy.pj.goods.pojo.Goods)

package com.cy.pj.goods.pojo;

import java.util.Date;
/**
 * pojo 对象,基于此对象封装从数据库查询到的数据
 * 思考:对象靠什么存储数据?属性
 */
public class Goods {
    private Long id;//id bigint primary key auto_increment
    private String name;//name varchar(100) not null
    private String remark;//remark text
    private Date createdTime;//createdTime datetime

    public Long getId() {return id;}

    public void setId(Long id) {this.id = id;}

    public String getName() {return name;}

    public void setName(String name) {this.name = name;}

    public String getRemark() {return remark;}

    public void setRemark(String remark) {this.remark = remark;}

    public Date getCreatedTime() {//        System.out.println("==getCreatedTime==");
        return createdTime;
    }

    public void setCreatedTime(Date createdTime) {this.createdTime = createdTime;}

    @Override
    public String toString() {
        return "Goods{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", remark='" + remark + '\'' +
                ", createdTime=" + createdTime +
                '}';
    }
}

第二步: 定义 GoodsDao 接口及方法

package com.cy.pj.goods.dao;

import com.cy.pj.goods.pojo.Goods;
import org.apache.ibatis.annotations.*;

import java.util.List;

/**
 * @Mapper 是由 Mybatis 框架中定义的一个描述数据层接口对象的注解(所有的注解起到一个描述性的作用)
 * 系统底层启动 mybatis 框架会基于 @Mapper 注解的描述, 创建其接口的实现类, 并将其实现类对象交给 spring 管理
 */
@Mapper
public interface GoodsDao {

    /**
     * 查找所有商品信息
     */
    @Select("SELECT id,name,remark,createdTime FROM tb_goods")
    List<Goods> findObjects();}

第三步: 定义 GoodsService 接口及其实现类

GoodsService

package com.cy.pj.goods.service;

import com.cy.pj.goods.pojo.Goods;

import java.util.List;

public interface GoodsService {List<Goods> findGoods();
}

GoodsServiceImpl

package com.cy.pj.goods.service.impl;

import com.cy.pj.goods.dao.GoodsDao;
import com.cy.pj.goods.pojo.Goods;
import com.cy.pj.goods.service.GoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
/**
 * 商品业务层对象, 负责业务逻辑处理
 */
@Service
public class GoodsServiceImpl implements GoodsService {

    @Autowired
    private GoodsDao goodsDao;

    @Override
    public List<Goods> findGoods() {Long start=System.currentTimeMillis();
        List<Goods> list=goodsDao.findObjects();
        long end=System.currentTimeMillis();
        System.out.println("query time:"+(end-start));
        return list;
    }
}

第四步: 定义 GoodsController 及其 url 映射

package com.cy.pj.goods.controller;

import com.cy.pj.goods.pojo.Goods;
import com.cy.pj.goods.service.GoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@Controller

@RequestMapping("/goods/")
public class GoodsController {

    @Autowired
    private GoodsService goodsService;
    
    //http:localhost:8080/goods/doGoodsUI
    // 此访问路径会传递到 DispatcherServlet 对象
    //DispatcherServlet 对象会基于用户输入的 url 找到对应的 controller 及方法
    //DispatcherServlet 底层会根据反射技术调用对应的控制层方法
    @RequestMapping("doGoodsUI")
    public String doGoodsUI(Model model) {
        // 获取业务数据
        List<Goods> list = goodsService.findGoods();
        // 将数据存储到作用域对象
        model.addAttribute("list", list);
        // 将页面响应到客户端
        return "goods";//view name
        // 将此 view 返回给前端控制器 DispatcherServlet
        // 前端控制器会调用视图解析器对 view 进行解析, 添加前缀和后缀
        //templates/pages/goods.html
        // 最后由 DispatcherServlet 将页面响应给客户端
    }
}

第五步: 定义 goods.html, 通过 Thymeleaf 模板将活动数据呈现在页面上

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <table>
        <thead>
            <th>id</th>
            <th>name</th>
            <th>remark</th>
            <th>createdTime</th>
        </thead>
        <tbody>
            <tr th:each="g:${list}" >
                <td th:text="${g.id}"></td>
                <td th:text="${g.name}"></td>
                <td th:text="${g.remark}"></td>
                <td th:text="${#dates.format(g.createdTime,'yyyy/MM/dd HH:mm')}"></td>
            </tr>
        </tbody>
    </table>
</body>
</html>

第六步: 运行 Application 启动服务器, 在网页中输入 url 地址

启动服务器:

在网页中输入 url 地址:http://localhost:8080/goods/doGoodsUI
查询结果如下:

课堂练习二: 基于 ID 删除商品库中的商品信息

时序分析:

第一步:GoodsDao 中定义基于 id 删除记录的方法 deleteById(Integer id);

package com.cy.pj.goods.dao;

import com.cy.pj.goods.pojo.Goods;
import org.apache.ibatis.annotations.*;

import java.util.List;

/**
 * @Mapper 是由 Mybatis 框架中定义的一个描述数据层接口对象的注解(所有的注解起到一个描述性的作用)
 * 系统底层启动 mybatis 框架会基于 @Mapper 注解的描述, 创建其接口的实现类, 并将其实现类对象交给 spring 管理
 */
@Mapper
public interface GoodsDao {
    /**
     * 基于 id 删除数据库中商品信息
     * @param id
     * @return
     */
    @Delete("delete from tb_goods where id=#{id}")
    int deleteById(Integer id);
    
    /**
     * 查找所有商品信息
     * @return
     */
    @Select("SELECT id,name,remark,createdTime FROM tb_goods")
    List<Goods> findObjects();}

第二步:GoodsService 及实现类中定义 deleteById(Integer id)方法用于执行记录删除
GoodsService

package com.cy.pj.goods.service;

import com.cy.pj.goods.pojo.Goods;

import java.util.List;

public interface GoodsService {List<Goods> findGoods();

    void deleteById(Integer id);
}

GoodsServiceImpl

package com.cy.pj.goods.service.impl;

import com.cy.pj.goods.dao.GoodsDao;
import com.cy.pj.goods.pojo.Goods;
import com.cy.pj.goods.service.GoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
/**
 * 商品业务层对象, 负责业务逻辑处理
 */
@Service
public class GoodsServiceImpl implements GoodsService {

    @Autowired
    private GoodsDao goodsDao;

    @Override
    public List<Goods> findGoods() {Long start=System.currentTimeMillis();
        List<Goods> list=goodsDao.findObjects();
        long end=System.currentTimeMillis();
        System.out.println("query time:"+(end-start));
        return list;
    }

    @Override
    public void deleteById(Integer id) {int rows=goodsDao.deleteById(id);
    }


}

第三步:ActivityController 中定义 doDeleteById(Integer id)方法用于处理删除请求

package com.cy.pj.goods.controller;

import com.cy.pj.goods.pojo.Goods;
import com.cy.pj.goods.service.GoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@Controller

@RequestMapping("/goods/")
public class GoodsController {

    @Autowired
    private GoodsService goodsService;

    //http://localhost:8080/goods/doDeleteById
    @RequestMapping("doDeleteById")
    public String doDeleteById(Integer id){
        // 调用业务层对象执行删除操作
        goodsService.deleteById(id);
        // 思考: 删除以后要做什么?
        // 在当前业务中我们可以重定向到查询页面
        return "redirect:doGoodsUI";
    }

    //http:localhost:8080/goods/doGoodsUI
    // 此访问路径会传递到 DispatcherServlet 对象
    //DispatcherServlet 对象会基于用户输入的 url 找到对应的 controller 及方法
    //DispatcherServlet 底层会根据反射技术调用对应的控制层方法
    @RequestMapping("doGoodsUI")
    public String doGoodsUI(Model model) {
        // 获取业务数据
        List<Goods> list = goodsService.findGoods();
        // 将数据存储到作用域对象
        model.addAttribute("list", list);
        // 将页面响应到客户端
        return "goods";//view name
        // 将此 view 返回给前端控制器 DispatcherServlet
        // 前端控制器会调用视图解析器对 view 进行解析, 添加前缀和后缀
        //templates/pages/goods.html
        // 最后由 DispatcherServlet 将页面响应给客户端
    }
}

第四步: 定义 goods.html, 通过 Thymeleaf 模板将活动数据呈现在页面上

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <table>
        <thead>
            <th>id</th>
            <th>name</th>
            <th>remark</th>
            <th>createdTime</th>
        </thead>
        <tbody>
            <tr th:each="g:${list}" >
                <td th:text="${g.id}"></td>
                <td th:text="${g.name}"></td>
                <td th:text="${g.remark}"></td>
                <td th:text="${#dates.format(g.createdTime,'yyyy/MM/dd HH:mm')}"></td>
                <td><a th:href="@{/goods/doDeleteById(id=${g.id})}">delete</a></td>
            </tr>
        </tbody>
    </table>
</body>
</html>

第五步: 运行 Application 启动服务器, 在网页中输入 url 地址

启动服务器:

在网页中输入 url 地址:http://localhost:8080/goods/doGoodsUI

点击 delete

课堂练习三: 将页面用户输入的商品信息写入到数据库

时序分析

第一步: 在 GoodsDao 中定义 insert(Goods entity)方法以及 SQL 映射

package com.cy.pj.goods.dao;

import com.cy.pj.goods.pojo.Goods;
import org.apache.ibatis.annotations.*;

import java.util.List;

/**
 * @Mapper 是由 Mybatis 框架中定义的一个描述数据层接口对象的注解(所有的注解起到一个描述性的作用)
 * 系统底层启动 mybatis 框架会基于 @Mapper 注解的描述, 创建其接口的实现类, 并将其实现类对象交给 spring 管理
 */
@Mapper
public interface GoodsDao {
    /**
     * 基于 id 删除数据库中商品信息
     * @param id
     * @return
     */
    @Delete("delete from tb_goods where id=#{id}")
    int deleteById(Integer id);

    /**
     * 基于 id 进行批量删除操作
     * @param ids
     * @return
     */
    //int deleteObjects(@Param("ids")Integer...ids); 早期版本需要基于 @Param 注解
    int deleteObjects(Integer...ids);//sql 映射中可使用 array,ids 参数名来接收方法参数

    /**
     * 查找所有商品信息
     * @return
     */
    @Select("SELECT id,name,remark,createdTime FROM tb_goods")
    List<Goods> findObjects();

    @Insert("insert into tb_goods (name,remark,createdTime) values(#{name},#{remark},now())")
    int insert(Goods entity);
}

第二步: 在 GoodsService 接口及实现类中添加 insert(Goods entity)

GoodsService

package com.cy.pj.goods.service;

import com.cy.pj.goods.pojo.Goods;

import java.util.List;

public interface GoodsService {List<Goods> findGoods();

    void deleteById(Integer id);

    int insert(Goods entity);
}

GoodsServiceImpl

package com.cy.pj.goods.service.impl;

import com.cy.pj.goods.dao.GoodsDao;
import com.cy.pj.goods.pojo.Goods;
import com.cy.pj.goods.service.GoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
/**
 * 商品业务层对象, 负责业务逻辑处理
 */
@Service
public class GoodsServiceImpl implements GoodsService {

    @Autowired
    private GoodsDao goodsDao;

    @Override
    public List<Goods> findGoods() {Long start=System.currentTimeMillis();
        List<Goods> list=goodsDao.findObjects();
        long end=System.currentTimeMillis();
        System.out.println("query time:"+(end-start));
        return list;
    }

    @Override
    public void deleteById(Integer id) {int rows=goodsDao.deleteById(id);
    }

    @Override
    public int insert(Goods entity) {int rows=goodsDao.insert(entity);
        return rows;
    }
}

第三步: 在 GoodsController 对象中添加 doSaveGoods 方法并定义 url 映射

package com.cy.pj.goods.controller;

import com.cy.pj.goods.pojo.Goods;
import com.cy.pj.goods.service.GoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@Controller

@RequestMapping("/goods/")
public class GoodsController {

    @Autowired
    private GoodsService goodsService;


    //http://localhost:8080/goods/doDeleteById
    @RequestMapping("doDeleteById")
    public String doDeleteById(Integer id){
        // 调用业务层对象执行删除操作
        goodsService.deleteById(id);
        // 思考: 删除以后要做什么?
        // 在当前业务中我们可以重定向到查询页面
        return "redirect:doGoodsUI";
    }

    @RequestMapping("doSaveGoods")
    public String doSaveGoods(Goods entity){goodsService.insert(entity);
        return "redirect:doGoodsUI";
    }

    //http:localhost:8080/goods/doGoodsUI
    // 此访问路径会传递到 DispatcherServlet 对象
    //DispatcherServlet 对象会基于用户输入的 url 找到对应的 controller 及方法
    //DispatcherServlet 底层会根据反射技术调用对应的控制层方法
    @RequestMapping("doGoodsUI")
    public String doGoodsUI(Model model) {
        // 获取业务数据
        List<Goods> list = goodsService.findGoods();
        // 将数据存储到作用域对象
        model.addAttribute("list", list);
        // 将页面响应到客户端
        return "goods";//view name
        // 将此 view 返回给前端控制器 DispatcherServlet
        // 前端控制器会调用视图解析器对 view 进行解析, 添加前缀和后缀
        //templates/pages/goods.html
        // 最后由 DispatcherServlet 将页面响应给客户端
    }
}

第四步: 在页面中 goods.html 添加 form 表单 (用户填写数据) 并设置样式

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
   <form action="/goods/doSaveGoods" method="post">
       <ul>
           <li>name:
           <li><input type="text" name="name">
           <li>remark:
           <li><textarea rows="5" cols=""50 name="remark"></textarea>
           <li><input type="submit" value="save"></li>
       </ul>
   </form>
   <fieldset>
       <legend> 商品列表 </legend>
       <table width="50%">
    <table>
        <thead>
            <th>id</th>
            <th>name</th>
            <th>remark</th>
            <th>createdTime</th>
        </thead>
        <tbody>
            <tr th:each="g:${list}" >
                <td th:text="${g.id}"></td>
                <td th:text="${g.name}"></td>
                <td th:text="${g.remark}"></td>
                <td th:text="${#dates.format(g.createdTime,'yyyy/MM/dd HH:mm')}"></td>
                <td><a th:href="@{/goods/doDeleteById(id=${g.id})}">delete</a></td>
            </tr>
        </tbody>
    </table>
</body>
</html>

第五步: 在表单中输入数据, 然后点击保存按钮, 将数据传递到服务端



最后

有什么不懂的欢迎在下方留言讨论,也可以选择私信问我,私信我一般看到之后都会回的,当然特别忙的时候没看到的话也请见谅!

退出移动版