接上一章:读 Spring Boot实战笔记–day002
测试
测试是开发工作中不可缺少的部分。单元测试只针对当前开发的类和方法进行测试,可以简单通过模拟依赖来实现,对运行环境没有依赖;但是仅仅进行单元测试是不够的,它只能验证当前类或方法能否正常工作,而我们想要知道系统的各个部分组合在-起是否能正常工作,这就是集成测试存在的意义。集成测试一般需要来自不同层的不同对象的交互,如数据库、网络连接、loC 容器等。其实我们也经常通过运行程序,然后通过自己操作来完成类似于集成测试的流程。集成测试为我们提供了一种无须部署或运行程序来完成验证系统各部分是否能正常协同工作的能力。
Spring通过Spring TestContex Framework对集成测试提供顶级支持。它不依赖于特定的测试框架,既可使用Junit,也可使用TestNG.基于Maven构建的项目结构默认有关于测试的目录: src/test/java (测试代码)、src/test/resources(测试资源),区别于src/main/java (项目源码)、src/main/resources (项目资源)。
Spring提供了一个SpringJUnit4ClassRunner 类,它提供了Spring TestContext Framework的功能。通过@ContextConfiguration来配置Application Context, 通过@ActiveProfiles确定活动的profile。在使用了Spring测试后,我们前面的例子的“运行”部分都可以用Spring测试来检验功能能否正常运作。集成测试涉及程序中的各个分层,本节只对简单配置的Application Context和在测试中注入Bean做演示,在本书第二部分和第三部分会对Spring测试做更多的讲述。
<!-- Spring test支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
package com.example.springboot;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @Author: hyh
* @Date: 2021/11/6 17:01
**/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {ProfileConfig.class})
@Profile("dev")
public class Test {
@Autowired
private Bean1 bean;
@org.junit.Test
public void test(){
bean.say();
}
}
SpringMVC 常用注解
@Controller注解在类上,表明这个类是Spring MVC里的Controller,将其声明为Spring的一个Bean, Dispatcher Servlet会自动扫描注解了此注解的类,并将Web请求映射到注解了@RequestMapping的方法上。
这里特别指出,在声明普通Bean 的时候,使用@Component、@Service、 @Repository @Controller是等同的,因为@Service、@Repository、 @Controller 都组合了@Compoment元注解;但在Spring MVC声明控制器Bean的时候,只能使用@Controllero
@RequestMapping注解是用来映射Web请求(访问路径和参数)、处理类和方法的。
@RequestMapping可注解在类或方法上。注解在方法上的@RequestMapping路径会继承注解在类上的路径, @RequestMapping支持Servlet的request和response作为参数,也支持对request和response的媒体类型进行配置。
@ResponseBody支持将返回值放在response体内,而不是返回一个页面。我们在很多基于Ajax的程序的时候,可以以此注解返回数据而不是页面;此注解可放置在返回值前或者方法上。
@RequestBody允许request的参数在request体中,而不是在直接链接在地址后面。此注解放置在参数前。
@PathVariable用来接收路径参数,如/news/001, 可接收001作为参数,此注解放置在参
数前。
@RestController是一个组合注解, 组合了@Controller和@ResponseBody,这就意味着当你只开发个和页面交互数据的控制的时候,需要使用此注解。若没有此注解,要想实现上述功能,则需自己在代码中加@Controller和@ResponseBody两个注解。下面的示例将演示这几个注解的使用。
SpringMVC 基本配置
Spring MVC 的定制配置需要我们的配置类继承一个 WebMvcConfigurerAdapter类,并在此类使用@EnableWebMvc注解,来开启对Spring MVC的配置支持,这样我们就可以重写这个类的方法,完成我们的常用配置。
我们将前面的 MyMveConfig 配置类继承 WebMvcConfigurerAdapter静态资源映射:程序的静态文件(js、css、图片)等需要直接访问,这时我们可以在配置里重写addResourceHandlers方法来实现。同上,我们在src/main/resources下建立 assets/js目录,并复制一个jquery.js放置在此目录下,如图4-6所示。
package com.example.springboot.mvc;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
/**
* @Author: hyh
* @Date: 2021/11/15 15:12
**/
@Configuration
@EnableWebMvc // 开启 webmvc
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//addResourceLocations 指的是文件放置的目录,
//addResourceHandler指的是对外暴露的访问路径。
registry.addResourceHandler("/assets/**")
.addResourceLocations("class:/assets/");
}
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("WEB-INF/class/views/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
}
拦截器配置
package com.example.springboot.mvc;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @Author: hyh
* @Date: 2021/11/15 15:25
**/
public class MyInterceptor implements HandlerInterceptor {
// 请求处理之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
return false;
}
// 请求处理之后
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
}
}
package com.example.springboot.mvc;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
/**
* @Author: hyh
* @Date: 2021/11/15 15:12
**/
@Configuration
@EnableWebMvc // 开启 webmvc
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//addResourceLocations 指的是文件放置的目录,
//addResourceHandler指的是对外暴露的访问路径。
registry.addResourceHandler("/assets/**")
.addResourceLocations("class:/assets/");
}
@Bean
public MyInterceptor interceptor(){
return new MyInterceptor();
}
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("WEB-INF/class/views/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
// 拦截所有请求
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor());
}
}
@ControllerAdvice
@ControllerAdvice注解是Spring3.2中新增的注解,学名是Controller增强器,作用是给Controller控制器添加统一的操作或处理。
通过@ControllerAdvice,我们可以将对于控制器的全局配置放置在同一个位置,注解了@Controller 的类的方法可使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上,这对所有注解了@RequestMapping 的控制器内的方法有效。
@ExceptionHandler:用于全局处理控制器里的异常。
@InitBinder:用来设置 WebDataBinder,WebDataBinder用来自动绑定前台请求参数到Model 中。
@ModelAttribute: @ModelAttribute本来的作用是绑定键值对到Modcl里,此处是让全局的@RequestMapping 都能获得在此处设置的键值对。
package com.example.springboot.mvc;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Author: hyh
* @Date: 2021/11/15 16:04
**/
@ControllerAdvice
public class ControllerAdviceHandler {
// 全局异常处理 通过 ExceptionHandler 注解进行拦截所有 Exception 的 controller
@ExceptionHandler(Exception.class)
public ModelAndView exception(Exception e){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("error",e);
return modelAndView;
}
// 全局异常处理 通过 ExceptionHandler和 ResponseStatus 注解进行拦截所有 500 的 controller
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String handleGlobalException(Exception e) {
return e.getMessage();
}
// 定制 web 请求时可以将Date 参数格式转换为 MM-dd-yyyy
@InitBinder
public void initBinder(WebDataBinder binder){
binder.registerCustomEditor(Date.class,new CustomDateEditor(new SimpleDateFormat("MM-dd-yyyy"),false));
}
// 将参数 msg 添加到全局 在所有 @RequestMapiing 都可以获取到
@ModelAttribute
public void addAttribute(Model model){
model.addAttribute("msg","123");
}
}
package com.example.springboot.mvc;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
/**
* @Author: hyh
* @Date: 2021/11/15 16:27
**/
@Controller
public class ControllerH {
// 通过ModelAttribute 注解获取参数 msg
@GetMapping("index")
public void index(@ModelAttribute("msg") String msg){
}
}