Spring实战第五章:构建Spring Web应用程序

一、SpringMVC起步

请求处理流程

Spring实战第五章:构建Spring Web应用程序_第1张图片

(1)请求携带url、参数等信息到达前端控制器DispatcherServlet(单例);

(2)DispatcherServlet会查询一个或多个处理器映射(handler mapping)来确定应该将请求发送给哪个控制器。处理器映射会根据请求所携带的URL信息来进行决策。 

(3)DispatcherServlet将请求发送给选中的控制器,然后请求会卸下负载(用户提交信息)并耐心等待控制器处理这些信息。

(4)控制器将处理请求产生的模型信息数据打包,并标示出用于渲染数据的视图,然后将请求连同模型和视图名发送回DispatcherServlet;(视图名是逻辑名称,可以用来查找真正视图)

(5)DispatcherServlet将会使用视图解析器(view resolver)来将逻辑视图名匹配为一个特定的视图实现(不一定是jsp)。

(6)视图将使用模型数据渲染输出,这个输出会通过响应对象传递给客户端;

 

二、代码编写

1、配置DispatcherServlet

(1)DispatcherServlet是Spring MVC的核心。在这里请求会第一次接触到框架,它要负责将请求路由到其他的组件之中。

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

(3)DispatcherServlet加载包含Web组件的bean,如控制器、视图解析器以及处理器映射,而ContextLoaderListener要加载应用中的其他bean。这些bean通常是驱动应用后端的中间层和数据层组件。

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

方式一:xml



  SpringMVC_01
  

   
     org.springframework.web.context.ContextLoaderListener
   

  


    contextConfigLocation
    classpath:config/applicationContext.xml


  
  
    springmvc
    org.springframework.web.servlet.DispatcherServlet
    
    1
  

  
    springmvc
    *.do
  

方式二:java

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import spittr.web.WebConfig;

public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
// 部署时自动发现这个类,并用它来配置Servlet上下文 P139
//AbstractAnnotationConfigDispatcherServletInitializer是传统web.xml的替代方案(servlet3.0之后支持)
  
  @Override 
  protected String[] getServletMappings() { 
// 会将一个或多个路径映射到DispatcherServlet,此处是"/"表示它会是默认的servlet,处理进入应用的所有请求
    return new String[] { "/" };
  }

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

  @Override // 指定配置类
  protected Class[] getServletConfigClasses() {
  // DispatcherServlet加载上下文时,会使用定义在WebConfig中bean
    return new Class[] { WebConfig.class };
  }

}

2、启用Spring MVC

方式一:xml方式


方式二:java方式

WebConfig:

@Configuration
@EnableWebMvc
public class WebConfig {
}
/*
但还有其他问题要解决:
1、没有配置视图解析器。Spring默认会使用BeanNameView-Resolver,这个视图解析器会查找ID与视图名称匹配的bean,并且查找的bean要实现View接口,以这样的方式来解析视图。
2、没有启用组件扫描。Spring只能找到显式声明在配置类中的控制器。
3、DispatcherServlet会映射为应用的默认Servlet,所以它会处理所有的请求,包括对静态资源的请求,如图片和样式表(可能并不是想要的效果)。
*/

改进:

@Configuration
@EnableWebMvc  // 启用spring mvc
@ComponentScan("spittr.web")  // 启用组件扫描
public class WebConfig extends WebMvcConfigurerAdapter {

  @Bean // InternalResourceViewResolver会查找jsp,查找的时候会添加视图名前后添加此前缀后缀
  public ViewResolver viewResolver() { // 配置jsp视图解析器
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setSuffix(".jsp"); 
    resolver.setExposeContextBeansAsAttributes(true);
    return resolver;
  }
  
  @Override // 配置静态资源的处理
  public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
// 设置DispatcherServlet对静态资源的请求转发到Servlet容器中默认的Servlet上,而不是使用DispatcherServlet本身来处理此类请求??
  }
}

问题:默认的Servlet是啥?本身处理的话将如何处理呢?

RootConfig:

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

3、编写控制器

在Spring MVC中,控制器只是方法上添加了@RequestMapping注解的类,这个注解声明了它们所要处理的请求。

(1)定义@RequestMapping

a、@RequestMapping应用在方法上

@Controller   // 声明为一个控制器
public class HomeController {
    @RequestMapping(value = "/",method = GET)  // 处理对“/”的GET请求
    public String home(){
        return "home";   // 视图名为 home
        // home将会被Spring MVC解读为要渲染的视图名称,DispatcherServlet会要求视图解析器将这个逻辑名称解析为实际的视图
        // 由前面配置,home将被解析为/WEB-INF/views/home.jsp路径的jsp
    }
}

b、@RequestMapping应用在类级别上

当控制器在类级别上添加@RequestMapping注解时,这个注解会应用到控制器的所有处理器方法上。处理器方法上的@RequestMapping注解会对类级别上的@RequestMapping的声明进行补充。

@Controller  
@RequestMapping("/")  // 将控制器映射到"/"
public class HomeController {
    @RequestMapping(method = GET)  // 处理GET请求
    public String home(){
        return "home";   // 视图名为 home
    }
}

@RequestMapping({"/","/homepage"}) 可以接收String数组;

(2)传递模型数据到控制器

@Controller
@RequestMapping("/spittles")
public class SpittleController {

    private SpittleRepository spittleRepository;

    @Autowired // 创建SpittleController bean时自动注入SpittleRepository
    public SpittleController(SpittleRepository spittleRepository){
        this.spittleRepository = spittleRepository;
    }

    @RequestMapping(method=RequestMethod.GET)
    public String spittles(Model model) { // 使用Model参数,则可将获取的信息填充到模型中
        // 将spittle添加到模型中
        model.addAttribute(spittleRepository   
                     .findSpittles(Long.MAX_VALUE,20));
        //当addAttribute不指定key时,key会根据值的对象类型推断,
        //此处是一个List,key被推断为spittleList
        return "spittles";  // 返回视图名
    }
}


@RequestMapping(method=RequestMethod.GET)
public String spittles(Model model) {
        model.addAttribute("spittleList", spittleRepository   
                     .findSpittles(Long.MAX_VALUE,20));// 指定key,功能同上
        return "spittles"; 
}


@RequestMapping(method=RequestMethod.GET)
public String spittles(Map model) {
        model.put("spittleList", spittleRepository   
                     .findSpittles(Long.MAX_VALUE,20));// 使用map
        return "spittles"; 
}

// 没有返回视图名称,没有显式指定模型
//当处理器方法像这样返回对象或集合时,这个值会放到模型中,模型的key会根据其类型推断得出
//逻辑视图的名称将会根据请求路径推断得出。因为这个方法处理针对“/spittles”的GET请求,因此视图的名称将会是spittles(去掉开头的斜线)
@RequestMapping(method=RequestMethod.GET)
public List spittles(){
    return spittleRepository.findSpittles(Long.MAX_VALUE, 20);
}

当视图是JSP的时候,模型数据会作为请求属性放到请求之中。

 

4、接口请求的输入

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

  • 查询参数(Query Parameter)
  • 表单参数(Form Parameter)
  • 路径变量(Path Variable)

(1)处理查询参数

/*
defaultValue:表示如果当前参数请求中不存在,则使用此默认参数。查询参数是String的,因此此处也需要是String型;当绑定到方法的max时,它会转换成Long类型
*/
@RequestMapping(method=RequestMethod.GET)
    public List spittles(
            @RequestParam(value="max", defaultValue=MAX_LONG_AS_STRING) long max,
            @RequestParam(value="count", defaultValue="20") int count) {
        return spittleRepository.findSpittles(max, count);
    }

(2)通过路径参数接受输入

假设我们的应用程序需要根据给定的ID来展现某一个Spittle记录。其中一种方法就是编写处理器方法,通过使用@RequestParam注解,让它接受ID作为查询参数,/spittles/show?spittle_id=12345。另一种方式,让需要识别的资源通过URL路径进行标示,形如/spittles/12345。前者描述的是带有参数的一个操作,后者能够识别出要查询的资源,在这里使用后者更加合理。

// 占位符的名称要用大括号(“{”和“}”)括起来。路径中的其他部分要与所处理的请求完全匹配,但是占位符可以是任意的值  
//@PathVariable("spittleId")注解,表明在请求路径中,不管占位符部分的值是什么都会传递到处理器方法的spittleId参数中
 @RequestMapping(value="/{spittleId}", method=RequestMethod.GET)
    public String spittle(@PathVariable("spittleId") long spittleId, Model model) {
        model.addAttribute(spittleRepository.findOne(spittleId));
        return "spittle";
    }

// 方法的参数名称与占位符名称相同,可以去掉@PathVariable的value属性。它会假设占位符的名称和方法参数名相同
 @RequestMapping(value="/{spittleId}", method=RequestMethod.GET)
    public String spittle(@PathVariable long spittleId, Model model) {
        model.addAttribute(spittleRepository.findOne(spittleId));
        return "spittle";
    }

(3)处理表单

少量的数据,可以使用查询参数和路径变量;但传递更多的数据,表单更适合;

@Controller
@RequestMapping("/spitter")
public class SpitterController {

    private SpitterRepository spitterRepository;

    @Autowired // 构造器注入进来
    public SpitterController(SpitterRepository spitterRepository){
        this.spitterRepository = spitterRepository;
    }


// 它接受一个Spitter对象作为参数。这个对象有firstName、lastName、username和password属性,
// 这些属性将会使用请求中同名的参数进行填充。
    @RequestMapping(value="/register", method=POST)
    public String processRegistration(Spitter spitter) {
        spitterRepository.save(spitter);
        return "redirect:/spitter/"+spitter.getUsername(); // 重定向
    }
}

(4)表单校验:校验入参合法性

方式一:在controller中写代码校验入参,比较初级

方式二:使用Spring对Java校验API

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

注解 描述
@AssertFalse 所注解的元素必须是Boolean类型,并且值为false
@AssertTrue 所注解的元素必须是Boolean类型,并且值为true
@DecimalMax 所注解的元素必须是数字,并且它的值要小于或等于给定的BigDecimalString值
@DecimalMin 所注解的元素必须是数字,并且它的值要大于或等于给定的BigDecimalString值
@Digits 所注解的元素必须是数字,并且它的值必须有指定的位数
@Future 所注解的元素的值必须是一个将来的日期
@Max 所注解的元素必须是数字,并且它的值要小于或等于给定的值
@Min 所注解的元素必须是数字,并且它的值要大于或等于给定的值
@NotNull 所注解的元素的值必须不能为null
@Null 所注解的元素的值必须为null
@Past 所注解的元素的值必须是一个已过去的日期
@Pattern 所直接的元素的值必须匹配给定的正则表达式
@Size 所注解的元素的值必须是String、集合或数组,并且它的长度要符合给定的范围
public class Spitter {
    private Long id;

    @NotNull
    @Size(min=5, max=16) // 非空,5-16字符
    private String username;

    @NotNull
    @Size(min=5, max=25)
    private String password;

    @NotNull
    @Size(min=2, max=30)
    private String firstName;

    @NotNull
    @Size(min=2, max=30)
    private String lastName;
}


@RequestMapping(value="/register", method=POST)
    public String processRegistration(
            @Valid Spitter spitter,   // 校验 Spitter输入
            Errors errors) {
// 如果校验出现错误,可以通过Errors对象进行访问。Errors参数要紧跟在@Valid注解的参数后面,@Valid注解所标注的就是要校验的参数
        if (errors.hasErrors()) {
            return "registerForm";   // 如果校验出现错误,则重新返回表单
        }
        spitterRepository.save(spitter);
        return "redirect:/spitter/" + spitter.getUsername();
    }

 

 

 

你可能感兴趣的:(框架)