Spring学习——SpringMVC

1、跟踪SpringMVC请求

以下描述来自于《Spring实战》第4版:

  1. 浏览器发送请求,到达DispatcherServlet(前端控制器)。
  2. DispatcherServlet查询一个或多个处理器映射器(handler mapping)来确定要请求哪一个控制器(Controller),处理器映射会根据请求的URL来进行决策。
  3. 一旦选择了合适的控制器,DispatcherServlet会将请求发送给选中的控制器。到达控制器后,请求会卸下其负载(也就是用户提交的信息)并耐心等待控制器处理这些信息。
  4. 控制器处理完成后,将数据模型(逻辑处理后产生的需要返回给用户的信息就是模型)打包,并且标示出用于渲染输出的视图名,然后将请求连同模型和视图名发送回DispatcherServlet。
  5. DispatcherServlet会使用视图解析器(view resolver)来将逻辑视图名匹配为一个特定的视图实现。
  6. DispatcherServlet找到视图解析器匹配的视图的实现,在这里它交付模型数据。
  7. 视图将使用模型数据渲染输出,这个输出会通过响应对象传递给客户端。

SpringMVC请求流程图如下:(取自教学视频)

SpringWebMVC.png

2、搭建SpringMVC

2.1、配置DispatcherServlet:

按照传统的方式,DispatcherServlet这样的Servlet会配置在web.xml文件中,这个文件会放到应用的war包里面。

Spring3.1后,可以使用Java配置类来配置DispatcherServlet(但是它只能部署到支持Servlet3.0的服务器中才能正常工作,如Tomcat7或更高版本),如下:

这个类相当于web.xml:

public class SpringMvcDispatcherServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {

    /*
     * 将DispatcherServlet映射到"/"
     * */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }


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


    }

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


}

注意:

扩展AbstractAnnotationConfigDispatcherServletInitializer的任意类都会自动配置DispatcherServlet和Spring应用上下文,Spring的应用上下文会位于应用程序的Servlet上下文之中。

AbstractAnnotationConfigDispatcherServletInitializer剖析

容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果能发现的话,就会用它来配置Servlet容器。

Spring提供了这个接口的实现,名为SpringServletContainerInitializer,这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给他们来完成。Spring3.2引入了一个便利的WebApplicationInitializer基础实现,也就是AbstractAnnotationConfigDispatcherServletInitializer。因为我们自己的配置类扩展了AbstractAnnotationConfigDispatcherServletInitializer(同时也就实现了WebApplicationInitializer),因此当部署到Servlet3.0容器中的时候,容器会自动发现它,并用它来配置Servlet上下文。

2.2、两个应用上下文

两个应用上下文——DispatcherServlet创建的应用上下文、ContextLoaderListener创建的应用上下文

当DispatcherServlet启动的时候,它会创建Spring应用上下文,并加载配置文件或配置类中所声明的bean。

在SpringWeb应用中,通常还会有另外一个应用上下文,这个应用上下文是由ContextLoaderListener创建的。

两个应用上下文的作用

  • DispatcherServlet加载包含Web组件的bean,如控制器、视图解析器以及处理器映射
  • ContextLoadListener加载应用中的其他bean,这些bean通常是驱动应用后端的中间层和数据层组件,用于管理数据库访问层、业务逻辑层等组件。

两个应用上下文何时创建:

  • AbstractAnnotationConfigDispatcherServletInitializer会同时创建DispatcherServlet和ContextLoadListener。
  • getServletConfigClasses()方法返回的带有@Configuration注解的类将会用来定义DispatcherServlet应用上下文中的bean
  • getRootConfigClasses()方法返回的带有@Configuration注解的类将会用来配置ContextLoadListener创建的应用上下文中的bean

3、启用SpringMVC

3.1、配置DispatcherServlet应用上下文

这个类是servlet容器的配置类,用于管理控制层、视图解析器等组件,相当于springmvc.xml

可以在其中配置视图解析器、静态资源解析器、拦截器等组件

@Configuration
@EnableWebMvc    //启用SpringMVC
@ComponentScan("cn.ssm.controller")  //启用组件扫描
public class WebConfig extends WebMvcConfigurerAdapter {

    /*
    * 配置jsp视图解析器
    * */
    @Bean
    public ViewResolver viewResolver(){

        InternalResourceViewResolver resourceViewResolver=new InternalResourceViewResolver();
        resourceViewResolver.setPrefix("/WEB-INF/JSP/views/");
        resourceViewResolver.setSuffix(".jsp");
        resourceViewResolver.setExposeContextBeansAsAttributes(true);

        return resourceViewResolver;
    }

    /*
    * 配置静态资源的处理
    * 将对静态资源的请求转发到Servlet容器中默认的Servlet上,而不是使用DispatcherServlet本身处理此类请求
    * */
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

3.2、配置ContextLoadListener应用上下文

目前,我们只用到了web相关配置,而web相关配置已经在DispatcherServlet应用上下文配置好了,所以,目前ContextLoadListener配置类如下:

这个是根容器的配置类,用于管理数据库访问层、业务逻辑层等组件,相当于spring.xml

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

这里也使用了@ComponentScan注解,这样的话,我们以后就可以使用非web组件来充实完善该配置类

3.3、编写控制器

最基本的控制器:

@Controller
public class HomeController {

    @RequestMapping(value = "/",method = RequestMethod.GET)
    public String index(){

        return "home";
    }
}

注:

  • @Controller是一个构造性注解,它基于@Component注解,它的目的就是辅助实现注解扫描。也可以使用@Component代替@Controller,实现的效果是一样的,但表意性差一些,无法清楚地看出HomeController是什么组件类型
  • @RequestMapping的value属性指定该controller要处理的请求路径,method属性细化了它要处理的请求的HTTP方法
  • 返回的String类型的"home"。这个String会被SpringMVC解读为要渲染的视图名称。DispatcherServlet会要求视图解析器将这个逻辑名称解析为实际的视图。根据WebConfig中的配置,视图名会被解析为"/WEB-INF/JSP/views/home.jsp"

home.jsp如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>欢迎</title>
</head>
<body>

<h1>Hello World!</h1>
    
</body>
</html>

部署后即可访问:
SpringMVC访问.png

4、接收请求参数

SpringMVC允许以多种方式将客户端中的数据传送到控制器的处理方法中,包括:

  • 查询参数
  • 表单参数
  • 路径变量

4.1、处理查询参数

在控制器中对应的方法参数上添加@RequestParam注解,其value属性值就是对应的参数名

@Controller
@RequestMapping("/test")
public class TestWithQueryParameterController {

    @RequestMapping("/queryparameter")
    public String testQueryParameter(@RequestParam("id") int id)	 {
		System.out.println(id);
        return "queryParameter";
    }
}

4.2、处理路径参数

对"/articles/12"发起的GET请求要优于对"/articles?id=12"发起请求。前者能识别出要查询的资源,而后者描述的是带有参数的一个操作——本质上是通过HTTP发起的RPC。

@RequestMapping("/pathvariable/{id}")
public String testPathVariable(@PathVariable("id") int id){

   System.out.println(id);
   return "queryParameter";
}

注意:

  • @RequestMapping中占位符的名称要用大括号("{“和”}")括起来。路径中的其他部分要与所处理的请求完全匹配,但是占位符部分可以是任意值(但要和对应方法的对应参数类型相同)

  • 当方法参数名和占位符名称相同时,可以去掉@PathVariable中的value属性,如下

    @RequestMapping("/pathvariable/{id}")
    public String testPathVariable(@PathVariable(int id){
    
       System.out.println(id);
       return "queryParameter";
    }
    

4.3、处理表单

4.3.1、接收表单参数

注:如果表单的form标签中没有设置action属性,在这种情况下,当表单提交时,它会提交到与展现时相同的URL路径上。

接收表单时,控制器参数是一般一个实体类,只要实体类中的属性名和表单提交的参数名相同,便会进行参数绑定。

4.3.2、校验表单

校验表单数据合法性,可以使用Spring对Java校验API(Java Validation API),又称JSR-303。

如要使用,需引入这个API的实现,比如Hibernate Validator。

Java校验API定义了许多注解,这些注解可以放到属性上,从而限制这些属性的值。所有注解都位于javax.validation.constraints包中,这里不再列举。

在对应属性上添加注解后,还要在控制器对应方法上,在要校验的参数前面添加@Valid。可以在参数后面紧跟着添加一个Error error参数,可以通过error获取到校验错误信息


代码书写世界,吉他演奏生活