SpringMVC

MVC(Model View Controller)

1. MVC

1.1 简介

  • MVC是一种软件设计规范

    • Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务

      • Dao、Service

      • View(视图):负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西

        • JSP
    • Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。也就是说控制器做了个调度员的工作

      • Servlet
  • 业务逻辑数据显示分离的方法来组织代码

  • 主要作用:降低视图与业务逻辑间的双向偶合
  • 典型MVC - SMBMS(JSP+servlet+javabean)

MVC逻辑

:thought_balloon: POJO的划分

  • PO(Persistent Object) = DO(Data Objecy)

    • 持久化对象,它跟持久层(通常是关系型数据库)的数据结构形成一一对应的映射关系,如果持久层是关系型数据库,那么,数据表中的每个字段(或若干个)就对应PO的一个(或若干个)属性。通过 DAO 层向上传输数据源对象。

      持久层:所谓“持久层”,也就是在系统逻辑层面上,专著于实现数据持久化的一个相对独立的领域(Domain),是把数据保存到可掉电式存储设备中。持久层是负责向(或者从)一个或者多个数据存储器中存储(或者获取)数据的一组类和组件 (MyBatis

  • VO(View Object)

    • 视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来
    • 简单理解为页面展示用的数据
  • DTO(Data Transfer Object)
    • 数据传输对象,Service或Manager向外传输的对象
    • 泛指用于展示层与服务层之间的数据传输对象 (无需展示完整PO字段的部分字段层)
  • BO(Business Object)
    • 业务对象,由Service层内封装的临时业务逻辑的对象。
    • 一个BO对象可以包括多个PO对象 (一个业务可能需要多个PO对象进行多表处理)
    • BO是一个业务对象,一类业务就会对应一个BO,数量上没有限制,而且BO会有很多业务操作,也就是说除了get,set方法以外,BO会有很多针对自身数据进行计算的方法
  • POJO(Plain Ordinary Java Object)
    • 专指只有setter/getter/toString的简单类,包括DO/DTO/BO/VO等
    • POJO是DO/DTO/BO/VO的统称

img

:thought_balloon: 各层命名规范

[强制] 类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:DO / BO /DTO / VO / AO 正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion

[参考]

  • Service/DAO层方法命名规约
    1. 获取单个对象的方法用get做前缀。
    2. 获取多个对象的方法用list做前缀。
    3. 获取统计值的方法用count做前缀。
    4. 插入的方法用save/insert做前缀。
    5. 删除的方法用remove/delete做前缀。
    6. 修改的方法用update做前缀。
  • 领域模型命名规约
    1. 数据对象:xxxDO,xxx即为数据表名。
    2. 数据传输对象:xxxDTO,xxx为业务领域相关的名称。
    3. 展示对象:xxxVO,xxx一般为网页名称。
    4. POJO是DO/DTO/BO/VO的统称,禁止命名成xxxPOJO。

:thought_balloon: OOP规约

[强制] 定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值

1.2 Mode1时代

  • Model1中,主要分为两层,视图层和模型层

Model1时代

优点:架构简单,比较适合小型项目开发

缺点:JSP职责不单一,职责过重,不便于维护;

1.3 Model2时代

  • 项目分成三部分:包括视图、控制、模型

model2时代

  • 职责划分
    • Controller
      1. 取得表单数据
      2. 调用业务逻辑
      3. 转向指定的页面
    • Model
      1. 业务逻辑
      2. 保存数据的状态
    • View
      1. 显示页面

提高的代码的复用率与项目的扩展性,且大大降低了项目的维护成本

  • 常见的服务器端MVC框架有:Struts、Spring MVC、ASP.NET MVC、Zend Framework、JSF
  • 常见前端MVC框架:vue、angularjs、react、backbone
  • 由MVC演化出了另外一些模式如:MVP、MVVM 等等….

2. SpringMVC

2.1 简介

image-20230723121012676

Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架

  • 特点 - ==主流,用的人多==
    1. 轻量级,简单易学
    2. 高效 , 基于请求响应的MVC框架
    3. 与Spring兼容性好,无缝结合
    4. 约定优于配置
    5. 功能强大:RESTful、数据验证、格式化、本地化、主题等
    6. 简洁灵活

2.2 中心控制器(DispatcherServlet)

  • Spring的web框架围绕DispatcherServlet [ 调度Servlet ] 设计

    DispatcherServlet的作用是将请求分发到不同的处理器。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解形式进行开发,十分简洁

  • 流程:

    • 当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。

    流程

2.3 执行流程

执行流程

  1. DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求
    • 假设请求url为 http://localhost:8080/SpringMVC/hello
    • 拆分 - 请求位于服务器localhost:8080上的SpringMVC站点的hello控制器
  2. HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler
  3. HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello
  4. HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等
  5. HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler
  6. Handler让具体的Controller执行
  7. Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView
  8. HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet
  9. DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名
  10. 视图解析器将解析的逻辑视图名传给DispatcherServlet
  11. DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图
  12. 最终视图呈现给用户

2.4 使用(配置版)

2.4.1 web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="true">
<!--
配置DispatcherServlet
请求分发器
前端控制器
-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- DispatcherServlet绑定SpringMVC的配置文件 -->
<!-- 文件命名一般以 'id-function.xml' -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 启动级别 - 1 -->
<load-on-startup>1</load-on-startup>
</servlet>

<!--
配置DispatcherServlet的映射信息
/: 拦截所有请求(不包括*.jsp)
/*: 拦截所有请求(包括*.jsp)
-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

2.4.2 springmvc-servlet.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- 处理器映射器: HandlerMapping -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 处理器适配器: HandlerAdapter -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!-- 视图解析器: ViewResolver -->
<!-- 后期使用模板引擎: Thymeleaf、FreeMarker、Velocity -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>

<!-- 所使用的的处理器映射器需要配置, 以后选用其他可以替代此步 -->
<bean id="/hello" class="com.bayyy.controller.HelloController"/>

</beans>

2.4.3 Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 注意此处继承的是接口的包,不要误选注解的jar包
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = new ModelAndView();
// 业务代码
String result = "HelloSpringMVC";
mv.addObject("msg", result);

// 视图跳转
mv.setViewName("test"); // /WEB-INF/jsp/test.jsp

return mv;
}
}

2.5 使用(注解版)

2.5.1 web.xml

:key: 基本不需要修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--
配置DispatcherServlet
请求分发器
前端控制器
-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- DispatcherServlet绑定SpringMVC的配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 启动级别 - 1 -->
<load-on-startup>1</load-on-startup>
</servlet>

<!--
配置DispatcherServlet的映射信息
/: 拦截所有请求(不包括*.jsp)
/*: 拦截所有请求(包括*.jsp)
-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

2.5.2 springmvc-servlet.xml

为了支持基于注解的IOC,设置了自动扫描包的功能

  • 让IOC的注解生效
  • 静态资源过滤 :HTML . JS . CSS . 图片 , 视频 …..
  • MVC的注解驱动
  • 配置视图解析器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!-- 自动扫描包 -->
<context:component-scan base-package="com.bayyy.controller"/>

<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler/>

<!--
支持mvc注解驱动
在SpringMVC中一般使用@RequestMapping注解来完成映射关系
要想使@RequestMapping注解生效
必须向上下文中注册DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter实例
但是在SpringMVC的DispatcherServlet中已经配置了以上两个bean
所以我们只需要在Spring的配置文件中开启MVC的注解驱动即可
-->
<mvc:annotation-driven/>

<!-- 视图解析器: ViewResolver -->
<!-- 后期使用模板引擎: Thymeleaf、FreeMarker、Velocity -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>


</beans>

2.5.3 Controller实现

  • @Controller是为了让Spring IOC容器初始化时自动扫描到;
  • @RequestMapping是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/hello/h1;
  • 方法中声明Model类型的参数是为了把Action中的数据带到视图中;
  • 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/hello.jsp。
1
2
3
4
5
6
7
8
9
10
11
12
@Controller // 代表这个类会被Spring接管,被这个注解的类中的所有方法,如果返回值是String,并且有具体页面可以跳转,那么就会被视图解析器解析
@RequestMapping("/hello") // 代表所有的方法请求地址都是在/hello下,比如/hello/h1
public class HelloController {
@RequestMapping("/h1") // 请求地址: 会在/hello下拼接/h1
public String hello(Model model){
// 封装数据
model.addAttribute("msg", "HelloSpringMVCAnnotation");
System.out.println("hello");
// return的是视图名
return "hello"; // 会被视图解析器处理,跳转到/WEB-INF/jsp/hello.jsp
}
}

2.5.4 视图层

在WEB-INF/ jsp目录中创建hello.jsp,视图可以直接取出并展示从Controller带回的信息;

可以通过EL表示取出Model中存放的值,或者对象

  • 测试结果:image-20230723145204564

2.6 步骤总结

处理器映射器、处理器适配器、视图解析器

通常,我们只需要手动配置视图解析器,而处理器映射器处理器适配器只需要开启注解驱动即可,而省去了大段的xml配置

3. RestFul和控制器

3.1 控制器Controller

  • 控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义两种方法实现。
  • 控制器负责解析用户的请求并将其转换为一个模型。
  • 在Spring MVC中一个控制器类可以包含多个方法
  • 在Spring MVC中,对于Controller的配置方式有很多种

3.2 实现Controller接口

  • web.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">
    <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvc-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>
    </web-app>
  • springmvc-servlet.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.bayyy.controller"/>
    <mvc:default-servlet-handler/>
    <mvc:annotation-driven/>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
    <!-- 前缀 -->
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <!-- 后缀 -->
    <property name="suffix" value=".jsp"/>
    </bean>

    <bean id="/test" class="com.bayyy.controller.ControllerTest1" />

    </beans>
  • Controller

    实现 Controller 接口的类获得控制器功能

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class ControllerTest1 implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
    ModelAndView mv = new ModelAndView();
    mv.addObject("msg","ControllerTest1");
    mv.setViewName("test");
    return mv;
    }
    }

3.3 使用注解 @Controller

  • web.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">
    <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvc-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>
    </web-app>
  • springmvc-servlet.xml

    Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明组件扫描。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.bayyy.controller"/>
    <mvc:default-servlet-handler/>
    <mvc:annotation-driven/>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
    <!-- 前缀 -->
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <!-- 后缀 -->
    <property name="suffix" value=".jsp"/>
    </bean>

    <!-- <bean id="/test" class="com.bayyy.controller.ControllerTest1" />-->

    </beans>
  • Controller

    @Controller注解类型用于声明Spring类的实例是一个控制器

    1
    2
    3
    4
    5
    6
    7
    8
    @Controller
    public class ControllerTest2 {
    @RequestMapping("/t2") // 从哪来
    public String test1(Model model) {
    model.addAttribute("msg","ControllerTest2");
    return "test"; // 到哪去
    }
    }
  • 测试结果:image-20230723154047910

3.4 @RequestMapping

  • @RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径
1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping("/c3") // 以下所有的方法都在/c3下
public class ControllerTest3 {
@RequestMapping("/t1") // 访问路径为/c3/t1
public String test1() {
System.out.println("ControllerTest3");
return "test";
}
}

3.5 RestFul风格

  • 简介

    • Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制

      1
      2
      http://127.0.0.1/item/name=bayyy
      http://127.0.0.1/item/name/bayyy
  • 功能

    • 资源:互联网所有的事物都可以被抽象为资源
    • 资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。
    • 分别对应 添加、 删除、修改、查询。
  • 传统方式操作是通过不同参数实现

    1
    2
    3
    4
    http://127.0.0.1/item/queryItem.action?id=1 查询,GET
    http://127.0.0.1/item/saveItem.action 新增,POST
    http://127.0.0.1/item/updateItem.action 更新,POST
    http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST
  • RestFul风格以不同的请求方式实现,请求地址一样但是功能可以不同

    1
    2
    3
    4
    http://127.0.0.1/item/1 查询,GET
    http://127.0.0.1/item 新增,POST
    http://127.0.0.1/item 更新,PUT
    http://127.0.0.1/item/1 删除,DELETE
  • 优点

    • 简洁、高效、安全

3.5.1 传统风格

1
2
3
4
5
6
7
8
9
@Controller
public class RestFulController {
@RequestMapping("/add")
public String test1(int a, int b, Model model) {
int res = a + b;
model.addAttribute("msg","结果为" + res);
return "test";
}
}
  • 测试结果:image-20230723155627365

3.5.2 RestFul风格

1
2
3
4
5
6
7
8
9
10
11
12
@Controller
public class RestFulController {
// 原来的:http://localhost:8080/add?a=1&b=2
// RestFul:http://localhost:8080/add/1/2
// @PathVariable 路径变量: 获得路径中的值
@RequestMapping("/add/{a}/{b}")
public String test1(int a, int b, Model model) {
int res = a + b;
model.addAttribute("msg","结果为" + res);
return "test";
}
}
  • 测试结果:image-20230723155920325

  • 修改参数类型:

    • ```java
      @Controller
      public class RestFulController {
      // RestFul:http://localhost:8080/add/2/%22bayyy%22
      @RequestMapping("/add/{a}/{b}")
      public String test1(@PathVariable int a, @PathVariable String b, Model model) {
          String res = a + b;
          model.addAttribute("msg","a=" + a + " b=" + b +" 结果为" + res);
          return "test";
      }
      
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22

      - 测试结果:![image-20230723160057278](https://s2.loli.net/2023/07/23/m893ONtEuBGZAVd.png)

      ### 3.5.3 使用method属性指定请求类型

      > 用于约束请求的类型,可以收窄请求范围。指定请求谓词的类型如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE等
      >
      > :pushpin: 地址栏输入默认为 ==HTTP GET== 类型

      ```java
      @Controller
      public class RestFulController {
      // 原来的:http://localhost:8080/add?a=1&b=2
      // RestFul:http://localhost:8080/add/1/2
      // @PathVariable 路径变量: 获得路径中的值
      @RequestMapping(value = "/add/{a}/{b}", method = RequestMethod.GET)
      public String test1(@PathVariable int a, @PathVariable String b, Model model) {
      String res = a + b;
      model.addAttribute("msg","a=" + a + " b=" + b +" 结果为" + res);
      return "test";
      }
      }
  • 方法级别的注释变体 - 组合注解

    • @GetMapping
      • 等效于 @RequestMapping(method =RequestMethod.GET)
    • @PostMapping
    • @PutMapping
    • @DeleteMapping
    • @PatchMapping

4. 数据处理和跳转

4.1 结果跳转方式

设置ModelAndView对象 , 根据view的名称 , 和视图解析器跳到指定的页面 .

页面 : {视图解析器前缀} + viewName +{视图解析器后缀}

1
2
3
4
5
6
7
8
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
1
2
3
4
5
6
7
8
9
10
public class ControllerTest1 implements Controller {

public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg","ControllerTest1");
mv.setViewName("test");
return mv;
}
}
  • 跳转到 /WEB-INF/jsp/test.jsp

4.1.1 ServletAPI

  1. 通过HttpServletResponse进行输出
  2. 通过HttpServletResponse实现重定向
  3. 通过HttpServletResponse实现转发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Controller
public class ResultGo {

@RequestMapping("/result/t1")
public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
rsp.getWriter().println("Hello,Spring BY servlet API");
}

@RequestMapping("/result/t2")
public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
// 重定向
rsp.sendRedirect("/index.jsp");
}

@RequestMapping("/result/t3")
public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
//转发
req.setAttribute("msg","/result/t3");
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
}
}

4.1.2 SpringMVC - 无视图解析器

通过SpringMVC来实现转发和重定向 - 无需视图解析器;

  • ```xml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

    ```java
    @Controller
    public class ResultGoSpringMVC {
    @RequestMapping("/r2/t1")
    public String test1(Model model) throws Exception {
    model.addAttribute("msg", "/WEB-INF/jsp/test.jsp");
    // 转发到视图
    return "/WEB-INF/jsp/test.jsp";
    }

    @RequestMapping("/r2/t2")
    public String test3(Model model) throws Exception {
    model.addAttribute("msg", "forward:/test.jsp");
    // 转发到视图
    return "forward:/WEB-INF/jsp/test.jsp";
    }

    @RequestMapping("/r2/t3")
    public String test2(Model model) throws Exception {
    // model.addAttribute("msg", "redirect:/test.jsp");
    // 重定向到视图
    return "redirect:/WEB-INF/jsp/test.jsp";
    }
    }

4.1.3 SpringMVC - 有视图解析器

默认为转发,加上 redirect: 为重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Controller
public class ResultSpringMVC2 {
@RequestMapping("/rsm2/t1")
public String test1(){
//转发
return "test";
}

@RequestMapping("/rsm2/t2")
public String test2(){
//重定向
return "redirect:/index.jsp";
//return "redirect:hello.do"; //hello.do为另一个请求/
}
}

4.2 数据处理

4.2.1 处理提交数据

1)提交的域名和处理方法的参数一致

提交数据 :localhost:8080/u/t1?name=bayyy

处理方法:

1
2
3
4
5
6
7
8
9
10
// localhost:8080/u/t1?name=bayyy
@GetMapping("/t1")
public String test1(String name, Model model) {
// 1. 接收前端参数
System.out.println("接收到前端的参数为:" + name);
// 2. 将返回的结果传递给前端
model.addAttribute("msg", name);
// 3. 视图跳转
return "test";
}

image-20230723164622039

2)提交的域名和处理方法的参数不一致

提交数据 :localhost:8080/u/t2?username=bayyy

处理方法:

1
2
3
4
5
6
7
8
9
10
11
// localhost:8080/u/t2?username=bayyy
// @RequestParam("username") String name: 表示前端传递过来的参数username,赋值给后端的变量name
@GetMapping("/t2")
public String test2(@RequestParam("username") String name, Model model) {
// 1. 接收前端参数
System.out.println("接收到前端的参数为:" + name);
// 2. 将返回的结果传递给前端
model.addAttribute("msg", name);
// 3. 视图跳转
return "test";
}

image-20230723165000112

3)提交一个对象

提交的表单域和对象的属性名一致 , 参数使用对象即可

:warning: 传递参数名和对象名必须一致

提交数据 : localhost:8080/u/t3?id=1&name=bayyy&age=18

处理方法:

1
2
3
4
5
6
7
8
9
10
11
// localhost:8080/u/t3?id=1&name=bayyy&age=18
// 前端接收的是一个对象:User{id,name,age}
@GetMapping("/t3")
public String test3(User user, Model model) {
// 1. 接收前端参数
System.out.println("接收到前端的参数为:" + user);
// 2. 将返回的结果传递给前端
model.addAttribute("msg", user);
// 3. 视图跳转
return "test";
}

image-20230723165024385

  • 但是如果属性名不一致:image-20230723165253290

4.3 数据显示到前端

4.3.1 ModelAndView

实现接口使用

1
2
3
4
5
6
7
8
9
10
public class ControllerTest1 implements Controller {

public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg","ControllerTest1");
mv.setViewName("test");
return mv;
}
}

4.3.2 ModelMap

1
2
3
4
5
6
7
8
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap model){
// 封装要显示到视图中的数据
// 相当于req.setAttribute("name",name);
model.addAttribute("name",name);
System.out.println(name);
return "hello";
}

4.3.3 Model

精简的 ModelMap

1
2
3
4
5
6
7
8
@RequestMapping("/ct2/hello")
public String hello(@RequestParam("username") String name, Model model){
// 封装要显示到视图中的数据
// 相当于req.setAttribute("name",name);
model.addAttribute("msg",name);
System.out.println(name);
return "test";
}

4.4 乱码问题

4.4.1 SpringMVC过滤器

1
2
3
4
5
6
7
8
9
10
11
12
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

个别情况下对get的支持不好

4.4.2 更通用的方法

1)修改Tomcat配置文件:设置编码

1
2
3
4
5
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
maxParameterCount="1000"
/>

image-20230723170619083

2)自定义过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package com.bayyy.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;

/**
* 解决get和post请求 全部乱码的过滤器
*/
public class GenericEncodingFilter implements Filter {

@Override
public void destroy() {
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//处理response的字符编码
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");

// 转型为与协议相关对象
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 对request包装增强
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {
}

}

//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {

private HttpServletRequest request;
//是否编码的标记
private boolean hasEncode;
//定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
public MyRequest(HttpServletRequest request) {
super(request);// super必须写
this.request = request;
}

// 对需要增强方法 进行覆盖
@Override
public Map getParameterMap() {
// 先获得请求方式
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
// post请求
try {
// 处理post乱码
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
// get请求
Map<String, String[]> parameterMap = request.getParameterMap();
if (!hasEncode) { // 确保get手动编码逻辑只运行一次
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
// 处理get乱码
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}

//取一个值
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
return values[0]; // 取回参数的第一个值
}

//取所有值
@Override
public String[] getParameterValues(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}

3)xml注册

1
2
3
4
5
6
7
8
<filter>
<filter-name>encoding</filter-name>
<filter-class>com.bayyy.filter.GenericEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

5. JSON交互处理

5.1 JSON简介

  • JSON:JavaScript Object Notation, JS 对象标记,是一种轻量级的数据交换格式,采用完全独立于编程语言的文本格式来存储和表示数据

  • 在 JavaScript 语言中,一切都是对象

  • 因此,任何JavaScript 支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。

  • 语法格式:

    • 对象表示为键值对,数据由逗号分隔
    • 花括号保存对象
    • 方括号保存数组
    1
    2
    3
    {"name": "bayyy"}
    {"age": "18"}
    {"sex": "男"}
  • 优点:易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率

  • JSON 是 JavaScript 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串

5.2 JSON和JS互转

1
2
3
4
5
6
7
8
9
10
11
12
// 编写一个JavaScript对象, ES6语法
var user = {
name: "bayyy",
age: 18,
sex: "男"
}
// 将JavaScript对象转换为JSON字符串
var jsonStr = JSON.stringify(user);
console.log(jsonStr);
// 将JSON字符串转换为JavaScript对象
var jsonObj = JSON.parse(jsonStr);
console.log(jsonObj);

image-20230723180048681

5.3 Controller返回JSON对象

5.3.1 基本使用 ObjectMapper

  • 导包

    我们这里使用Jackson,使用它需要导入它的jar包;

    当然工具不止这一个,比如还有阿里巴巴的 fastjson 等等

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
  • 配置web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!--1.注册servlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>

<!--所有请求都会被springmvc拦截 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

</web-app>
  • 配置springmvc-servlet.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 -->
<context:component-scan base-package="com.bayyy.controller"/>
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>

<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>

</beans>
  • Controller

    produces 解决json乱码问题(指定响应体返回类型和编码)

    @ResponseBody 使返回值不经过视图解析器,直接返回字符串

    ObjectMapper jackson所带的json处理类

1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
public class UserController {
@RequestMapping(value="/j1", produces = "application/json;charset=utf-8")
@ResponseBody // 不会走视图解析器,会直接返回一个字符串
public String json1(Model model) throws JsonProcessingException {
// jackson, ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
// 创建一个对象
User user = new User("Bayyy", 18, "男");
String s = objectMapper.writeValueAsString(user);
return s;
}
}
  • 测试结果:image-20230723203125276

5.3.2 乱码解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- JSON 乱码解决 -->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

5.3.3 @RestController 统一返回JSON

在类上直接使用 @RestController ,所有的方法都只会返回 json 字符串

:key: 前后端分离开发中,一般都使用 @RestController

1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController // 代表这个类的所有方法返回的数据直接写给浏览器,如果是对象转为json数据
public class UserController {
@RequestMapping("/j1")
// @ResponseBody // 不会走视图解析器,会直接返回一个字符串
public String json1(Model model) throws JsonProcessingException {
// jackson, ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
// 创建一个对象
User user = new User("Bayyy", 18, "男");
String s = objectMapper.writeValueAsString(user);
return s;
}
}

5.3.4 集合输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RequestMapping("/j2")
public String json2() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
List<User> userList = new ArrayList<User>();
User user1 = new User("Bayyy1", 18, "男");
User user2 = new User("Bayyy2", 18, "男");
User user3 = new User("Bayyy3", 18, "男");
User user4 = new User("Bayyy4", 18, "男");
userList.add(user1);
userList.add(user2);
userList.add(user3);
userList.add(user4);
String s = mapper.writeValueAsString(userList);
return s;
}
  • 测试结果:image-20230723203641686

5.3.5 时间输出

1)基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RequestMapping("/j3")
public String json3() throws JsonProcessingException {
// 时间戳
return new ObjectMapper().writeValueAsString(new Date());
}

@RequestMapping("/j3")
public String json3() throws JsonProcessingException {
Date date = new Date();
// 时间格式转换 - SimpleDateFormat
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return new ObjectMapper().writeValueAsString(sdf.format(date));
}

@RequestMapping("/j3")
public String json3() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
// 时间格式转换 - mapper.configure
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
mapper.setDateFormat(sdf);
Date date = new Date();
return mapper.writeValueAsString(date);
}
  • 测试结果:image-20230723203846895

image-20230723204026224

2)抽象工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class JsonUtils {
public static String getJson(Object object) {
return getJson(object,"yyyy-MM-dd HH:mm:ss");
}

public static String getJson(Object object,String dateFormat) {
ObjectMapper mapper = new ObjectMapper();
//不使用时间差的方式
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//自定义日期格式对象
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
//指定日期格式
mapper.setDateFormat(sdf);
try {
return mapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
}
  • 使用

    1
    2
    3
    4
    5
    6
    7
    @RequestMapping("/j4")
    public String json4() throws JsonProcessingException {
    Date date = new Date();
    String json = JsonUtils.getJson(date);
    // String json = JsonUtils.getJson(date, "yyyy-MM-dd HH:mm:ss");
    return json;
    }

5.4 FastJson

fastjson.jar是阿里开发的一款专门用于Java开发的包,可以方便的实现json对象与JavaBean对象的转换,实现JavaBean对象与json字符串的转换,实现json对象与json字符串的转换。实现json的转换方法很多,最后的实现结果都是一样的。

  • 三个主要的类
    • JSONObject 代表 json 对象
      • JSONObject实现了Map接口, 猜想 JSONObject底层操作是由Map实现的。
      • JSONObject对应json对象,通过各种形式的get()方法可以获取json对象中的数据,也可利用诸如size(),isEmpty()等方法获取”键:值”对的个数和判断是否为空。其本质是通过实现Map接口并调用接口中的方法完成的。
    • JSONArray 代表 json 对象数组
      • 内部是有List接口中的方法来完成操作的。
    • JSON代表 JSONObject和JSONArray的转化
      • JSON类源码分析与使用
      • 仔细观察这些方法,主要是实现json对象,json对象数组,javabean对象,json字符串之间的相互转化。

5.4.1 导包

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.37</version>
</dependency>

5.4.2 测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RequestMapping("/j5")
public String json5() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
List<User> userList = new ArrayList<User>();
User user1 = new User("Bayyy1", 18, "男");
User user2 = new User("Bayyy2", 18, "男");
User user3 = new User("Bayyy3", 18, "男");
User user4 = new User("Bayyy4", 18, "男");
userList.add(user1);
userList.add(user2);
userList.add(user3);
userList.add(user4);
String s = JSON.toJSONString(userList);
return s;
}

5.4.3 更多使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
System.out.println("*******Java对象 转 JSON字符串*******");
String str1 = JSON.toJSONString(userList);
System.out.println("JSON.toJSONString(list)==>"+str1);
String str2 = JSON.toJSONString(user1);
System.out.println("JSON.toJSONString(user1)==>"+str2);

System.out.println("\n****** JSON字符串 转 Java对象*******");
User jp_user1=JSON.parseObject(str2,User.class);
System.out.println("JSON.parseObject(str2,User.class)==>"+jp_user1);

System.out.println("\n****** Java对象 转 JSON对象 ******");
JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2);
System.out.println("(JSONObject) JSON.toJSON(user2)==>"+jsonObject1.getString("name"));

System.out.println("\n****** JSON对象 转 Java对象 ******");
User to_java_user = JSON.toJavaObject(jsonObject1, User.class);
System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>"+to_java_user);
  • Java对象 转 JSON字符串
    • String str = JSON.toJSONString(userList);
  • JSON字符串 转 Java对象
    • User user = JSON.parseObject(str, User.class);
  • Java对象 转 JSON对象
    • JSONObject jsonObject = (JSONObject) JSON.toJSON(user)
  • JSON对象 转 Java对象
    • User user = JSON.toJavaObject(jsonObject, User.class);

6. SSM整合

此时的环境配置

  • JAVA 1.8.0_361
  • mysql 5.7.19
  • Tomcat 9.0.76
  • Maven 3.9.2

6.1 数据库搭建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CREATE DATABASE `ssmbuild`;

USE `ssmbuild`;

DROP TABLE IF EXISTS `books`;

CREATE TABLE `books` (
`bookID` INT(10) NOT NULL AUTO_INCREMENT COMMENT '书id',
`bookName` VARCHAR(100) NOT NULL COMMENT '书名',
`bookCounts` INT(11) NOT NULL COMMENT '数量',
`detail` VARCHAR(200) NOT NULL COMMENT '描述',
KEY `bookID` (`bookID`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO `books`(`bookID`,`bookName`,`bookCounts`,`detail`)VALUES
(1,'Java',1,'从入门到放弃'),
(2,'MySQL',10,'从删库到跑路'),
(3,'Linux',5,'从进门到进牢');

6.2 基本环境搭建

6.2.1 Maven项目

6.2.2 pom依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<dependencies>
<!-- Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>

<!-- servlet 依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!-- JSP 依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<!-- JSTL 依赖 -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!-- JSTL 实现 -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>

<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<!-- 数据库连接池 C3P0 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>

<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<!-- MyBatis-Spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>

<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.24</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.24</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.19</version>
</dependency>

<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
</dependencies>


<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
<finalName>ssmbuild</finalName>
</build>

6.2.3 结构和配置架构

  • pojo

  • dao

  • service

  • controller

  • mybatis-config.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "https://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    <settings>
    <!-- <setting name="logImpl" value="STDOUT_LOGGING"/>-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <typeAliases>
    <package name="com.bayyy.pojo"/>
    </typeAliases>
    </configuration>
  • applicationContext.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 指定要扫描的包,这个包下的注解都会生效 -->
    <context:component-scan base-package="com.bayyy"/>
    <context:annotation-config/>

    <!-- 开启注解支持: 会自动扫描注解 -->
    <aop:aspectj-autoproxy/>

    <import resource="spring-dao.xml"/>

    <!-- 注册bean -->

    </beans>

6.2.4 MyBatis层编写

  1. db.properties

    1
    2
    3
    4
    driver=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username=root
    password=123456
  2. mybatis-config.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "https://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    <settings>
    <!-- <setting name="logImpl" value="STDOUT_LOGGING"/>-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <typeAliases>
    <package name="com.bayyy.pojo"/>
    </typeAliases>

    <mappers>
    <mapper class="com.bayyy.dao.BookMapper"/>
    </mappers>
    </configuration>
  3. POJO

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Books {
    private int bookID;
    private String bookName;
    private int bookCounts;
    private String detail;
    }
  4. Mapper接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public interface BookMapper {
    // 增加一本书
    int addBook(Books books);

    // 删除一本书
    int deleteBookByID(@Param("bookID") int id);

    // 更新一本书
    int updateBook(Books books);

    // 查询一本书
    Books queryBookByID(@Param("bookID") int id);

    // 查询全部的书
    List<Books> queryAllBook();
    }
  5. Mapper.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <!--namespace->绑定一个对应的Dao/Mapper接口-->
    <mapper namespace="com.bayyy.dao.BookMapper">

    <insert id="addBook" parameterType="books">
    insert into books (bookName,bookCounts,detail)
    values(#{bookName},#{bookCounts},#{detail})
    </insert>

    <delete id="deleteBookById" parameterType="int">
    delete from books where bookID=#{bookID}
    </delete>

    <update id="updateBook" parameterType="books">
    update books set bookName=#{bookName},bookCounts=#{bookCounts},detail=#{detail}
    where bookID=#{bookID}
    </update>

    <select id="queryBookById" parameterType="int" resultType="books">
    select * from books where bookID=#{bookID}
    </select>

    <select id="queryAllBook" resultType="books">
    select * from books
    </select>

    </mapper>
  6. Service接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public interface BookService {
    // 增加一本书
    int addBook(Books books);

    // 删除一本书
    int deleteBookByID(int id);

    // 更新一本书
    int updateBook(Books books);

    // 查询一本书
    Books queryBookByID(int id);

    // 查询全部的书
    List<Books> queryAllBook();
    }

  7. Service实现类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class BookServiceImpl implements BookService{
    private BookMapper bookMapper;

    public void setBookMapper(BookMapper bookMapper) {
    this.bookMapper = bookMapper;
    }

    @Override
    public int addBook(Books books) {
    return this.bookMapper.addBook(books);
    }

    @Override
    public int deleteBookByID(int id) {
    return this.bookMapper.deleteBookByID(id);
    }

    @Override
    public int updateBook(Books books) {
    return this.bookMapper.updateBook(books);
    }

    @Override
    public Books queryBookByID(int id) {
    return this.bookMapper.queryBookByID(id);
    }

    @Override
    public List<Books> queryAllBook() {
    return this.bookMapper.queryAllBook();
    }
    }

6.2.5 Spring层 配置

  1. spring-dao.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 1. 关联数据库配置文件 -->
    <context:property-placeholder location="classpath:db.properties"/>

    <!-- 2. 连接池
    dbcp: 半自动化操作, 不能自动连接
    c3p0: 自动化操作, 自动连接
    druid: 自动化操作, 自动连接, 监控功能强
    hikari: 自动化操作, 自动连接, 性能最好, springboot默认使用 -->
    <bean id="datasource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!-- 配置连接池属性 -->
    <property name="driverClass" value="${jdbc.driver}"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>
    <property name="user" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>

    <!-- c3p0连接池的私有属性 -->
    <property name="maxPoolSize" value="30"/>
    <property name="minPoolSize" value="10"/>
    <!-- 关闭连接后不自动commit -->
    <property name="autoCommitOnClose" value="false"/>
    <!-- 获取连接超时时间 -->
    <property name="checkoutTimeout" value="10000"/>
    <!-- 当获取连接失败重试次数 -->
    <property name="acquireRetryAttempts" value="2"/>
    </bean>


    <!-- 3. sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="datasource"/>
    <!-- 指定mybatis全局配置文件的位置 -->
    <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!-- 配置dao接口扫描包, 动态实现dao接口, 注入到spring容器中 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!-- 注入 sqlSessionFactory -->
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    <!-- 给出需要扫描的dao接口包 -->
    <property name="basePackage" value="com.bayyy.dao"/>
    </bean>
    </beans>
  2. spring-service.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 1. 扫描service下的包 -->
    <context:component-scan base-package="com.bayyy.service"/>

    <!-- 2. 将业务层和dao层的bean注入到spring容器中 -->
    <bean id="BookServiceImpl" class="com.bayyy.service.BookServiceImpl">
    <property name="bookMapper" ref="bookMapper"/>
    </bean>

    <!-- 3. 声明式事务配置 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据库连接池 -->
    <property name="dataSource" ref="datasource"/>
    </bean>

    <!-- 4. aop事务配置 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!-- 给哪些方法配置事务 -->
    <tx:attributes>
    <tx:method name="add*" propagation="REQUIRED"/>
    <tx:method name="delete*" propagation="REQUIRED"/>
    <tx:method name="update*" propagation="REQUIRED"/>
    <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
    <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
    </tx:attributes>
    </tx:advice>
    </beans>

6.2.6 SpringMVC层 配置

  1. web.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">

    <!--1. DispatcherServlet-->
    <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 2. 配置字符编码过滤器 -->
    <filter>
    <filter-name>encoding</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
    <param-name>encoding</param-name>
    <param-value>utf-8</param-value>
    </init-param>
    </filter>
    <filter-mapping>
    <filter-name>encoding</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 3. Session自动过期时间 -->
    <session-config>
    <session-timeout>30</session-timeout>
    </session-config>
    </web-app>
  2. spring-mvc.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    https://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/mvc
    https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 1. 注解驱动 -->
    <mvc:annotation-driven/>
    <!-- 2. 静态资源过滤 -->
    <mvc:default-servlet-handler/>
    <!-- 3. 扫描包 -->
    <context:component-scan base-package="com.bayyy.controller"/>
    <!-- 4. 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
    id="internalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
    </bean>

    </beans>
  3. applicationContext.xml整合

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">

    <import resource="classpath: spring-dao.xml"/>
    <import resource="classpath: spring-service.xml"/>
    <import resource="classpath:spring-mvc.xml"/>

    </beans>

6.2.7 Controller和视图层

1)BookController-allBook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Controller
@RequestMapping("/book")
public class BookController {
// controller 调用 service 层
@Autowired
@Qualifier("bookServiceImpl")
private BookService bookService;


// 查询全部的书籍,并且返回到一个书籍展示页面
@RequestMapping("/allBook")
public String list(Model model) {
List<Books> books = bookService.queryAllBook();
model.addAttribute("list", books);
return "allBook";
}
}

2)index.jsp 及美化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE HTML>
<html>
<head>
<title>首页</title>
<style type="text/css">
a {
text-decoration: none;
color: black;
font-size: 18px;
}
h3 {
width: 180px;
height: 38px;
margin: 100px auto;
text-align: center;
line-height: 38px;
background: deepskyblue;
border-radius: 4px;
}
</style>
</head>
<body>

<h3>
<a href="${pageContext.request.contextPath}/book/allBook">点击进入列表页</a>
</h3>
</body>
</html>

3)allBook.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>书籍列表</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 引入 Bootstrap -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<div class="container">

<div class="row clearfix">
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>书籍列表 —— 显示所有书籍</small>
</h1>
</div>
</div>
</div>

<div class="row">
<div class="col-md-4 column">
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增</a>
</div>
</div>

<div class="row clearfix">
<div class="col-md-12 column">
<table class="table table-hover table-striped">
<thead>
<tr>
<th>书籍编号</th>
<th>书籍名字</th>
<th>书籍数量</th>
<th>书籍详情</th>
<th>操作</th>
</tr>
</thead>
<%-- 书籍从数据库中获取, 从list中遍历: forEach --%>
<tbody>
<c:forEach var="book" items="${list}">
<tr>
<td>${book.bookID}</td>
<td>${book.bookName}</td>
<td>${book.bookCounts}</td>
<td>${book.detail}</td>
<td>
<a href="${pageContext.request.contextPath}/book/toUpdateBook?id=${book.bookID}">更改</a>
|
<a href="${pageContext.request.contextPath}/book/del/${book.bookID}">删除</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>

4)BookController-add/del

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 跳转到增加书籍页面
@RequestMapping("/toAddBook")
public String toAddBook() {
return "toAddBook";
}

// 添加书籍的请求
@RequestMapping("/addBook")
public String addBook(Books books) {
bookService.addBook(books);
return "redirect:/book/allBook";
}

// 跳转到修改书籍页面
@RequestMapping("/toUpdateBook/{bookId}")
public String toUpdateBook(@PathVariable("bookId") int bookId, Model model) {
Books book = bookService.queryBookByID(bookId);
System.out.println(book);
model.addAttribute("book", book);
return "toUpdateBook";
}

// 修改书籍
@RequestMapping("/updateBook")
public String updateBook(Books books) {
bookService.updateBook(books);
return "redirect:/book/allBook";
}

// 删除书籍
@RequestMapping("/del/{bookId}")
public String deleteBookById(@PathVariable("bookId") int bookId) {
bookService.deleteBookByID(bookId);
return "redirect:/book/allBook";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>
<head>
<title>新增书籍</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 引入 Bootstrap -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">

<div class="row clearfix">
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>新增书籍</small>
</h1>
</div>
</div>
</div>
<form action="${pageContext.request.contextPath}/book/addBook" method="post">
<div class="form-group">
<label for="bookName">书籍名称</label>
<input type="text" class="form-control" name="bookName" id="bookName" placeholder="请输入书籍名称" required>
</div>
<div class="form-group">
<label for="bookCounts">书籍数量</label>
<input type="text" class="form-control" name="bookCounts" id="bookCounts" placeholder="请输入书籍数量" required>
</div>
<div class="form-group">
<label for="detail">书籍描述</label>
<input type="text" class="form-control" name="detail" id="detail" placeholder="请输入书籍描述" required>
</div>
<input type="submit" value="添加">
</form>

</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>修改信息</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 引入 Bootstrap -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">

<div class="row clearfix">
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>修改信息</small>
</h1>
</div>
</div>
</div>

<form action="${pageContext.request.contextPath}/book/updateBook" method="post">
<input type="hidden" name="bookID" value="${book.bookID}"/>
书籍名称:<input type="text" name="bookName" value="${book.bookName}"/>
书籍数量:<input type="text" name="bookCounts" value="${book.bookCounts}"/>
书籍详情:<input type="text" name="detail" value="${book.detail}"/>
<input type="submit" value="修改"/>
</form>

</div>

5)新增查询功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 查询书籍
@RequestMapping("/queryBook")
public String queryBookByName(@Param("queryBookName") String queryBookName, Model model) {
if (queryBookName.equals("")) {
model.addAttribute("error", "未查到");
return "allBook";
}
Books books = bookService.queryBookByName(queryBookName);
ArrayList<Books> list = new ArrayList<Books>();
list.add(books);
if (books == null) {
list = (ArrayList<Books>) bookService.queryAllBook();
model.addAttribute("error", "未查到");
}
model.addAttribute("list", list);
return "allBook";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div class="row">
<div class="col-md-4 column">
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增</a>
</div>
<div class="col-md-4 column">
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/allBook">显示所有书籍</a>
</div>
<div class="col-md-4 column">
<%--查询书籍--%>
<form action="${pageContext.request.contextPath}/book/queryBook" method="post" class="form-inline">
<span class="glyphicon glyphicon-pencil" aria-hidden="true" style="color: red;font-weight: bold">${error}</span>
<input type="text" name="queryBookName" class="form-control" placeholder="请输入要查询的书籍名称">
<input type="submit" value="查询" class="btn btn-primary">
</form>
</div>
</div>

7. Ajax

7.1 简介

  • AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
  • AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
  • Ajax 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的Web应用程序的技术。

在 2005 年,Google 通过其 Google Suggest 使 AJAX 变得流行起来。Google Suggest能够自动帮你完成搜索单词

Google Suggest 使用 AJAX 创造出动态性极强的 web 界面:当您在谷歌的搜索框输入关键字时,JavaScript 会把这些字符发送到服务器,然后服务器会返回一个搜索建议的列表。

  • 传统的网页(即不用ajax技术的网页),想要更新内容或者提交一个表单,都需要重新加载整个网页。
  • 使用ajax技术的网页,通过在后台服务器进行少量的数据交换,就可以实现异步局部更新。

使用Ajax,用户可以创建接近本地桌面应用的直接、高可用、更丰富、更动态的Web用户界面。

7.2 伪造Ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>iframe测试体验页面无刷新</title>

<script>
function go() {
const url = document.getElementById("url").value;
document.getElementById("iframe1").src=url;
}
</script>

</head>
<body>
<div>
<p>请输入地址</p>
<p>
<input type="text" id="url" value="https://www.bilibili.com/">
<input type="submit" value="提交" onclick="go()">
</p>
</div>
<div>
<iframe id="iframe1" style="width: 100%; height: 500px"></iframe>
</div>

</body>
</html>

7.3 jQuery.ajax

Ajax的核心和本质就是XMLHttpRequest对象(XHR)。XHR为向服务器发送请求和解析服务器响应提供了接口。能够以异步方式从服务器获取新数据

通过 jQuery AJAX 方法,能够使用 HTTP Get 和 HTTP Post 从远程服务器上请求文本、HTML、XML 或 JSON – 同时您能够把这些外部数据直接载入网页的被选元素中

7.3.1 语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
jQuery.ajax(...)
部分参数:
url:请求地址
type:请求方式,GET、POST(1.9.0之后用method)
headers:请求头
data:要发送的数据
contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8")
async:是否异步
timeout:设置请求超时时间(毫秒)
beforeSend:发送请求前执行的函数(全局)
complete:完成之后执行的回调函数(全局)
success:成功之后执行的回调函数(全局)
error:失败之后执行的回调函数(全局)
accepts:通过请求头发送给服务器,告诉服务器当前客户端可接受的数据类型
dataType:将服务器端返回的数据转换成指定类型
"xml": 将服务器端返回的内容转换成xml格式
"text": 将服务器端返回的内容转换成普通文本格式
"html": 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。
"script": 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式
"json": 将服务器端返回的内容转换成相应的JavaScript对象
"jsonp": JSONP 格式使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数

7.3.2 使用

1)配置

1
2
3
<context:component-scan base-package="com.bayyy.controller"/>
<mvc:default-servlet-handler />
<mvc:annotation-driven />

2)AjaxController

1
2
3
4
5
6
7
8
9
@RequestMapping("/a1")
public void a1(String name, HttpServletResponse response) throws IOException {
System.out.println("a1:param: " + name);
if ("bayyy".equals(name)) {
response.getWriter().print("true");
} else {
response.getWriter().print("false");
}
}

3)编写index.jsp测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="${pageContext.request.contextPath}/statics/js/jquery-3.7.0.js"></script>

<script>
function a() {
$.post({
url: "${pageContext.request.contextPath}/a1",
data: {"name": $("#username").val()},
success: function (data, status) {
alert("data=>"+data+" status=>"+status);
}
})
}
</script>

</head>
<body>

<%--失去焦点的时候,发起请求到后台--%>
用户名:<input type="text" id="username" onblur="a()">

</body>
</html>
  • 测试结果:image-20230726000724977

7.3.2 返回显示表单+html

1
2
3
4
5
6
7
8
@RequestMapping("/a2")
public List<User> a2() {
List<User> users = new ArrayList<User>();
users.add(new User("bayyy", 18, "男"));
users.add(new User("cdn", 18, "女"));
users.add(new User("bayyy", 18, "男"));
return users;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="${pageContext.request.contextPath}/statics/js/jquery-3.7.0.js"></script>
<script>
$(function () {
$("#btn").click(function () {
$.post("${pageContext.request.contextPath}/a2", function (data) {
var html = "";
for (let i = 0; i < data.length; i++) {
html += "<tr>" +
"<td>" + data[i].name + "</td>" +
"<td>" + data[i].age + "</td>" +
"<td>" + data[i].sex + "</td>" +
"</tr>"
}
$("#content").html(html);
})

})
});
</script>
</head>
<body>
<input type="button" value="加载数据" id="btn">
<table>
<tr>
<td>姓名</td>
<td>年龄</td>
<td>性别</td>
</tr>
<tbody id="content">

</tbody>
</table>

</body>
</html>
  • 测试结果:image-20230726000902792

7.3.3 注册提示效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="${pageContext.request.contextPath}/statics/js/jquery-3.7.0.js"></script>
<script>
function a1() {
$.post({
url:"${pageContext.request.contextPath}/a3",
data:{"name":$("#name").val()},
success:function (data) {
if (data.toString()==="ok") {
$("#userInfo").css("color","green");
} else {
$("#userInfo").css("color","red");
console.log(data);
}
$("#userInfo").html(data);
}
})
}
function a2() {
$.post({
url:"${pageContext.request.contextPath}/a3",
data:{"pwd":$("#pwd").val()},
success:function (data) {
if (data.toString()==="ok") {
$("#pwdInfo").css("color","green");
} else {
$("#pwdInfo").css("color","red");
}
$("#pwdInfo").html(data);
}
})
}
</script>
</head>
<body>
<p>
用户名:<input type="text" id="name" onblur="a1()">
<span id="userInfo"></span>
</p>
<p>
密码:<input type="text" id="pwd" onblur="a2()">
<span id="pwdInfo"></span>
</p>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RequestMapping("/a3")
public String a3(String name, String pwd) {
String msg = "";
if (name!=null) {
if ("admin".equals(name)) {
msg = "ok";
} else {
msg = "用户名有误";
}
}
if (pwd!=null) {
if ("123456".equals(pwd)) {
msg = "ok";
} else {
msg = "密码有误";
}
}
return msg;
}
  • 测试结果:image-20230726153904969

8. 拦截器

8.1 概述

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。

过滤器与拦截器的区别:拦截器是AOP思想的具体应用。

过滤器

  • servlet规范中的一部分,任何java web工程都可以使用
  • 在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截

拦截器

  • 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
  • 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的

8.2 自定义拦截器

实现 HandlerInterceptor 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MyInterceptor implements HandlerInterceptor {
// ture: 执行下一个拦截器,放行
// false: 不执行下一个拦截器,放行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("处理前");
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("处理后");
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("清理");
}
}

  • 配置(applicationContext.xml):

    1
    2
    3
    4
    5
    6
    7
    <mvc:interceptors>
    <mvc:interceptor>
    <!-- 拦截的路径(**代表所有路径) -->
    <mvc:mapping path="/**"/>
    <bean class="com.bayyy.config.MyInterceptor"/>
    </mvc:interceptor>
    </mvc:interceptors>
  • 测试结果:image-20230726155343362

8.3 用户登录的实现

1、有一个登陆页面,需要写一个controller访问页面。

2、登陆页面有一提交表单的动作。需要在controller中处理。判断用户名密码是否正确。如果正确,向session中写入用户信息。返回登陆成功。

3、拦截用户请求,判断用户是否登陆。如果用户已经登陆。放行, 如果用户未登陆,跳转到登陆页面

  • login.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>登录页面</h1>
<form action="${pageContext.request.contextPath}/user/login" method="get">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
  • main.jsp
1
2
3
4
5
6
7
8
9
10
11
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<span>${username}</span>
<a href="${pageContext.request.contextPath}/user/logout">注销</a>
</body>
</html>
  • index.jsp
1
2
3
4
5
6
7
8
9
10
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1><a href="${pageContext.request.contextPath}/user/goLogin">登录</a></h1>
<h1><a href="${pageContext.request.contextPath}/user/main">首页</a></h1>
</body>
</html>
  • Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Controller
@RequestMapping("/user")
public class LoginController {
@RequestMapping("/login")
public String login(@RequestParam("username") String name, @RequestParam("password") String pwd, HttpSession session) {
// 模拟登录, 将用户信息保存到session中
session.setAttribute("user", name);
System.out.println(session.getAttribute("user"));
return "main";
}

@RequestMapping("/goLogin")
public String goLogin(HttpSession session) {
return "login";
}

@RequestMapping("/logout")
public String logout(HttpSession session) {
session.removeAttribute("user");
return "login";
}

@RequestMapping("/main")
public String main() {
return "main";
}
}
  • LoginInterceptor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
// 放行
if (request.getRequestURI().contains("ogin")){
return true;
}
if (session.getAttribute("user") != null) {
return true;
}
// 判断什么情况下没有登录
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
}

9. 文件上传和下载

9.1 准备工作

9.1.1 前端

:key: enctype

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
  • text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。
  • method=post
  • enctype=multipart/form-data
1
2
3
4
<form action="" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit">
</form>

9.1.2 导包

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.5</version>
</dependency>

9.2 文件上传

9.2.1 前端页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>

<form action="${pageContext.request.contextPath}/upload2" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit">
</form>

</body>
</html>

9.2.2 Bean

:hotsprings: id 必须是 multipartResolver

1
2
3
4
5
6
7
8
<!--文件上传配置-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 上传文件大小上限,单位为字节(10485760=10M) -->
<property name="maxUploadSize" value="10485760"/>
<property name="maxInMemorySize" value="40960"/>
</bean>

9.2.3 两种文件上传的Controller实现方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象
//批量上传CommonsMultipartFile则为数组即可
@RequestMapping("/upload")
public String fileUpload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {

//获取文件名 : file.getOriginalFilename();
String uploadFileName = file.getOriginalFilename();

//如果文件名为空,直接回到首页!
if ("".equals(uploadFileName)) {
return "redirect:/index.jsp";
}
System.out.println("上传文件名 : " + uploadFileName);

//上传路径保存设置
String path = request.getServletContext().getRealPath("/upload");
//如果路径不存在,创建一个
File realPath = new File(path);
if (!realPath.exists()) {
realPath.mkdir();
}
System.out.println("上传文件保存地址:" + realPath);

InputStream is = file.getInputStream(); //文件输入流
OutputStream os = new FileOutputStream(new File(realPath, uploadFileName)); //文件输出流

//读取写出
int len = 0;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
os.flush();
}
os.close();
is.close();
return "redirect:/index.jsp";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
* 采用file.Transto 来保存上传的文件
*/
@RequestMapping("/upload")
public String fileUpload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {

//上传路径保存设置
String path = request.getServletContext().getRealPath("/upload");
File realPath = new File(path);
if (!realPath.exists()){
realPath.mkdir();
}
//上传文件地址
System.out.println("上传文件保存地址:"+realPath);

//通过CommonsMultipartFile的方法直接写文件(注意这个时候)
file.transferTo(new File(realPath +"/"+ file.getOriginalFilename()));

return "redirect:/index.jsp";
}

9.3 文件下载

1、设置 response 响应头

2、读取文件 — InputStream

3、写出文件 — OutputStream

4、执行操作

5、关闭流 (先开后关)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@RequestMapping(value="/download")
public String downloads(HttpServletResponse response ,HttpServletRequest request) throws Exception{
//要下载的图片地址
String path = request.getServletContext().getRealPath("/upload");
String fileName = ".jpg";

//1、设置response 响应头
response.reset(); //设置页面不缓存,清空buffer
response.setCharacterEncoding("UTF-8"); //字符编码
response.setContentType("multipart/form-data"); //二进制传输数据
//设置响应头
response.setHeader("Content-Disposition",
"attachment;fileName="+URLEncoder.encode(fileName, "UTF-8"));

File file = new File(path,fileName);
//2、 读取文件--输入流
InputStream input=new FileInputStream(file);
//3、 写出文件--输出流
OutputStream out = response.getOutputStream();

byte[] buff =new byte[1024];
int index=0;
//4、执行 写出操作
while((index= input.read(buff))!= -1){
out.write(buff, 0, index);
out.flush();
}
out.close();
input.close();
return null;
}