SpringBoot核心功能,配置文件、Web开发、单元测试

一、配置文件

1. properties文件

properties文件的用法比较熟悉,这里就不过多介绍

2. yaml文件

2.1 简介

  • YAML是“YAML Ain`t Markup Language”(是一种标记语言)的递归缩写。
  • 适合用来做以数据为中心的配置文件

2.2 基本语法

  • key: value;k v之间有一个空格
  • 大小写敏感
  • 使用缩进进行层级关系
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • # 表示注释

2.3 数据类型

  1. 字面量:当个、不可分的值。date、Boolean、String、number、null

    K: v
    
  2. 对象:键值对的集合。map、hash、set、Object

    # 行内写法
    k: {k1:v1,k2:v2,k3:v3}
    k: 
      k1: v1
      k2: v2
      k3: v3
    
  3. 数组:一组按次序排列的值。array、list、queue

    # 行内写法
    k: [v1,v2,v3]
    k:
      - v1
      - v2
      - v3
    

二、Web开发

1. 简单功能

1.1 静态资源访问

  1. 类路径下:called:/static(or /publicor /resourcesor /META-INF/resources)

  2. 静态资源放到以上目录下,可以通过根目录加静态资源名的方式直接访问

  3. 静态资源会映射/**,当请求进来时,先去Controller看是否能够处理,若不能处理则将请求交给静态资源处理。当静态资源也不能处理时则会报404。

  4. 静态资源访问前缀和静态资源路径配置

    spring:
      mvc:
        # 静态资源的访问前缀路径
        static-path-pattern: /res/**
      web:
        resources:
          # 静态资源的文件夹位置
          static-locations: classpath:/test
    

1.2 欢迎页和Favicon

  1. 欢迎页:默认访问静态资源路径下的index.html文件
  2. Favicon:favicon.ico放在静态资源路径下即可

1.3 静态资源配置原理

  1. SpringBoot启动默认加载xxxAutoConfiguration类(自动配置类)

  2. SpringMVC功能的自动配置类WebMvcAutoConfiguration生效

    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnWebApplication(
        type = Type.SERVLET
    )
    @ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
    @ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
    @AutoConfigureOrder(-2147483638)
    @AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
    public class WebMvcAutoConfiguration {......}
    
  3. 给容器中配置的内容

    @Configuration(
        proxyBeanMethods = false
    )
    @Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
    @EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class, WebProperties.class})
    @Order(0)
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {......}
    
  4. 配置文件相关属性和xxx进行了绑定,WebMvcProperties,ResourceProperties,WebProperties

    @ConfigurationProperties(
        prefix = "spring.mvc"
    )
    public class WebMvcProperties {......}
    
    @Deprecated
    @ConfigurationProperties(
        prefix = "spring.resources",
        ignoreUnknownFields = false
    )
    public class ResourceProperties extends Resources {......}
    
    @ConfigurationProperties("spring.web")
    public class WebProperties {......}
    
  5. 资源处理的默认规则

    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        if (!this.resourceProperties.isAddMappings()) {
            logger.debug("Default resource handling disabled");
        } else {
            this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
            this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
                registration.addResourceLocations(this.resourceProperties.getStaticLocations());
                if (this.servletContext != null) {
                    ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
                    registration.addResourceLocations(new Resource[]{resource});
                }
    
            });
        }
    }
    
    private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, String... locations) {
        this.addResourceHandler(registry, pattern, (registration) -> {
            registration.addResourceLocations(locations);
        });
    }
    
    private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, Consumer<ResourceHandlerRegistration> customizer) {
        if (!registry.hasMappingForPattern(pattern)) {
            ResourceHandlerRegistration registration = registry.addResourceHandler(new String[]{pattern});
            customizer.accept(registration);
            registration.setCachePeriod(this.getSeconds(this.resourceProperties.getCache().getPeriod()));
            registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());
            registration.setUseLastModified(this.resourceProperties.getCache().isUseLastModified());
            this.customizeResourceHandlerRegistration(registration);
        }
    }
    
  6. 资源配置的默认值

    public static class Resources {
        private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
        private String[] staticLocations;
        ......
        }
    
  7. 欢迎页的处理规则

    //HandlerMapping:处理器映射,保存了每一个Handler能处理哪些请求
    @Bean
    public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
        WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
        welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
        welcomePageHandlerMapping.setCorsConfigurations(this.getCorsConfigurations());
        return welcomePageHandlerMapping;
    }
    
    //欢迎页的处理逻辑
    final class WelcomePageHandlerMapping extends AbstractUrlHandlerMapping {
    
    	private static final Log logger = LogFactory.getLog(WelcomePageHandlerMapping.class);
    
    	private static final List<MediaType> MEDIA_TYPES_ALL = Collections.singletonList(MediaType.ALL);
    
    	WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,
    			ApplicationContext applicationContext, Resource welcomePage, String staticPathPattern) {
    		if (welcomePage != null && "/**".equals(staticPathPattern)) {
    			logger.info("Adding welcome page: " + welcomePage);
    			setRootViewName("forward:index.html");
    		}
    		else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {
    			logger.info("Adding welcome page template: index");
    			setRootViewName("index");
    		}
    	}
        ......
    }
    

2. 请求参数处理

2.1 常用注解

@PathVariable 路径变量(取路径中的值)
@RequestHeader 获取请求头
@RequestParam 获取请求参数(指问号后的参数,url?a=1&b=2@CookieValue 获取Cookie@RequestAttribute 获取request域属性
@RequestBody 获取请求体[POST]
@MatrixVariable 矩阵变量
@ModelAttribute

3. 响应数据

  1. 响应页面
  2. 响应数据
    1. json
    2. xml
    3. 音视频
    4. 自定义协议数据等

3.1 响应JSON

  1. Jackson.jar+@ResponseBody

  2. 导入web模块依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    	<!--web starter中包含了JSON相关依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
            <version>2.5.3</version>
            <scope>compile</scope>
        </dependency>
    
  3. 测试

    @Controller
    @Slf4j
    public class HelloController {
    
        @ResponseBody
        @GetMapping(value = "/user")
        public User getUser() {
            User user = new User();
            user.setAge(18);
            user.setName("Tom");
            log.info(user.toString());
            return user;
        }
    }
    

4. 拦截器

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
    }
}
  1. 编写拦截器实现HandlerInterceptor接口

  2. 拦截器注册到容器中(实现WebMvcConfigurer的addInterceptor)

  3. 编写拦截规则

    /**
     * @className: LoginInterceptor
     * @description: TODO
     * @version: 1.0
     * 1. 配置好拦截器需要拦截的请求
     * 2,将这些配置放在容器中
     */
    public class LoginInterceptor implements HandlerInterceptor {
    
        /**
         * @return boolean
         * @description 目标方法执行之前
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            HttpSession session = request.getSession();
            Object loginUser = session.getAttribute("loginUser");
            //返回true表示放行
            if (!Objects.isNull(loginUser)) {
                return true;
            }
            request.setAttribute("msg", "请先登录");
            request.getRequestDispatcher("/").forward(request, response);
            return false;
        }
    
        @Override
        //目标方法执行之后
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
        }
    
        @Override
        //页面渲染之后
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        }
    }
    
    //配置拦截器
    /**
     * @className: AdminWebConfig
     * @description: TODO
     * @version: 1.0
     */
    @Configuration
    public class AdminWebConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new LoginInterceptor())
                    //拦截的路径
                    .addPathPatterns("/**")
                    //放行的路径
                    .excludePathPatterns("/", "/login");
        }
    }
    

6. 文件上传

  1. 代码测试
@RestController
@Slf4j
public class FileController {

    /**
     * @return java.util.Map<java.lang.String, java.lang.String>
     * @description 文件上传
     * [email, username, image, photos]
     */
    @PostMapping(value = "/upload")
    public Map<String, String> upload(@RequestParam("email") String email,
                                      @RequestParam("username") String username,
                                      @RequestParam("image") MultipartFile image,
                                      @RequestParam("photos") MultipartFile[] photos) throws IOException {
        log.info("上传的数据:email={},username={},image={},photos={}", email, username, image.getSize(), photos.length);
        if (!image.isEmpty()) {
            String originalFilename = image.getOriginalFilename();
            image.transferTo(new File("F:\\test\\" + originalFilename));
        }
        if (photos.length > 0) {
            for (MultipartFile photo : photos) {
                if (!photo.isEmpty()) {
                    String originalFilename = photo.getOriginalFilename();
                    photo.transferTo(new File("F:\\test\\" + originalFilename));
                }
            }
        }
        HashMap<String, String> res = new HashMap<>();
        res.put("msg", "ok");
        res.put("code", "200");
        return res;
    }
}
  1. 配置文件

    spring:
      servlet:
        multipart:
          # 文件上传的大小
          max-file-size: 10MB
          # 整体文件上传的大小
          max-request-size: 100MB
    

7. 异常处理

  1. 定义基础接口

    /**
     * @interfaceName: BaseErrorInfoInterface
     * @description: TODO
     * @date: 2021/10/15 16:30
     * @version: 1.0
     */
    public interface BaseErrorInfoInterface {
    
        /**
         * 错误码
         */
        String getResultCode();
    
        /**
         * 错误信息
         */
        String getMsg();
    }
    
  2. 定义异常枚举类

    public enum ExceptionEnum implements BaseErrorInfoInterface {
    
        SUCCESS("200", "成功"),
    
        NOT_FOUND("404", "未找资源"),
    
        SERVER_ERROR("500", "服务器内部异常");
    
        /**
         * 错误码
         */
        private final String code;
    
        /**
         * 错误信息
         */
        private final String msg;
    
        ExceptionEnum(String code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    
        @Override
        public String getResultCode() {
            return code;
        }
    
        @Override
        public String getMsg() {
            return msg;
        }
    }
    
  3. 自定义异常

    @Data
    public class MyDefinedException extends RuntimeException {
    
        private static final long serialVersionUID = 2L;
    
        protected String errorCode;
    
        protected String errorMsg;
    
        public MyDefinedException() {
            super();
        }
    
        public MyDefinedException(BaseErrorInfoInterface baseErrorInfoInterface) {
            super(baseErrorInfoInterface.getResultCode());
            this.errorCode = baseErrorInfoInterface.getResultCode();
            this.errorMsg = baseErrorInfoInterface.getMsg();
        }
    
        public MyDefinedException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) {
            super(errorInfoInterface.getResultCode(), cause);
            this.errorCode = errorInfoInterface.getResultCode();
            this.errorMsg = errorInfoInterface.getMsg();
        }
    
        public MyDefinedException(String errorMsg) {
            super(errorMsg);
            this.errorMsg = errorMsg;
        }
    
        public MyDefinedException(String errorCode, String errorMsg) {
            super(errorCode);
            this.errorCode = errorCode;
            this.errorMsg = errorMsg;
        }
    
        public MyDefinedException(String errorCode, String errorMsg, Throwable cause) {
            super(errorCode, cause);
            this.errorCode = errorCode;
            this.errorMsg = errorMsg;
        }
    
        @Override
        public Throwable fillInStackTrace() {
            return this;
        }
    }
    
  4. 自定义数据传输类

    /**
     * @className: ResultResponse
     * @description: 传输数据
     * @date: 2021/10/15 16:53
     * @version: 1.0
     */
    @Getter
    @Setter
    public class ResultResponse {
    
        private String code;
    
        private String msg;
    
        private Object result;
    
        public ResultResponse() {
        }
    
        public ResultResponse(BaseErrorInfoInterface errorInfoInterface) {
            this.code = errorInfoInterface.getResultCode();
            this.msg = errorInfoInterface.getMsg();
        }
    
        public static ResultResponse success() {
            return success(null);
        }
    
        public static ResultResponse success(Object data) {
            ResultResponse res = new ResultResponse();
            res.setCode(ExceptionEnum.SUCCESS.getResultCode());
            res.setMsg(ExceptionEnum.SUCCESS.getMsg());
            res.setResult(data);
            return res;
        }
    
        public static ResultResponse error(BaseErrorInfoInterface errorInfo) {
            ResultResponse res = new ResultResponse();
            res.setMsg(errorInfo.getMsg());
            res.setCode(errorInfo.getResultCode());
            res.setResult(null);
            return res;
        }
    
        public static ResultResponse error(String code, String msg) {
            ResultResponse res = new ResultResponse();
            res.setCode(code);
            res.setMsg(msg);
            res.setResult(null);
            return res;
        }
    
        @Override
        public String toString() {
            return JSON.toJSONString(this);
        }
    }
    
  5. 全局异常处理

    /**
     * @className: GlobalExceptionHandler
     * @description: 全局异常处理
     * @date: 2021/10/15 17:05
     * @version: 1.0
     */
    @ControllerAdvice
    @Slf4j
    public class GlobalExceptionHandler {
    
        /**
         * @return com.gridsum.springboot.common.ResultResponse
         * @description 自定义业务异常
         * @date 17:15 2021/10/15
         */
        @ExceptionHandler(value = MyDefinedException.class)
        @ResponseBody
        public ResultResponse myDefinedExceptionHandler(HttpServletRequest request, MyDefinedException e) {
            log.error("业务异常:{}", e.getErrorMsg());
            return ResultResponse.error(e.getErrorCode(), e.getErrorMsg());
        }
    
        /**
         * @return com.gridsum.springboot.common.ResultResponse
         * @description 空指针
         * @date 17:15 2021/10/15
         */
        @ExceptionHandler(value = NullPointerException.class)
        @ResponseBody
        public ResultResponse exceptionHandler(HttpServletRequest req, NullPointerException e) {
            log.error("空指针异常:{}", e.getMessage());
            return ResultResponse.error("501", "空指针异常");
        }
    
        /**
         * @return com.gridsum.springboot.common.ResultResponse
         * @description 其他异常
         * @date 17:15 2021/10/15
         */
        @ExceptionHandler(value = Exception.class)
        @ResponseBody
        public ResultResponse exceptionHandler(HttpServletRequest req, Exception e) {
            log.error("服务器内部异常:{}", e.getMessage());
            return ResultResponse.error(ExceptionEnum.SERVER_ERROR);
        }
    }
    

三、数据访问

1. SQL

1.1 配置数据源

  1. 导入jdbc场景

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jdbc</artifactId>
    </dependency>
    
  2. 导入数据库驱动

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.26</version>
    </dependency>
    
  3. 修改配置项

      datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/db_test
        username: root
        password: xxxxxx
    

2. NoSQL

  1. 引入依赖

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

    spring:
      redis:
        host: xxx
        port: 6379
        password: xxxxx
        client-type: jedis
    

四、单元测试

  1. 引入依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    
  2. 常用注解

    • @Test:表示方法是测试方法。与JUnit 4的@Test注释不同,这个注释不声明任何属性,因为JUnit Jupiter中的测试扩展基于它们自己的专用注释进行操作。
    • @ParameterizedTest:表示方法是参数化测试。
    • @RepeatedTest:表示方法是重复测试的测试模板
    • @TestFactory:表示方法是动态测试的测试工厂。
    • @TestInstance:用于为带注释的测试类配置测试实例生命周期。
    • @TestTemplate:表示方法是为测试用例设计的模板,根据注册提供程序返回的调用上下文的数量进行多次调用。
    • @DisplayName:声明测试类或测试方法的自定义显示名称。
    • @BeforeEach:表示在当前类中每个@Test、@RepeatedTest、@ParameterizedTest或@TestFactory方法之前执行注释的方法;类似于JUnit 4的@Before。
    • @AfterEach:表示在当前类中的每个@Test、@RepeatedTest、@ParameterizedTest或@TestFactory方法之后,都应该执行带注释的方法;类似于JUnit 4的@After。
    • @BeforeAll:表示应在当前类中的所有@Test、@RepeatedTest、@ParameterizedTest和@TestFactory方法之前执行带注释的方法;类似于JUnit 4的@BeforeClass。
    • @AfterAll:表示在当前类中,所有@Test、@RepeatedTest、@ParameterizedTest和@TestFactory方法都应该执行注释的方法;类似于JUnit 4的@AfterClass。
    • @Nested:表示带注释的类是一个嵌套的、非静态的测试类。@BeforeAll和@AfterAll方法不能直接在 @Nested 测试类中使用,除非使用“每个类”测试实例生命周期。
    • @Tag:用于在类或方法级别声明过滤测试的标记;类似于TestNG中的测试组或JUnit 4中的类别。
    • @Disabled:用于禁用测试类或测试方法;类似于JUnit 4的@Ignore。
    • @ExtendWith:用于注册自定义扩展。

五、其他特性

1. 环境切换

  1. application-profile功能

    1. 默认配置文件:application.yaml;任何时候都会加载
    2. 指定环境配置文件 application-{env}.yaml
    3. 激活指定环境
      1. 配置文件激活
      2. 命令行激活
    4. 默认配置和环境配置同时生效
    5. 同名配置项,profile配置优先
  2. 配置

    • application.yaml文件
    spring:  
      profiles:
        # 指定激活的环境
        active: prod
    
    • application-prod.yaml文件
    # 配置端口号
    server:
      port: 8080
    
  3. 通过命令行切换环境

    java -jar [项目名称] --spring.profiles.active=[环境]
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ysw!不将就

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值