Spring in action. 开发web应用

127 阅读5分钟

展现信息

构建领域类

应用的领域指的是它所要解决的主题范围:也就是会影响到对应用理解和概念。

@Data
@RequiredArgsConstructor
public class Ingredient {

    private final String id;
    private final String name;
    private final Type type;

    public static enum Type {
        WRAP,PROTEIN,VEGGIES,CHESS,SAUCE
    }
    
}
  • @Data:由Lombok提供
    • 它会为类自动生成所缺失的方法(getter(),setter(),equals(),hashCode(),toString())等。
    • 生成一个以final属性作为参数等构造器。
  • 为什么要将实例定义成final?因为使用了@RequiredArgsConstructor
    • 首先介绍下常见的DI注入,(注解方式注入、构造器注入、setter注入),最后一种就是使用@RequiredArgsConstructor注入,该注解要求:
      • 将变量声明为final;
      • 再根据构造器进行注入(@Data提供)

创建控制器类

我们需要一个简单的控制器,它需要完成如下的功能:

  • 处理路径为“/design”的HTTP GET请求。
  • 构建配料的列表。
  • 处理请求,并将配料表数据传递给要渲染为HTM的试图模板,发送给发起请求的web浏览器。
@Slf4j
@Controller
@RequestMapping("/design")
public class DesignTacoController {

    @GetMapping
    public String showDesignForm(Model model) {
        List<Ingredient> ingredients = Arrays.asList(
                new Ingredient("FLTO", "Flour Tortilla", Type.WRAP),
                new Ingredient("COTO", "Corn Tortilla", Type.WRAP),
                new Ingredient("GRBF", "Ground Beef", Type.PROTEIN),
                new Ingredient("CARN", "Carnitas", Type.PROTEIN),
                new Ingredient("TMTO", "Diced Tomatoes", Type.VEGGIES),
                new Ingredient("LETC", "Lettuce", Type.VEGGIES),
                new Ingredient("CHED", "Cheddar", Type.CHEESE),
                new Ingredient("JACK", "Monterrey Jack", Type.CHEESE),
                new Ingredient("SLSA", "Salsa", Type.SAUCE),
                new Ingredient("SRCR", "Sour Cream", Type.SAUCE)
        );
        Type[] types = Ingredient.Type.values();
        for (Type type : types) {
            model.addAttribute(type.toString().toLowerCase(),
                    filterByType(ingredients, type));
        }
        model.addAttribute("design",new Taco());
        return "desing";
    }

    private List<Ingredient> filterByType(
            List<Ingredient> ingredients, Type type) {
        return ingredients
                .stream()
                .filter(x -> x.getType().equals(type))
                .collect(Collectors.toList());
    }
}
  • @Slf4j:Lombok提供,在运行时会在这个类中自动生成一个SLF4J LOGGER。
  • @Controller:它会将这个类识别为控制器,并让其可以被组件扫描到,让该实例Spring应用上下文中的bean。
  • @RequestMapper:该注解用到类级别的时候,它能够指定该控制器所处理的请求类型
  • @GetMapping:用在方法上,与前面的@RequestMapper组成访问路径(get请求)。GetMapping是在Spring4.3引入的,在4.3之前,方法级别的注解为*@RequestMapping(method=RequestMethod.GET)*
  • Model:Model对象负责在控制器和展现数据的试图之间传递数据。实际上,放到Model属性中的数据将会复制到Servlet Response的属性中。

设计视图

引入spring-boot-starter-Thymleaf
Thymleaf视图库通过与servlet的request属性协作,在Spring将请求转移到视图之前,它就会把模型数据复制到request属性中,这样Thymleaf和其他视图模板就能访问到他们了(通过各自模板特定的语法来使用)

处理表单提交

@PostMapping
public String processDesign(Taco design) {
    //save the taco design
    log.info("Processing design: " + design);
    return "redirect:/orders/current";
}
  • "redirect:/orders/current": redirect前缀表明这是一个重定向视图,它表明在processDesign方法完成后,用户的浏览器会重定向到相对路径“/order/current"。

校验表单输入

引入Validation API。
Validation API提供了一些可以添加到领域对象上的注解,以便声明校验规则。
在Spring MVC中应用校验,我们需要:

  • 要在被校验的类上添加校验规则
  • 在控制器方法中声明要进行校验
  • 修改视图表单以展现校验错误

声明校验规则

@NotNull
@Size(min=5, message="Name must be at least 5 characters long")
private String name;
@Size(min=1, message="You must choose at least 1 ingredient")
private List<String> ingredients;

@CreditCardNumber(message="Not a valid credit card number")
private String ccNumber;

@Pattern(regexp="^(0[1-9]|1[0-2])([\/])([1-9][0-9])$",
         message="Must be formatted MM/YY")
private String ccExpiration;

@Digits(integer=3, fraction=0, message="Invalid CVV")
private String ccCVV;
  • ccNumber:保证它不为空,且必须是一个合法的信用卡号码。
  • ccExpiration:必须符合MM/YY格式
  • ccCVV:要求它是一个三位的数字

在表单绑定的时候执行校验

@PostMapping
public String processDesign(@Valid Taco design, Errors errors) {
    if (errors.hasErrors()) {
        return "design";
    }
    //save the taco design
    log.info("Processing design: " + design);
    return "redirect:/orders/current";
}
  • @Valid:该注解会告诉Spring MVC要对提交的Taco进行校验,而校验的时机是在它绑定完表单数据之后,调用processDesign()方法之前。如果校验错误,那么这些错误信息会被捕捉到一个Errors对象中。

使用视图控制器

上述的几个控制器都有着相似的编程模型:

  • 它们都使用了@Controller注解,表明了它们都是控制器类,并被被Spring的组件扫描功能自动发现并初始化为spring应用上下文中的bean;
  • 在控制器上使用类级别的注解@RequestMapping,据此定义该注解所处理的基本请求模式;
  • 它们都有一个或者多个带@GetMapping或@PostMapping注解的方法,用来指明用什么方式的请求。
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("home");
        
    }
}

缓存模板

默认情况下,模板只有在第一次使用的时候解析一次,解析的结果会被后续的请求所使用。对于生产环境来说,这是个很棒的特性,它能防止每次请求时多余的模板解析过程,因此有助于提升性能。
但在开发期这个特性就不太友好了,我们最好禁用它

  • spring.thymleaf.cache = false

在将应用部署到生产环境之前,一定要删除这行。

小结

  • Spring提供了一个非常强大的Web框架,名为SpringMVC,能够为Spring应用开发web前端。
  • SpringMVC是基于注解的,通过像@RequestMapping,@GetMapping,@PostMapping这样的注解来启用请求处理方法的声明。
  • 大多数请求处理方法最终会一个视图的逻辑名称,比如Thymleaf模版,请求会转发到这样的视图上(同时会带有任意的模型数据)
  • SpringMVC支持校验,这是通过Java Bean Validation API和Validation API实现的
  • 对于没有模型数据和逻辑处理的HTTP GET请求,可以使用视图控制器。
  • 除了Thymleaf之外,Spring支持各种视图方案,包括FreeMaker,Groovy Templates和Mustache。

--Spring 实战(第五版)Craig Walls,张卫滨