拿捏Java:SpringMVC

11 阅读10分钟

一、SpringMVC原理

MVC主要作用是降低了视图与业务逻辑间的双向耦合;

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

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

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

最典型的MVC就是JSP + servlet + javabean的模式

image.png

用户发请求
Servlet接收请求数据,并调用对应的业务逻辑方法
业务处理完毕,返回更新后的数据给servlet
servlet转向到JSP,由JSP来渲染页面
响应给前端更新后的页面

Controller:控制器

取得表单数据
调用业务逻辑
转向指定的页面

Model:模型

业务逻辑
保存数据的状态

View:视图

显示页面

原理:

image.png image.png 图为SpringMVC的一个较完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现。

执行流程

1、DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。

请求的url为 : http://localhost:8080/SpringMVC/hello

http://localhost:8080 服务器域名

SpringMVC部署在服务器上的web站点

hello表示控制器

2、HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。

3、HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。

4、HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。

5、HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。Handler让具体的Controller执行。

6、Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。

ModelAndView中包含以下信息:
Model: 这是一个Map对象,用于存储需要传递给View层的数据。在Controller中,可以向Model中添加各种类型的数据,如POJO对象、List、Map等,最终这些数据会被渲染到View层。
View: 指定需要渲染的视图,可以是一个字符串视图名称,也可以是一个实现了View接口的对象。常见的视图实现有JSP、Thymeleaf、Freemarker等。
Status: 用于指定HTTP响应状态码。例如,可以设置状态为HttpStatus.OK或HttpStatus.NOT_FOUND。
Redirect: 用于指定重定向的URL。

7、HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。

8、DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。

9、视图解析器将解析的逻辑视图名传给DispatcherServlet。

10、DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。

11、最终视图呈现给用户

代码编写

配置版

1、配置DispatcherServlet:这是SpringMVC的核心,请求分发器,前端控制器 DispatcherServlet要绑定Spring的配置文件

web.xml

image.png

springmvc-servlet.xml
SpringMVC的核心三要素:处理器映射器,处理器适配器,视图解析器 image.png

编写controller: image.png

注册Bean: image.png

但是上述方法不智能,现实开发中使用注解完成

注解版

image.png

1、编写web.xml

<?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>

</web-app>

2、编写springmvc-servlet.xml

<?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.kuang.controller"/>
   <!-- 让Spring MVC不处理静态资源 -->
   <mvc:default-servlet-handler />
   <!--
   支持mvc注解驱动
       在spring中一般采用@RequestMapping注解来完成映射关系
       要想使@RequestMapping注解生效
       必须向上下文中注册DefaultAnnotationHandlerMapping
       和一个AnnotationMethodHandlerAdapter实例
       这两个实例分别在类级别和方法级别处理。
       而annotation-driven配置帮助我们自动完成上述两个实例的注入。
    -->
   <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>

3、创建controller类

package com.kuang.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/HelloController")
public class HelloController {

   //真实访问地址 : 项目名/HelloController/hello
   @RequestMapping("/hello")
   public String sayHello(Model model){
       //向模型中添加属性msg与值,可以在JSP页面中取出并渲染
       model.addAttribute("msg","hello,SpringMVC");
       //web-inf/jsp/hello.jsp
       return "hello";
  }
}

使用springMVC必须配置的三大件:处理器映射器、处理器适配器、视图解析器

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

二、Controller和RestFul风格

Controller

负责解析用户的请求并把他转换成一个模型

注解:@Component      组件
     @Controller     controller层
     @Service        service层
     @Repository     dao层

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

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

编写Controller:

image.png

        //Spring MVC会自动实例化一个Model对象用于向视图中传值
       model.addAttribute("msg", "ControllerTest2");
       //返回视图位置
       return "test";

RequestMapping

@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。

RestFul风格

image.png

一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格

@Controller
public class RestFulController {
   //映射访问路径
   @RequestMapping("/commit/{p1}/{p2}")
   public String index(@PathVariable int p1, @PathVariable int p2, Model model){
       
       int result = p1+p2;
       //Spring MVC会自动实例化一个Model对象用于向视图中传值
       model.addAttribute("msg", "结果:"+result);
       //返回视图位置
       return "test";       
  }  
}

image.png @PathVariable会自动识别到传递过来的参数1和2,并把它们绑定到P1和P2上

使用RESTful操作资源 :可以通过不同的请求方式来实现不同的效果!如下:请求地址一样,但是功能可以不同!

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

使用method属性指定请求类型

//映射访问路径,必须是Get请求
    @RequestMapping(name="/commit/{p1}/{p2}",method ={RequestMethod.GET})
   public String index(@PathVariable int p1, @PathVariable int p2, Model model){       
       int result = p1+p2;
       //Spring MVC会自动实例化一个Model对象用于向视图中传值
       model.addAttribute("msg", "结果:"+result);
       //返回视图位置
       return "test";       
  }  

将@RequestMapping(name="/commit/{p1}/{p2}",method ={RequestMethod.GET})直接用注解代替,即@GetMapping("/commit/{p1}/{p2}")

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

三、数据处理及跳转

ModelAndView

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

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

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

ServletAPI:

通过设置ServletAPI , 不需要视图解析器

1、通过HttpServletResponse进行输出
2、通过HttpServletResponse实现重定向
3、通过HttpServletResponse实现转发

SpringMVC

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

@Controller
public class ResultSpringMVC2 {
   @RequestMapping("/rsm2/t1")
   public String test1(){
       //转发
       return "test";
  }
   @RequestMapping("/rsm2/t2")
   public String test2(){
       //重定向
       return "redirect:/index.jsp";//想要重定向,则加上redirect
  }
}

重定向的时候,地址栏会发生变化;转发的时候,地址栏不发生变化。

数据处理

处理提交数据

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

提交数据 : http://localhost:8080/hello?name=kuangshen

@RequestMapping("/hello")
public String hello(String name){
   System.out.println(name);
   return "hello";
}

后台输出 : kuangshen

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

提交数据 : http://localhost:8080/hello?username=kuangshen

@RequestParam("username") //: username提交的域的名称 .
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name){
   System.out.println(name);
   return "hello";
}

后台输出 : kuangshen

3、提交的是一个对象

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

实体类:

public class User {
   private int id;
   private String name;
   private int age;}

提交数据 : http://localhost:8080/mvc04/user?name=kuangshen&id=1&age=15

@RequestMapping("/user")
public String user(User user){
   System.out.println(user);
   return "hello";
}

后台输出 : User { id=1, name='kuangshen', age=15 }; 如果使用对象的话,前端传递的参数名和对象名必须一致,否则就是null

数据显示到前端

Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;

ModelMap 继承了 LinkedMap,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;

ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。

Json交互处理

后端和前端之间的交互通过json

轻量级的数据交换格式;文本格式存储和表示数据; JSON 是 JavaScript 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串 JSON 和 JavaScript 对象互转

要实现从JSON字符串转换为JavaScript 对象,使用 JSON.parse() 方法:
var obj = JSON.parse('{"a": "Hello", "b": "World"}');
//结果是 {a: 'Hello', b: 'World'}

要实现从JavaScript 对象转换为JSON字符串,使用 JSON.stringify() 方法:
var json = JSON.stringify({a: 'Hello', b: 'World'});
//结果是 '{"a": "Hello", "b": "World"}'

Controller返回JSON数据

后端java生成json(本质是字符串)传给前端,需要写接口

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

   private String name;
   private int age;
   private String sex;
   
}

@ResponseBody,ObjectMapper对象

@Controller
public class UserController {

   @RequestMapping("/json1")
   @ResponseBody//不会走视图解析器找页面,而是会返回一个字符串
   public String json1() throws JsonProcessingException {
       //创建一个jackson的对象映射器,用来解析数据
       ObjectMapper mapper = new ObjectMapper();
       //创建一个对象
       User user = new User("秦疆1号", 3, "男");
       //将我们的对象解析成为json格式
       String str = mapper.writeValueAsString(user);
       //由于@ResponseBody注解,这里会将str转成json格式返回;十分方便
       return str;
  }
}

在类上直接使用 @RestController ,这样子,里面所有的方法都只会返回 json 字符串了,不用在每一个都添加@ResponseBody !

@RestController
public class UserController {
   //produces:指定响应体返回类型和编码
   @RequestMapping(value = "/json1")
   public String json1() throws JsonProcessingException {
       //创建一个jackson的对象映射器,用来解析数据
       ObjectMapper mapper = new ObjectMapper();
       //创建一个对象
       User user = new User("秦疆1号", 3, "男");
       //将我们的对象解析成为json格式
       String str = mapper.writeValueAsString(user);
       //由于@ResponseBody注解,这里会将str转成json格式返回;十分方便
       return str;
  }
}

返回集合和时间也是和上面一样的

如果经常使用,可以把上述方法抽象成工具类 JsonUtils

package com.kuang.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.text.SimpleDateFormat;

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;
  }
}

四、ajax

五、拦截器