Spring Framework 学习笔记(3) Sping M

2021-07-07  本文已影响0人  张云飞Vir

1. 背景

Sping MVC 是在 Spring 之上的框架,用于开发 Web 程序。

2. 初步了解 Spring MVC

2.1 知识

Spring MVC 是建立在 Servlet API 之上的 Web 框架,包含在 Spring Framework 中。MVC 是指 模型,视图,控制器的意思,Spring MVC 实现了这种思想。

Spring MVC 分离了控制器、模型对象、过滤器以及处理程序对象的角色,这种分离让它们更容易进行定制。

Spring MVC 不依赖 JSP,可以使用其他模板引擎(JSP,thymeleaf等)。RESTful API 返回的 JSON 格式可以理解为 json View,也是 MVC。

Spring MVC 与许多其他 Web 框架一样,是围绕前端控制器模式( front controller )设计的,其中DispatcherServlet为请求处理提供统一入口,而实际工作由委托组件处理。

一个HTTP请求经过 Spring MVC 需要经历的过程如下:


image.png

2.2 编写一个精简的 MVC 项目

刚刚说了 一个请求所要经历的过程,提到了几个组件,下面我们通过搭建基础版的项目进一步了解。

传统的web项目需要一个web.xml进行配置,包括 Servlet的配置映射,请求映射,视图解析,异常处理,委托组件等。DispatcherServlet 需要知道这些配置。

我们这里不这么做,而由 java 代码配置 DispatcherServlet 。 通过继承 AbstractAnnotationConfigDispatcherServletInitializer 来实现,当它部署在 sevlet 3.0的容器中时,容器会自动发现它并应用配置,示例:

/**
 * web app 的初始化辅助类。等同于 web.xml
 */
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{RootConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{ServletConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

上面的代码中配置说明如下:

通常在一个 web 应用中有这么两个上下文:

关系如下:


image.png

我这里 RootConfig 是空的。

@Configuration
@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = EnableWebMvc.class)})
public class RootConfig {
}

ServletConfig 类中配置一个 jsp 视图解析器。

@Configuration
@EnableWebMvc
@ComponentScan
public class ServletConfig extends WebMvcConfigurerAdapter {


    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

写一个简单的 Controller

@Controller
@RequestMapping("/main")
public class HelloWorldController {
    @RequestMapping(value = "/say", method = RequestMethod.GET)
    public String sayHello(Model model) {
        model.addAttribute("yourname", "zhangsan");
        return "welcome";
    }
}

welcome.jsp 文件代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>HelloWorld page</title>
</head>
<body>
<h1>这是首页</h1>
Your Name : ${yourname}
</body>
</html>

使用 Spring 结合 Spring MVC 的一个精简版的项目就搭建好了。
我的完整代码示例见:https://github.com/vir56k/java_demo/tree/master/spring_mvc_demo_1

2.3 读取请求中的参数

支持下述参数:

示例:

使用 @RequestParam 读取查询字符串中或表单数据的参数值

    @GetMapping
    public String setupForm(@RequestParam("petId") int petId, Model model) { 
        Pet pet = this.clinic.loadPet(petId);
        model.addAttribute("pet", pet);
        return "petForm";
    }

使用 @PathVariable 注解读取 青汽路径的参数。

@GetMapping(path = "/pets/{petId}", params = "myParam=myValue") 
public void findPet(@PathVariable String petId) {
    // ...
}

2.4 参数的合法性校验

Spring Framework 提供对 Java Bean Validation API 的支持。

示例:

public class PersonForm {

    @NotNull
    @Size(max=64)
    private String name;

    @Min(0)
    private int age;
}
public class User {

    private String email;

    @NotNull @Email
    public String getEmail() {
      return email;
    }

    public void setEmail(String email) {
      this.email = email;
    }
}

public class UserService {

  public void createUser(@Email String email,
                         @NotNull String name) {
    ...
  }
}

更多请参考:https://beanvalidation.org/

2.5 视图渲染

而类似 JSP 开发的方式已是古老的方法。较新一点的是 Thymeleaf 框架。当前(本文写作时间2021-07-06)比较流行的开发方式是前后端分离的技术,使用 ReactJS,VUE 单独开发项目。本文不再多介绍。

2.6 使用HTTP消息转换器

消息转换器
消息转换(message conversion)提供了一种更为直接的方式,它能够将控制器产生的数据转换为服务于客户端的表述形式(JSON,XML等)。

当使用消息转换功能时,DispatcherServlet不再将模型数据传送到视图中,它直接通过消息转换器直接转换成指定格式。

比如 如果 Jackson JSON在类路径下,那么处理方法返回的对象将交给 MappingJacksonHttpMessageConverter 来处理。

@RestController注解
正常情况下,当处理方法返回Java对象时,这个对象会放在模型中并在视图中渲染使用。但是,如果使用了消息转换功能的话,我们需要告诉Spring 跳过正常的模型/视图流程,并使用消息转换器。最简单
的方法是为控制器方法添加@ResponseBody注解。

@RequestMapping("/login")
@ResponseBody
public Object login(String name, String password, HttpSession session) {
    ...
    return new JsonResult(user);
}

如果在控制器类上使用@RestController来代替@Controller的话,Spring将会为该控制器的所有处理方法应用消息转换功能。我们不必为每个方法都添加@ResponseBody了。

@RestController
public class XxxController{
   ...
}

返回ResponseEntity对象
控制器方法可以返回一个ResponseEntity对象。ResponseEntity中可以包含响应相关的元数据(如头部信息和状态码)以及要转换的对象实体。

@GetMapping("/custom")
ResponseEntity<String> custom() {
    HttpHeaders headers = new HttpHeaders();
    headers.add("Custom-Header", "foo");

    return new ResponseEntity<>(
      "Custom header set", headers, HttpStatus.OK);
}

2.7 扩展

UriComponentsBuilder
Spring提供了UriComponentsBuilder,可以给我们一
些帮助。它是一个构建类,通过逐步指定URL中的各种组成部分(如host、端口、路径以及
查询),我们能够使用它来构建UriComponents实例。

UriComponents uriComponents=UriComponentsBuilder
        .fromHttpUrl("http://localhost:8080//hello")
        .queryParams(params).build() 
String uri=uriComponents.toUriString();

@RequestMapping
@RequestMapping 标识了这是一个请求映射,还有一些基于此扩展的方法,像下面这些,从名字就能看出具体的含义。

自定义 servlet 和 filter
扩展一下,通过继承 WebApplicationInitializer 可以注册自定义 servlet 和 filter:

/**
 * 初始化 web 应用
 */
public class MyWebApplicationInitializer implements WebApplicationInitializer {

    public void onStartup(ServletContext servletContext) throws ServletException {
        // 注册一个 servlet
        ServletRegistration.Dynamic servlet = servletContext.addServlet("myservlet1", MyServlet.class);
        servlet.addMapping("/custom/**");

        // 注册一个过滤器
        FilterRegistration.Dynamic filter1 = servletContext.addFilter("filter1", MyFilter1.class);
        filter1.addMappingForUrlPatterns(null, false, "/custom/*");
    }
}

Multipart 解析器和文件上传
注册一个 Multipart 解析器

public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    // ...

    @Override
    protected void customizeRegistration(ServletRegistration.Dynamic registration) {

        // Optionally also set maxFileSize, maxRequestSize, fileSizeThreshold
        registration.setMultipartConfig(new MultipartConfigElement("/tmp"));
    }

}

接收上传文件

@Controller
public class FileUploadController {

    @PostMapping("/form")
    public String handleFormUpload(@RequestParam("name") String name,
            @RequestParam("file") MultipartFile file) {

        if (!file.isEmpty()) {
            byte[] bytes = file.getBytes();
            // store the bytes somewhere
            return "redirect:uploadSuccess";
        }
        return "redirect:uploadFailure";
    }
}

异常处理
Spring MVC 提供了多种形式将异常转化成 响应:

@ResponseStatus 注解的自定义异常,将自动映射到 HTTP 的状态码:

@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "啊呜,找不见了...")
public class MyException extends RuntimeException {

}

使用 @ExceptionHandler 注解捕获异常:
@ExceptionHandler 作用在 controller 类上时,可以捕获这个 controller 的异常。

    // @ExceptionHandler 注解,在这里会捕获这个类异常
    @ExceptionHandler(MyException2.class)
    public String handleException() {
        return "errorrrr";
    }

@ExceptionHandler 注解 结合“ 控制器通知 ” 可以捕获所有 控制器的异常。

控制器通知( controller advise ) 是指 被标注了@ControllerAdvice 注解的类。这个类可以配合以下的注解使用:

@ControllerAdvice
public class AppExceptionHandler {

    // @ExceptionHandler 注解,在这里会捕获这个类异常
    @ExceptionHandler(MyException3.class)
    public String handleException() {
        return "errorrrr";
    }
}

3. 示例

我的代码示例见:https://github.com/vir56k/java_demo/tree/master/spring_mvc_demo_2

4.参考:

https://docs.spring.io/spring-framework/docs/current/reference/html/web.html

http://websystique.com/springmvc/spring-4-mvc-helloworld-tutorial-annotation-javaconfig-full-example/

上一篇下一篇

猜你喜欢

热点阅读