SpringBoot-web

功能分析

1、静态资源访问

1、静态资源目录

只要静态资源放在类路径下: called /static (or /public or /resources or /META-INF/resources

访问 : 当前项目根路径/ + 静态资源名

原理: 静态映射/**。

请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面

改变默认的静态资源路径

spring:
  mvc:
    static-path-pattern: /res/**

  resources:
    static-locations: [classpath:/haha/]

2、静态资源访问前缀

默认无前缀

spring:
  mvc:
    static-path-pattern: /res/**

当前项目 + static-path-pattern + 静态资源名 = 静态资源文件夹下找

3、webjar

自动映射 /webjars/**

https://www.webjars.org/

<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>jquery</artifactId>
    <version>3.5.1</version>
</dependency>

访问地址:http://localhost:8080/webjars/jquery/3.5.1/jquery.js 后面地址要按照依赖里面的包路径

2.静态资源配置原理

  • SpringBoot启动默认加载 xxxAutoConfiguration 类(自动配置类)
  • SpringMVC功能的自动配置类 WebMvcAutoConfiguration,生效
spring:
#  mvc:
#    static-path-pattern: /res/**

  resources:
    add-mappings: false   禁用所有静态资源规则

3、请求参数处理

1.请求映射

1、rest使用与原理

Rest风格支持(使用HTTP请求方式动词来表示对资源的操作

Rest原理(表单提交要使用REST的时候)

  • 表单提交需要带上**_method=PUT**

在这里插入图片描述

  • 请求过来被HiddenHttpMethodFilter拦截

    # 默认是false,需要开启
    @Bean
    @ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
    @ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
    public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
        return new OrderedHiddenHttpMethodFilter();
    }
    
  • spring:
      mvc:
        hiddenmethod:
          filter:
            enabled: true   #开启页面表单的Rest功能
    
    • 请求是否正常,并且是POST
      • 获取到**_method**的值。
      • 兼容以下请求;PUT.DELETE.PATCH
      • 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
      • 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。

修改默认的_method

//自定义filter
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
    HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
    methodFilter.setMethodParam("_m");
    return methodFilter;
}
2、请求映射原理

在这里插入图片描述

SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet-》doDispatch()

protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
    throws Exception {
    HandlerExecutionChain mappedHandler = null;
    、、、
    try {
        ModelAndView mv = null;
        Exception dispatchException = null;
        try {
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // 找到当前请求使用哪个Handler(Controller的方法)处理
            mappedHandler = getHandler(processedRequest);

            //HandlerMapping:处理器映射。/xxx->>xxxx

RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射规则。

2.普通参数与基本注解

//  car/2/owner/zhangsan
@GetMapping("/car/{id}/owner/{username}")
public Map<String,Object> getCar(@PathVariable("id") Integer id,
                                 @PathVariable("username") String name,
                                 @PathVariable Map<String,String> pv,){
    
}
# @PathVariable Map<String,String> pv, 用来存放所有的键值对,
#@PathVariable@RequestParam@RequestHeader 都可用map接受,意为全部

@RequestAttribute 获取request域属性

@MatrixVariable 矩阵变量

//1、语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd	(以分号分隔,可以是kv,也可以是数组)
//2.使用场景:cookie禁用了,可以通过矩阵变量方式传递jsessionid。
//2、SpringBoot默认是禁用了矩阵变量的功能
//      手动开启:原理。对于路径的处理。UrlPathHelper进行解析。
//              removeSemicolonContent(移除分号内容)支持矩阵变量
//3、矩阵变量必须有url路径变量才能被解析
@GetMapping("/cars/{path}")
public Map carsSell(@MatrixVariable("low") Integer low,
                    @MatrixVariable("brand") List<String> brand,
                    @PathVariable("path") String path){
    Map<String,Object> map = new HashMap<>();
    map.put("low",low);
    map.put("brand",brand);
    map.put("path",path);
    return map;
}

// /boss/1;age=20/2;age=10
// pathVar 代表获取那个路径下的值
@GetMapping("/boss/{bossId}/{empId}")
public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,
                @MatrixVariable(value = "age",pathVar = "empId") Integer empAge){
    Map<String,Object> map = new HashMap<>();
    map.put("bossAge",bossAge);
    map.put("empAge",empAge);
    return map;
}

配置类:

在这里插入图片描述

3.复杂参数

Map、**Model(map、model里面的数据会被放在request的请求域 request.setAttribute)、**Errors/BindingResult、RedirectAttributes( 重定向携带数据)ServletResponse(response)

在这里插入图片描述

4.拦截器

1.HandlerInterceptor 接口

package com.atxins.practicedemo.config;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * @author Axin
 * @since  2021/9/5 22:18
 * 1.配置好拦截器拦截那些请求
 * 2.把这些配置放在容器中
 * 3.拦截器执行步骤,preHandle->目标方法->postHandle->afterCompletion
 */
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 目标方法执行之前
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        Object user = session.getAttribute("loginUser");
        if (user != null) {
            //放行
            return true;
        }
        request.setAttribute("msg","请先登录");
        //拦截住,转发到登录页
        request.getRequestDispatcher("/").forward(request,response);
        return false;
    }

    /**
     * 目标方法执行完成后
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    /**
     * 页面渲染以后
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

2.配置拦截器

@Configuration
public class AdminWebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
            //所有请求都被拦截包括静态资源
            .addPathPatterns("/**") 
            //放行的请求
          .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**","/js/**"); 
    }
}

3、拦截器原理

在这里插入图片描述

5、文件上传

1.文件上传代码

@PostMapping("/upload")
public String upload(@RequestParam("email") String email,
                     @RequestParam("username") String username,
                     @RequestPart("headerImg") MultipartFile headerImg,
                     @RequestPart("photos") MultipartFile[] photos) throws IOException {
    if(!headerImg.isEmpty()){
        //保存到磁盘
        String originalFilename = headerImg.getOriginalFilename();
        headerImg.transferTo(new File("H:\\cache\\"+originalFilename));
    }
    if(photos.length > 0){
        for (MultipartFile photo : photos) {
            if(!photo.isEmpty()){
                String originalFilename = photo.getOriginalFilename();
                photo.transferTo(new File("H:\\cache\\"+originalFilename));
            }
        }
    }
    return "main";
}

2.文件大小限制

spring:
  servlet:
    multipart:
      //单个文件最大限制
      max-file-size: 10MB
      //所有文件总的限制
      max-request-size: 100MB

6、全局异常处理器

spring boot 默认情况下会映射到 /error 进行异常处理,但是提示并不十分友好,下面自定义异常处理,提供友好展示。

@RestControllerAdvice +@ExceptionHandler 实现拦截异常并统一处理

/**
 * controller 增强器
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 全局异常捕捉处理
     * @param ex
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public Map errorHandler(Exception ex) {
        Map map = new HashMap();
        map.put("code", 100);
        map.put("msg", ex.getMessage());
        return map;
    }
    
    /**
     * 拦截捕捉自定义异常 MyException.class
     * @param ex
     * @return
     */

    @ExceptionHandler(value = MyException.class)
    public Map myErrorHandler(MyException ex) {
        Map map = new HashMap();
        map.put("code", ex.getCode());
        map.put("msg", ex.getMsg());
        return map;
    }
}
public class MyException extends RuntimeException {

    public MyException(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    private String code;
    private String msg;

    // getter & setter
}

@RestControllerAdvice是一个组合注解,由@ControllerAdvice、@ResponseBody组成,而@ControllerAdvice继承了@Component,因此@RestControllerAdvice本质上是个Component,用于定义@ExceptionHandler,@InitBinder和@ModelAttribute方法,适用于所有使用@RequestMapping方法。

@ControllerAdvice  
public class GlobalController{  
     
    //(1)全局数据绑定
    //应用到所有@RequestMapping注解方法  
    //此处将键值对添加到全局,注解了@RequestMapping的方法都可以获得此键值对  
    @ModelAttribute 
    public void addUser(Model model) {   
        model.addAttribute("msg", "此处将键值对添加到全局,注解了@RequestMapping的方法都可以获得此键值对");  
    }    
    //(2)全局数据预处理
    //应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器  
    //用来设置WebDataBinder  
    @InitBinder("user")
    public void initBinder(WebDataBinder binder) {
    }    
    
    // (3)全局异常处理
    //应用到所有@RequestMapping注解的方法,在其抛出Exception异常时执行  
    //定义全局异常处理,value属性可以过滤拦截指定异常,此处拦截所有的Exception  
    @ExceptionHandler(Exception.class)    
    @ResponseBody
    public String handleException(Exception e) {    
        return "error";
    }    
}  

@ModelAttribute:在Model上设置的值,对于所有被 @RequestMapping 注解的方法中,都可以通过 ModelMap 获取,如下:

@RequestMapping("/home")
public String home(ModelMap modelMap) {
    System.out.println(modelMap.get("author"));
}

//或者 通过@ModelAttribute获取

@RequestMapping("/home")
public String home(@ModelAttribute("author") String author) {
    System.out.println(author);
}

7、Web原生组件注入(Servlet、Filter、Listener)

1.使用Servlet API

@WebServlet、@WebFilter、@WebListener +@ServletComponentScan

//urlPatterns = "/my" 处理的路径,效果:直接响应,没有经过Spring的拦截器
@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.getWriter().write("666");
    }
}
//拦截静态资源路径
@WebFilter(urlPatterns = {"/css/*","/images/*"})
@Slf4j
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("MyFilter初始化完成");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        log.info("MyFilter工作");
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        log.info("MyFilter销毁");
    }
}
@WebListener
@Slf4j
public class MyServletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContextListener.super.contextInitialized(sce);
        log.info("MyServletContextListener监听到项目初始化完成");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        log.info("MyServletContextListener监听到项目销毁");
    }
}
//主启动类标注@ServletComponentScan注解,指定原生Servlet组件都放在那里
@SpringBootApplication
@ServletComponentScan
public class PracticeDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(PracticeDemoApplication.class, args);
    }
}

2.使用RegistrationBean

ServletRegistrationBean, FilterRegistrationBean, and ServletListenerRegistrationBean

@Configuration
public class MyRegistConfig {

    @Bean
    public ServletRegistrationBean myServlet(){
        MyServlet myServlet = new MyServlet();

        return new ServletRegistrationBean(myServlet,"/my","/my02");
    }


    @Bean
    public FilterRegistrationBean myFilter(){

        MyFilter myFilter = new MyFilter();
//        return new FilterRegistrationBean(myFilter,myServlet());
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
        return filterRegistrationBean;
    }

    @Bean
    public ServletListenerRegistrationBean myListener(){
        MySwervletContextListener mySwervletContextListener = new MySwervletContextListener();
        return new ServletListenerRegistrationBean(mySwervletContextListener);
    }
}

@WebServlet(urlPatterns = “/my”),为什么没有经过Spring的拦截器?

对于多个Servlet都能处理到同一层路径,根据精确优选匹配原则

A: /

B: /my/

B会执行,会由原生的servlet(Tomcat处理),而只有由DispatcherServlet(Spring)处理的请求才会经过spring的拦截器。

执行顺序:监听器>filter>自定义servlet>dispatchServlet(springMvc)

8、定制化原理

1.定制化的常见方式

  • 修改配置文件;
  • xxxxxCustomizer(定制化器,可以改变xxxx的默认规则);
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;

@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(9000);
    }
}
  • 编写自定义的配置类 xxxConfiguration;+@Bean替换、增加容器中默认组件;
  • Web应用 编写一个配置类实现WebMvcConfigurer 即可定制化web功能;+ @Bean给容器中再扩展一些组件
@Configuration
public class AdminWebConfig implements WebMvcConfigurer
  • @EnableWebMvc + WebMvcConfigurer —— @Bean 可以全面接管SpringMVC,所有规则全部自己重新配置,实现定制和扩展功能;

    原理:

    • WebMvcAutoConfiguration 默认的SpringMVC的自动配置功能类。静态资源、欢迎页…
    • 一旦使用 @EnableWebMvc ,会 @Import(DelegatingWebMvcConfiguration.class)
    • DelegatingWebMvcConfiguration 的 作用,只保证SpringMVC最基本的使用
      • 把所有系统中的 WebMvcConfigurer 拿过来。所有功能的定制都是这些 WebMvcConfigurer 合起来一起生效
      • 自动配置了一些非常底层的组件。RequestMappingHandlerMapping、这些组件依赖的组件都是从容器中获取
      • public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
      • WebMvcAutoConfiguration 类上有注解,@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),@EnableWebMvc 会导致了 WebMvcAutoConfiguration 没有生效。

2.原理分析套路

场景starter - >xxxxAutoConfiguration -> 导入xxx组件 -> 绑定xxxProperties – 绑定配置文件项

9、SpringBoot整合MyBatis-Plus

1.MyBatis-Plus

整合:

  • 引入mybatis-plus-boot-starter

    • 引入mybatis-plus后,mybatis-spring-boot-starter无需再引,构成的依赖都包含了

    • mapperLocations 自动配置好的。有默认值。classpath*:/mapper/**/*.xml;类路
      径下的所有mapper文件夹下任意路径下的所有xml都是sql映射文件。

【Java】classpath到底是指什么

  • 配置appHcation.yaml中,可做一些全局配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  • @MapperScan(“com.atguigu.admin.mapper”)会批量扫描@Mapper标注的接口,接口就可以不用标注@Mapper注解

自动配置:

  • MybatisPlusAutoConfiguration 配置类, MybatisPlusProperties 配置项绑定配置文件前缀为mybatis-plus,xxx就是对mybatis-plus的定制
  • SqISessionFactory 自动配置好,底层是容器中默认的数据源
  • 容器中也自动配置好了 SqISessionTemplate
  • @Mapper标注的接口也会被自动扫描

**优点:**只需要我们的Mapper继承 BaseMapper 就可以拥有crud能力

2.mybatis

  • 导入mybatis官方starter
  • 在application.yaml中指定Mapper配置文件的位置,以及指定全局配置文件的信息 (建议;配置在mybatis.configuration)
# 配置mybatis规则
mybatis:
  mapper-locations: classpath:mybatis/mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true		#匹配驼峰命名
  • 简单方法直接注解方式
  • 复杂方法编写mapper.xml进行绑定映射
@Mapper
public interface CityMapper {
    @Select("select *from city where id=#{id}")
    public City getById( Long id);
}

3.MyBatis-Plus分页使用

分页插件

@Configuration
@MapperScan("scan.your.mapper.package")
public class MybatisPlusConfig {

    /**
     * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
         // 分页插件
        PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
        // 数据库类型
        paginationInterceptor.setDbType(DbType.POSTGRE_SQL);
        // 溢出总页数后是否进行处理
        paginationInterceptor.setOverflow(false);
        //数据库类型
        interceptor.addInnerInterceptor(paginationInterceptor);
        // 防止全表更新与删除插件
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        return interceptor;
    }

    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.setUseDeprecatedExecutor(false);
    }
}

controller:

@ApiOperation(value = "分页查询老师信息")
    @GetMapping("/pageTeacher/{current}/{limit}")
    public R pageListTeacher(
            @ApiParam(name = "current", value = "当前页", required = true) @PathVariable long current,
            @ApiParam(name = "limit", value = "每页记录数", required = true)@PathVariable long limit){
        Page<EduTeacher> page = new Page<>(current, limit);
        // 把分页结果封装到 page 对象中
        teacherService.page(page, null);
        return R.ok().data("total", page.getTotal()).data("rows", page.getRecords());
    }

springboot~@ConditionalOnMissingBean注解的作用

Mybatis-Plus:分页,简单分页查询&复杂分页查询

10、SpringBoot整合redis

1.redis的自动配置

<dependency>
    <groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot- starter-data-redis</artifactId>
</dependency>

在这里插入图片描述

自动配置:

  • RedisAutoConfiguration 自动配置类。RedisProperties 属性类 --> spring.redis.xxx是对redis
    的配置
  • 连接工厂是准备好的。导入了LettuceConnectionConfiguration、JedisConnectionConfiguration配置类
  • 自动注入了RedisTemplate<Object, Object> : xxxTemplate;
  • 自动注入了StringRedisTemplate; k: v都是String
  • 底层只要我们使用 StringRedisTemplate、RedisTemplate就可以操作redis

redis环境搭建:

  1. 阿里云搜索redis,立即购买
  2. 阿里云按量付费redis、经典网络、读写分离板
  3. 申请redis的公网连接地址
  4. 修改白名单 允许0.0.0.0/0 访问,即允许所有机器访问
  5. 可以分配账号
  6. 用完后进行资源释放

2.RedisTemplate与Lettuce

spring:
   # redis 配置
  redis:
    host: law.internal.gridsumdissector.com
    port: 34490
    # 数据库索引
    database: 0
    # 密码
    password: Zsydian88
    # 连接超时时间
    timeout: 10s
    lettuce:
      pool:
        # 连接池中的最小空闲连接
        min-idle: 0
        # 连接池中的最大空闲连接
        max-idle: 8
        # 连接池的最大数据库连接数
        max-active: 8
        # #连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms
@Test
void testRedis() {
    redisTemplate.opsForValue().set("hello","world");
    String hello = redisTemplate.opsForValue().get("hello");
    System.out.println(hello);
}

3.切换至jedis

<!--导入jedis-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
spring:
   # redis 配置
  redis:
    host: law.internal.gridsumdissector.com
    port: 34490
    # 数据库索引
    database: 0
    # 密码
    password: Zsydian88
    client-type: jedis
    jedis:
      pool:
        # 连接池的最大数据库连接数
        max-active: 8

4.使用redis统计接口访问次数

@Component
public class RedisUrlCountInterceptor implements HandlerInterceptor {
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String url = String.valueOf(request.getRequestURL());
        //默认每次访问当前url就会计数+1
        stringRedisTemplate.opsForValue().increment(url);
        return true;
    }
}
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
     /**
     * Filter、Interceptor的区别:
     * 1、Filter是Servlet定义的原生组件。好处,脱离Spring应用也能使用
     * 2、Interceptor是Spring定义的接口。可以使用Spring的自动装配等功能
     */	
    @Autowired
    RedisUrlCountInterceptor urlCountInterceptor;

     @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //直接new,自动注入stringRedisTemplate不会生效,因为只有容器的中组件Spring才会解析
        //registry.addInterceptor(new RedisUrlCountInterceptor());
        //从容器中拿
        registry.addInterceptor(urlCountInterceptor)
                .addPathPatterns("/**")
                // 放行静态资源
                .excludePathPatterns("/css/**","/js/**","/images/**","/html/**");
    }
}
//获取接口访问次数
@Test
void testGetUrlCount(){
    String s = stringRedisTemplate.opsForValue().get("/addOrUpdateTag");
    System.out.println(Long.parseLong(s));
}

11、单元测试

1.JUnit5 的变化

Spring Boot 2.2.0 版本开始引入 JUnit 5 作为单元测试默认库

JUnit 5由三个不同子项目的几个不同模块组成。

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

JUnit Platform: Junit Platform是在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入。
JUnit Jupiter: JUnit jupiter提供了JUnit5的新的编程模型,是jUnit5新特性的核心。内部包含了一个测试引擎,用于在junit Platform上运行。
JUnit Vintage: 由于JUint已经发展多年,为了照顾老的项目,JUnit Vintage提供了兼容JUnit4.x,Junit3.x的测试引擎。

<dependency>
    < groupId >org.springframework.boot </groupId >
    < artifactId > spring- boot -starter-test </artifactId >
    < scope>test </scope>
</dependency>

注意:
SpringBoot 2.4以上版本移除了默认对 Vintage 的依赖。如果需要兼容junit4需要自行引入

<dependency>
    < gnoupId >org.junit.vintage</groupId >
    < antifactld >junit-vintage-engine</artifactld >
    < scope>test </scope>
    <exclusions>
        < exclusion>
        < groupId >org.hamcrest </groupId >
        < artifactId > hamcrest-core</artifactId >
        </exclusion >
    </exclusions>
</dependency>
@SpringBootTest
class Boot05WebAdminApplicationTests {
    @Test
    void contextLoads(){
    }
}

以前:
@SpringBootTest + @RunWith(SpringTest.class)

SpringBoot整合junit以后。

  • 编写测试方法:@Test标注(注意需要使用junit5版本的注解)
  • Junit类具有Spring的功能, @Autowired、比如 @Transactional 标注测试方法,测试完成后自
    动回滚

2.JUnit5常用注解

https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations

@Test:表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由jupiter提供额外测试
@ParameterizedTest:表示方法是参数化测试
@RepeatedTest :表示方法可重复执行
@DisplayName:为测试类或者测试方法设置展示名称
@BeforeEach:表示在每个单元测试之前执行
@AfterEach:表示在每个单元测试之后执行
@BeforeAII:表示在所有单元测试之前执行
@AfterAII:表示在所有单元测试之后执行
@Tag:表示单元测试类别,类似于JUnit4中的@Categories
@Disabled:表示测试类或测试方法不执行,类似于JUnit4中的@lgnore

@Timeout:表示测试方法运行如果超过了指定时间将会返回错误
@ExtendWith:为测试类或测试方法提供扩展类引用

import org.junit.jupiter.api.Test; //注意这里使用的是jupiter的Test注解!!
@DisplayName( "JUnit5的功能测试类"public class TestDemo {
    @Test
    @DisplayName( "第一次测试"public void firstTest() {
       System.out.printIn("hello world");
	}
      
    // 规定方法超时时间。超出时间测试出异常           
    @Timeout(value = 500,unit = TimeUnit.MILLISECONDS) 
    @Test
	void testTimeout() throws InterruptedException {
        Thread.sleep( millis: 600);
	}             
}
//@BootstrapWith(SpringBootTestContextBootstrapper.class)
//@ExtendWith(SpringExtension.class)
@SpringBootTest		//包含上面两个注解
public class TestDemo {
    @Test
    public void firstTest() {
       System.out.printIn("hello world");
}

3.断言(assertions)

简单断言

方法说明
assertEquals判断两个对象或两个原始类型是否相等
assertNotEquals判断两个对象或两个原始类型是否不相等
assertSame判断两个对象弓I用是否指向同一个对象
assertNotSame判断两个对象弓I用是否指向不同的对象
assertTrue判断给定的布尔值是否为 true
assertFalse判断给定的布尔值是否为 false
assertNull判断给定的对象弓I用是否为 null
assertNotNull判断给定的对象引用是否不为 null

在这里插入图片描述

数组断言

在这里插入图片描述

组合断言

assertAII 方法接受多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易的提供这些断言

在这里插入图片描述

异常断言

在这里插入图片描述

超时断言

在这里插入图片描述

快速失败

在这里插入图片描述

4.前置条件(assumptions)

JUnit 5 中的前置条件 (assumptions【假设】) 类似于断言,不同之处在于不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止。前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。

在这里插入图片描述

5.嵌套测试

JUnit 5 可以通过 java 中的内部类和@Nested 注解实现嵌套测试,从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用@BeforeEach 和@AfterEach 注解,而且嵌套的层次没有限制。

6.参数化测试

@ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
@Nu11Source: 表示为参数化测试提供一个nu11的入参
@EnumSource: 表示为参数化测试提供一个枚举入参
@CsvFileSource: 表示读取指定CSV文件内容作为参数化测试入参
@MethodSource: 表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)

在这里插入图片描述

12、Profile功能

1.pplication-profile功能

  • 默认配置文件 application.yaml; 任何时候都会加载

  • 指定环境配置文件 application-{env}.yaml

  • 激活指定环境

    • 通过配置文件激活,在application.yml中
    Spring:
      profiles:
        active: prod
    
    • 命令行激活:java -jar xxx.jar -spring.profiles.active=prod —person.name=haha
      • 修改配置文件的任意值,命令行优先
  • 默认配置与环境配置同时生效

  • 同名配置项,profile配置优先

2.@Profile条件装配功能

@Profile标注在类上

public interface Person {
    String getName();

    Integer getAge();
}
@Profile("prod")
@Data
@Component
@ConfigurationProperties("person")
public class Boss implements Person{

    private String name;
    private Integer age;
}
@Profile("test")
@Data
@Component
@ConfigurationProperties("person")
public class Worker implements Person{
    private String name;
    private Integer age;
}

若当前环境是test,则Worker对象生效,若当前环境是prod,则Boss对象生效。

@Profile标注在方法上,指定的环境,对应的方法组件会生效

@Configuration
public class MyConfig {

    @Profile("test")
    @Bean
    public Color red(){
        return Color.RED;
    }

    @Profile("prod")
    @Bean
    public Color blue(){
        return Color.BLUE;
    }
}

3.profile分组

spring.profiles.group.production[0]=proddb
spring.profiles.group.production[l]=prodmq

使用: spring,profiles.active=production 激活

4.外部化配置

Core Features (spring.io)

  1. 外部配置源
    常用:Java properties 文件、YAML文件、环境变量、命令行参数;

(环境变量、命令行参数):系统环境变量,java -jar xxx.jar --spring.application.name=xxx

@Value("MAVEN_HOME")
private String msg;
@SpringBootApplication
public class SupervisionApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(SupervisionApplication.class, args);
        ConfigurableEnvironment environment = run.getEnvironment();
        //系统的环境变量
        Map<String, Object> systemEnvironment = environment.getSystemEnvironment();
        //系统的属性
        Map<String, Object> systemProperties = environment.getSystemProperties();
        System.out.println(systemEnvironment);
        System.out.println(systemProperties);
    }
}
  1. 配置文件查找位置(后面的可以覆盖前面的同名配置项

Core Features (spring.io)Core Features (spring.io)

​ (1) classpath 根路径(源码包标识的都叫,java、resource)

​ (2) classpath 根路径下config目录

​ (3) jar包当前目录

​ ⑷ jar包当前目录的config目录

​ (5) /config子目录的直接子目录(在linux系统下验证)

  1. 配置文件加载顺序(指定环境优先,外部优先,后面的可以覆盖前面的同名配置项
    1. 当前jar包内部的的application.properties和application.yml
    2. 当前jar包内部的的application-{profile}.properties 和 application-{profile}.yml
    3. 引用的外部jar包的application.properties和application.ym
    4. 引用的外部jar包的application-{profile}.properties 和 application-{profile}.yml

13.自定义starter

高级特性-自定义starter细节

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值