CategorySpringBoot article

Spring Boot 与 Web 开发

  1. SpringMVC 快速使用

    • 基于 restful http 接口的 CURD

      @RestController
      @RequestMapping("/user")
      public class UserController {
      
          @Autowired
          UserService userService;
      
          @GetMapping("/{id}")
          public Result getUser(@PathVariable Integer id) {
              User user = userService.getUserById(id);
              return new Result<>(200, "查询成功", user);
          }
      
          @PostMapping("/add")
          public Result getUser(User user) {
              userService.add(user);
              return new Result<>(200, "添加成功");
          }
      
          @PutMapping("/{id}")
          public Result editUser(User user) {
              userService.update(user);
              return new Result(200, "修改成功");
          }
      
          @DeleteMapping("/{id}")
          public Result deleteUser(@PathVariable Integer id) {
              userService.delete(id);
              return new Result<>(200, "删除成功");
          }
      }
    • 调用 rest http 接口

      • 通过 restTemplate 调用

        RestTemplate 类可用于在应用中调用 rest 服务,它简化了与 http 服务的通信方式,统一了 RESTful 的标准,封装了 http 链接,我们只需要传入 url 及返回值类型即可。

        适用于微服务架构下,服务之间的远程调用。 ps:以后使用微服务架构,使用 spring cloud feign 组件

        restTemplate 和 webClient 都可以调用远程服务,区别:webclient 依赖 webflux,webclient 请求远程服务是无阻塞的,响应的。RestTemplate 是阻塞的,需要等待请求响应后才能执行下一句代码。

        DELETEdelete
        GETgetForObject 按照指定 Class 返回对象
        getForEntity 返回对象为 ResponseEntity 对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等。
        HEADheadForHeaders
        OPTIONSoptionForAllow
        POSTpostForLocaltion
        postForObject
        PUTput
        any
        支持任何请求方法类型
        exchange
        execute
        • 基于 restTemplate 调用查询

          // 声明了 RestTemplate
          private final RestTemplate restTemplate;
          
          // 当 Bean 没有无参构造函数的时候, spring 将自动拿到有参的构造函数,参数进行自动注入
          public OrderController(RestTemplateBuilder restTemplateBuilder) {
              this.restTemplate = restTemplateBuilder.build();
          }
          
          @RequestMapping("/order")
          public String order() {
              Result forObject = restTemplate.getForObject("http://localhost:8080/user/{id}", Result.class, 1);
          
              return forObject.toString();
          }
        • 基于 restTemplate 调用查询

          @RequestMapping("/order")
          public String order() {
              //基于 restTemplate 调用查询
              User user = new User("AZhang", "wyu");
              // url: 请求的远程 rest url
              // object: post 请求的参数
              // Class<T>: 返回的类型
              // ...Object: 是 @PathVariable 占位符的参数
              ResponseEntity<Result> resultResponseEntity = restTemplate.postForEntity("http://localhost:8080/user/add", user, Result.class);
              System.out.println(resultResponseEntity);
              return resultResponseEntity.getBody().toString();
          }
        • 基于 restTemplate 调用修改

          @RequestMapping("/order")
          public String order() {
              //基于 restTemplate 调用修改
              User user = new User(1, "AZhang", "wyu");
              //restTemplate.put("http://localhost:8080/user/{id}", user, Result.class);
              HttpEntity<User> httpEntity = new HttpEntity<>(user);
              ResponseEntity<Result> resultResponseEntity = restTemplate.exchange("http://localhost:8080/user/{id}", HttpMethod.PUT, httpEntity, Result.class, 1);
              System.out.println(resultResponseEntity);
              return resultResponseEntity.getBody().toString();
          }
        • 基于 restTemplate 调用删除

          @RequestMapping("/order")
          public String order() {
              //基于 restTemplate 调用删除
              ResponseEntity<Result> resultResponseEntity = restTemplate.exchange("http://localhost:8080/user/{id}", HttpMethod.DELETE, null, Result.class, 1);
              System.out.println(resultResponseEntity);
              return resultResponseEntity.getBody().toString();
          }
     

 - 通过 postman 调用

 - 通过 MockMvc 测试

   MockMvc 是由 spring-test 包提供,实现了对 Http 请求的模拟,能够直接使用网络的形式,转换到 Controller 的调用,使得测试速度快、不依赖网络环境。同时提供了一套验证的工具,结果的验证十分方便。
  1. SpringMVC 自动配置原理分析

    ​ Spring Boot 为 Spring MVC 提供了自动配置,可与大多数应用程序完美配合。

    自动配置在 Spring 的默认值之上添加了以下功能:

    • 包含 ContentNegotiatingViewResolverBeanNameViewResolver

      • ViewResolver 都是 SpringMVC 内置的视图解析器

        • ContentNegotiatingViewResolver

          • 不会解析视图,而是委派给其他视图解析器进行解析。
          • 所有的视图解析器,都会根据返回的视图名称进行解析视图 resolveViewName

            public View resolveViewName(String viewName, Locale locale) throws Exception {
                RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
                Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
                List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
                if (requestedMediaTypes != null) {
                    // 获得所有匹配的视图
                    List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
                    // 获取最终的这个
                    View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
                    if (bestView != null) {
                        return bestView;
                    }
                }

            委派给其他视图解析器进行解析:

            @Override
            protected void initServletContext(ServletContext servletContext) {
                Collection<ViewResolver> matchingBeans =
                        BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), ViewResolver.class).values();
                if (this.viewResolvers == null) {
                    this.viewResolvers = new ArrayList<>(matchingBeans.size());
                    for (ViewResolver viewResolver : matchingBeans) {
                        if (this != viewResolver) {
                            this.viewResolvers.add(viewResolver);
                        }
                    }
                }
        • BeanNameViewResolver

          • 会根据 handler 方法返回的视图名称,去 IOC 容器中找到名字叫 azhang 的一个 Bean,并且这个 Bean要实现了 View接口。
          • 示例:

            @GetMapping("/test")
            public String getUser() {
                return "azhang";
            }

            可以配置一个名字叫 azhang 的视图(View)

            @Component
            public class Azhang implements View {
                @Override
                public String getContentType() {
                    return "text/html";
                }
            
                @Override
                public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
                    response.getWriter().print("Welcome to AzhangView");
                }
            }
       

   - 由以上代码可以得出结论,它是从 Spring IOC 容器获得 ViewResolve 类型 Bean,name我们可以自己定制一个 ViewResolver,ContentNegotiatingViewResolver 也会帮我们委派解析:

     ```java
     @Bean
     public ViewResolver AZhangViewResolve() {
         InternalResourceViewResolver resolver = new InternalResourceViewResolver();
         resolver.setPrefix("/");
         resolver.setSuffix(".html");
         return resolver;
     }
     ```

     ![image-20220506164908963](https://static-upyun.hackerjk.top/markdown/image-20220506164908963.png!)
  • 支持提供静态资源,包括对 WebJars 的支持。

    • 以前要访问 jpg / css / js 等这些静态资源文件,需要在 web.xml 配置,但现在在 SpringBoot 中不需要配置,只需要放在约定文件夹中就可以(约定大于配置)
    • 原理:

      • WebJars:就是将静态资源放在 jar 包中进行访问
      • WebJars 官网:https://www.webjars.org/
      • @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            if (!this.resourceProperties.isAddMappings()) {
                logger.debug("Default resource handling disabled");
                return;
            }
            addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
            addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
                registration.addResourceLocations(this.resourceProperties.getStaticLocations());
                if (this.servletContext != null) {
                    ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
                    registration.addResourceLocations(resource);
                }
            });
        }
        
        private void addResourceHandler(ResourceHandlerRegistry registry, String pattern, String... locations) {
            addResourceHandler(registry, pattern, (registration) -> registration.addResourceLocations(locations));
        }
        
        private void addResourceHandler(ResourceHandlerRegistry registry, String pattern,
                Consumer<ResourceHandlerRegistration> customizer) {
            if (registry.hasMappingForPattern(pattern)) {
                return;
            }
            ResourceHandlerRegistration registration = registry.addResourceHandler(pattern);
            customizer.accept(registration);
            registration.setCachePeriod(getSeconds(this.resourceProperties.getCache().getPeriod()));
            registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());
            registration.setUseLastModified(this.resourceProperties.getCache().isUseLastModified());
            customizeResourceHandlerRegistration(registration);
        }
      • 当访问 /webjars/** 时,就会去 classpath:/META-INF/resources/webjars/ 对应进行映射

        当访问 http://localhost:8080/webjars/jquery/3.5.1/jquery.js 对应映射到 /META-INF/resources/webjars/jquery/3.5.1/jquery.js

        image-20220506171959211

        • 在 static 文件中访问的静态资源,默认的四个静态资源路径 (对应的映射地址) :
        { "classpath:/META-INF/resources/",
                "classpath:/resources/",
                "classpath:/static/",
    • 配置首页欢迎页:

      private Resource getWelcomePage() {
          for (String location : this.resourceProperties.getStaticLocations()) {    // 拿到上面静态资源地址
              Resource indexHtml = getIndexHtml(location);
              if (indexHtml != null) {
                  return indexHtml;
              }
          }
          ServletContext servletContext = getServletContext();
          // 去里面找一个 index.html 的首页文件
          if (servletContext != null) {
              return getIndexHtml(new ServletContextResource(servletContext, SERVLET_LOCATION));
          }
          return null;
      }
      
      private Resource getIndexHtml(String location) {
          return getIndexHtml(this.resourceLoader.getResource(location));
      }
      
      private Resource getIndexHtml(Resource location) {
          try {
              Resource resource = location.createRelative("index.html");
              if (resource.exists() && (resource.getURL() != null)) {
                  return resource;
              }
          }
          catch (Exception ex) {
          }
          return null;
      }
    • 也可以通过配置文件指定具体的静态资源地址:

  • 自动注册 ConverterGenericConverterFormatter Bean 类。

    • 使用方式
  • 支持 HttpMessageConverters

    • HttpMessageConverters 负责 http 请求和响应的报文处理。
    • image-20220507011511095
  • 自动注册 MessageCodesResolver

    • 修改 4xx 错误下格式转换出错、类型转换出错的错误代码
    • 以前的格式是 errorCode + "." + object name + "." + field

      例:typeMismatch.user.birthday

    • 可以通过 spring.mvc.message-codes-resolver-format=postfix_error_code 将格式修改为:object name + "." + field + "." + errorCode
  • 静态 index.html 支持。

    • 在 springboot 中可以直接返回 html 的视图,因为在 WebMvcAutoConfiguration 配置类配置了:

      @Bean
      @ConditionalOnMissingBean
      public InternalResourceViewResolver defaultViewResolver() {
          InternalResourceViewResolver resolver = new InternalResourceViewResolver();
          resolver.setPrefix(this.mvcProperties.getView().getPrefix());
          resolver.setSuffix(this.mvcProperties.getView().getSuffix());
          return resolver;
      }

      所以可以通过在配置文件中完成:

      spring.mvc.view.prefix=/pages/
      spring.mvc.view.suffix=.html
  • 自动使用 ConfigurableWebBindingInitializer bean。

    1. 定制 SpringMVC 的自动配置

      SpringMVC 的自动配置类:WebMvcAutoConfiguration

  • 在大多数情况下,SpringBoot 在自动配置类中标记了很多 @ConditionalOnMissingBean(xxxxxxxxxx.class) ;(意思就是如果容器中没有,当前的 Bean 才会生效)。只需要在自己的配置类中配置对应的一个 @Bean 就可以覆盖默认自动配置。
  • 通过 WebMvcConfigurer 进行拓展

    • 扩展视图控制器
    • 扩展拦截器
    • 扩展全局 CORS
    • @Configuration
      public class MyWebMvcConfigurer implements WebMvcConfigurer {
          /*@Override
          public void configurePathMatch(PathMatchConfigurer configurer) {
              configurer.setUseTrailingSlashMatch(false);
          }*/
      
          @Override
          public void addInterceptors(InterceptorRegistry registry) {
              registry.addInterceptor(new TimeInterceptor())      // 添加拦截器
                  .addPathPatterns("/**")      // 拦截映射规则  /**拦截所有的请求
                  .excludePathPatterns("/pages/**");        // 排除 pages 下面所有的请求
          }
      
          /**
           *  CORS 配置
           *  全局跨域请求配置
           */
          /*@Override
          public void addCorsMappings(CorsRegistry registry) {
              registry.addMapping("/user/*")       // 映射服务器中哪些 http 接口允许运行跨域访问
                      .allowedOrigins("http://localhost:8081")                // 配置哪些来源有权限跨域
                      .allowedMethods("GET", "POST", "PUT", "DELETE");        // 配置允许跨域访问的请求方法
          }*/
      
          @Bean
          public InternalResourceViewResolver AZhangViewResolver() {
              InternalResourceViewResolver resolver = new InternalResourceViewResolver();
              resolver.setPrefix("/pages");
              resolver.setSuffix(".html");
              return resolver;
          }
      }
    • WebMvcConfigurer 原理

      实现 WebMvcConfigurer 接口可以扩展 Mvc 实现,又既保留了 SpringBoot 的自动配置

      • WebMvcAutoConfiguration 也有一个实现了 WebMvcConfigurer 的配置类
      • WebMvcAutoConfigurationAdapter 它也是利用这种方式去进行扩展,所以我们通过查看这个类我们发现它帮我们实现了其他不常用的方法,帮助我们进行自动配置,我们只需要定制(拦截器、视图控制器、CORS 等在开发中需要额外定制的功能)

        @Configuration(proxyBeanMethods = false)
        @Import(EnableWebMvcConfiguration.class)
        @EnableConfigurationProperties({ WebMvcProperties.class, WebProperties.class })
        @Order(0)
        public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {
      • 导入了 EnableWebMvcConfiguration

        @Import(EnableWebMvcConfiguration.class)

        EnableWebMvcConfiguration 它的父类上 setConfigurers 使用了 @AutoWired 注解

        • 它会去容器中将所有实现了 WebMvcConfigurer 接口的 Bean 都自动注入进来,添加到 configurers 变量中
      public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
      
          private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
      
          @Autowired(required = false)
          public void setConfigurers(List<WebMvcConfigurer> configurers) {
              if (!CollectionUtils.isEmpty(configurers)) {
                  this.configurers.addWebMvcConfigurers(configurers);
              }
          }
      ```
    
    - 添加到 ``delegates`` 委派器中
    
      ```java
      public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {
          if (!CollectionUtils.isEmpty(configurers)) {
              this.delegates.addAll(configurers);
          }
      }
      ```
    
    - 底层调用 WebMvcConfigurer 对应的方法时,就是去拿到之前注入到 delegates 的 WebMvcConfigurer,依次调用
    
      ```java
      @Override
      public void addInterceptors(InterceptorRegistry registry) {
          for (WebMvcConfigurer delegate : this.delegates) {
              delegate.addInterceptors(registry);
          }
      }
      ```
    
    • 当添加了 @EnableWebMvc 就不会使用 SpringMVC 自动配置类的默认配置了

      原理:

      • 在 EnableMvc 中 @Import(DelegatingWebMvcConfiguration.class)

        @Retention(RetentionPolicy.RUNTIME)
        @Target(ElementType.TYPE)
        @Documented
        @Import(DelegatingWebMvcConfiguration.class)
        public @interface EnableWebMvc {
        }
      • 在 DelegatingWebMvcConfiguration 中继承了 WebMvcConfigurationSupport

        @Configuration(proxyBeanMethods = false)
        public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
      • 在 WebMvcAutoConfiguration 中 @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)

        ​ 当容器中不存在 WebMvcConfigurationSupport 这个 Bean 的时候自动配置类才会生效

        ​ 正因为通过 @EnableWebMvc 导入了 DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport 从而才使自动配置类失效

        @Configuration(proxyBeanMethods = false)
        @ConditionalOnWebApplication(type = Type.SERVLET)
        @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
        @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
        @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
        @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
                ValidationAutoConfiguration.class })
        public class WebMvcAutoConfiguration {
  • Json 开发

    • Spring Boot 提供了与三个 JSON 映射库的集成:

      • Gson

        • Jackson 性能最好
      • JSON-B

      Jackson 是我们使用的默认 json 库

    • jackson 的使用

      • @JsonIgnore

        • 进行排除 json 序列化,将它标注在属性上将不会进行 json 格式化
  • 国际化
  • 统一异常处理
  1. SpringBoot 的嵌入式 Servlet 容器

热部署与日志

  1. springboot 中 devtools 热部署

    1. 1 引言

      ​ 为了进一步提高开发效率,springboot 为我们提供了全局项目热部署,日后在开发过程中修改部分代码以及相关配置文件后,不需要每次重启使修改生效,在项目中开启了 springboot 全局热部署之后只需要在修改之后等待几秒即可使修改生效。

    2. 2 开启热部署

      1. 21 项目中引入依赖

        <!-- 1. devtools 热部署 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
      2. 22 当我们修改了类文件后,idea 不会自动编译,得修改 idea 设置。

        • File-Setting-Compiler-Build Project automatically

          image-20220505164635699

        • ctrl + shift + alt + /,选择 Registry,勾上 Compiler autoMake allow when app running

          image-20220505164651913

          image-20220505164728718

  2. 带你弄清混乱的 JAVA 日志体系

    1. 1 日志框架

      日志实现日志门面
      log4jJCL
      jul java.util.loggingSJF4J
      log4j2
      logback(推荐使用)
    2. 2 代码实现

      1. 21 日志实现

        1. 211 jul(注意包名)

          package com.springboot;
          
          import java.util.logging.Logger;
          
          public class JulMain {
              public static void main(String[] args) {
                  Logger logger = Logger.getLogger(JulMain.class.getName());
                  logger.info("崇尚官方开发组:Jul");
              }
          }

          image-20220428214936443

        2. 212 log4j(注意包名)

          导包:

          <!-- log4j 的核心依赖 -->
          <dependency>
              <groupId>log4j</groupId>
              <artifactId>log4j</artifactId>
              <version>1.2.17</version>
          </dependency>

          添加 log4j.properties 文件:

          log4j.rootLogger=trace, stdout
          log4j.appender.stdout=org.apache.log4j.ConsoleAppender
          log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
          log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n

          java 代码实现:

          package com.springboot;
          
          import org.apache.log4j.Logger;
          
          public class Log4jMain {
              public static void main(String[] args) {
                  Logger logger = Logger.getLogger(Log4jMain.class.getName());
                  logger.info("崇尚开源开发组:Log4j");
              }
          }

          image-20220428214918691

      2. 22 日志及日志门面实现

        开发标准:记录日志不能直接使用日志实现框架,必须通过日志门面来实现。

        1. 221 Jul (实现) + JCL (门面)

          导包:

          <!-- 引入 JCL 门面依赖 -->
          <dependency>
              <groupId>commons-logging</groupId>
              <artifactId>commons-logging</artifactId>
              <version>1.2</version>
          </dependency>

          添加 commons-logging.properties 文件:

          org.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger

          java 实现:

          package com.springboot;
          
          import org.apache.commons.logging.Log;
          import org.apache.commons.logging.LogFactory;
          
          public class JulMain {
              public static void main(String[] args) {
                  Log log = LogFactory.getLog(JulMain.class.getName());
                  System.out.println(log.getClass());
                  log.info("崇尚官方开发组:Jul");
              }
          }
        2. 222 log4j (实现) + slf4j (门面)

          导包:

          <!-- slf4j 的核心依赖 -->
          <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>slf4j-api</artifactId>
          </dependency>
          <!-- 加上桥接器 -->
          <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>slf4j-log4j12</artifactId>
          </dependency>

          java 实现:

          package com.springboot;
          
          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;
          
          public class Log4jMain {
              public static void main(String[] args) {
                  Logger logger = LoggerFactory.getLogger(Log4jMain.class);
                  System.out.println(logger.getClass());
                  logger.info("崇尚开源开发组:Log4j");
              }
          }
  1. 3 桥接器

    image-20220428220818406

  2. 4 将 JCL 转化到 slf4j

    使用 jcl-over-slf4j 或者 jul-to-slf4j

    <!-- 为了日志统一实现,将 JCL 转化到 slf4j
         添加 JCL-slf4j 的适配器
    -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
    </dependency>
  1. logback 日志的集成

    image-20220428232620876

    1. SpringBoot 底层也是使用 slf4j + logback 的方式进行日志记录

      logback 桥接:logback-calssic

    2. SpringBoot 也把其他的日志都替换成了 slf4j;

      • log4j 适配:log4j-over-slf4j
      • jul 适配:jul-to-slf4j
      • jcl-over-slf4j
  2. SpringBoot 日志使用

    • 日志级别

      可以设置 TRACE,DEBUG,INFO,WARN,ERROR 或 OFF 之一

      logging:
        level:
          root: "warn"
          org.springframework.web: "debug"
          org.hibernate: "error"
  • 日志格式

    2022-04-29 16:18:09.667 TRACE 78860 --- [           main] com.springboot.Application               : 跟踪
    2022-04-29 16:18:09.667 DEBUG 78860 --- [           main] com.springboot.Application               : 调试
    2022-04-29 16:18:09.667  INFO 78860 --- [           main] com.springboot.Application               : 信息
    2022-04-29 16:18:09.667  WARN 78860 --- [           main] com.springboot.Application               : 警告
    2022-04-29 16:18:09.667 ERROR 78860 --- [           main] com.springboot.Application               : 异常

    详细介绍:

    可以使用 logging.pattern.console 修改默认的控制台的日志格式:

    %clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}
    • 2022-04-29 16:18:09.667

      • 日期和时间:毫秒精度,易于排序。
      • %clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint}

        • %clr 代表当前内容的颜色 {faint}
        • (%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}) 括号中表示显示的内容

          • ${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}

            使用了 ${value:value2} springboot 的占位符 + null 条件的表达式(如果 value 为 null 则使用 value2)

            LOG_DATEFORMAT_PATTERN:系统环境变量中的值,springboot 底层会根据对应的配置项将值设置到对应的环境变量中 LOG_DATEFORMAT_PATTERN=logging.pattern.dateformat 可通过 dateformat: -yyyy-MM-dd 设置

          • %d{-yyyy-MM-dd HH:mm:ss.SSS}

            %d 表示 logback 的日期显示方式

            {-yyyy-MM-dd HH:mm:ss.SSS} 表示日期的格式

    • TRACE

      • 日志级别。
      • %clr(${LOG_LEVEL_PATTERN:-%5p})

        • %clr 表示内容的颜色,会根据不同的日志级别输出对应的颜色
        • ${LOG_LEVEL_PATTERN:-%5p}

          • %5 代表当前内容所占字符长度
          • p 表示输出日志的级别
    • 78860

      • 进程 ID。
      • %clr(${PID:- }){magenta}

        • %clr 表示当前内容的颜色为 {magenta}
        • ${PID:- } springboot 的占位符 + null 条件的表达式(如果 value 为 null 则使用 value2)
        • PID 是系统环境变量中的进程 ID (由系统分配)
    • ---

      • 一个 --- 分离器来区分实际日志消息的开始。
    • [ main]

      • 线程名称:用方括号括起来(对于控制台输出可能会被截断)。
    • com.springboot.Application

      • 记录日志的类。
    • 跟踪

      • 日志消息
  • 文件输出

    默认情况下,Spring Boot 仅记录到控制台,不写日志文件。如果除了控制台输出外还想写日志文件,则需要设置一个 logging.file.name 或 logging.file.path 属性(例如,在中 application.properties)。

    下表显示了如何 logging.* 一起使用这些属性:

    logging.file.namelogging.file.path实例描述
    (没有)(没有) 仅控制台记录
    指定文件名(没有)my.log写入指定的日志文件
    (没有)具体目录/var/log写入 spring.log 指定的目录
    • logging.file.name

      • 可以设置文件的名称,如果没有设置路径会默认在项目的相对路径下创建
      • 还可以指定路径 + 文件名: D:/my.log
    • logging.file.path

      • 不可以指定文件的名称,必须要指定一个物理文件夹路径,会默认使用 spring.log 为文件名称
  • Spring Boot 的默认日志级别是 INFO

    @SpringBootApplication
    public class Application {
    
        //1. 声明日志记录器
        static Logger logger = LoggerFactory.getLogger(Application.class);
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
    
            logger.trace("跟踪");
            logger.debug("调试");
            //Spring Boot 的默认日志级别是 INFO
            logger.info("信息");
            logger.warn("警告");
            logger.error("异常");
        }
    
    }

    image-20220429155655577

  • 日志迭代(轮转)

    如果您使用的是 Logback,则可以使用 application.properties 或 application.yaml 文件微调日志轮播设置。对于所有其他日志记录系统,您需要直接配置轮转设置(例如,如果使用 Log4J2,则可以添加 log4j.xml 文件)。

    名称描述
    logging.logback.rollingpolicy.file-name-pattern归档的文件名
    logging.logback.rollingpolicy.clean-history-on-start如果应在应用程序启动时进行日志归档清理
    logging.logback.rollingpolicy.max-file-size归档前日志文件的最大大小
    logging.logback.rollingpolicy.total-size-cap删除日志档案之前可以使用的最大大小
    logging.logback.rollingpolicy.max-history保留日志存档的天数(默认为7)
    • logging.logback.rollingpolicy.file-name-pattern

      • ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz

        • ${LOG_FILE} 对应 logging.file.name
        • %d{yyyy-MM-dd} 日期 年-月-日
        • %i 索引,当文件超出指定大小后进行的文件索引递增
  • 自定义日志配置文件

    可以通过在类路径中包含适日志配置文件来激活各种日志记录系统或使用 logging.config

    Logging SystemCustomization
    Logbacklogback-spring.xml, logback-spring.groovy, logback.xml, or logback.groovy
    log4j2log4j2-spring.xml or log4j2.xml
    JDK(Java Util Logging)logging.properties

    注意:

    • 如果使用自定义日志配置文件,会使 springboot 中全局配置文件的 logging 相关配置失效。
    • 可以结合 springboot 提供的 Profile 来控制日志的生效

      • 要注意的是,一定要将日志配置文件的文件名改成 logback-spring.xml, 因为 logback.xml 会在 springboot 容器加载之前先被 logback 给加载到,那么由于 logback 无法解析 springProfile 将会报错

        image-20220429181012835

      image-20220429181043577

  • 切换日志框架

    • 将 logback 切换成 log4j2

      添加 spring-boot-starter-log4j2 依赖(场景启动器)

      <!-- log4j2 的场景启动器 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-log4j2</artifactId>
      </dependency>

      排除 logback 的场景启动器

      <!-- starter-web 里面自动添加了 starter-logging 也就是 logback 的依赖 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
          <exclusions>
              <!-- 排除 starter-logging 也就是 logback 的依赖 -->
              <exclusion>
                  <groupId>org.apache.logging.log4j</groupId>
                  <artifactId>log4j-slf4j-impl</artifactId>
              </exclusion>
              <exclusion>
                  <groupId>org.apache.logging.log4j</groupId>
                  <artifactId>log4j-jul</artifactId>
              </exclusion>
              <exclusion>
                  <groupId>org.slf4j</groupId>
                  <artifactId>jul-to-slf4j</artifactId>
              </exclusion>
          </exclusions>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-logging</artifactId>
          <exclusions>
              <exclusion>
                  <groupId>*</groupId>
                  <artifactId>*</artifactId>
              </exclusion>
          </exclusions>
      </dependency>

      添加 log4j2.xml 文件

      <?xml version="1.0" encoding="UTF-8" ?>
      <configuration status="OFF">
          <appenders>
              <Console name="Console" target="SYSTEM_OUT">
                  <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} +++++++ %msg%n"/>
              </Console>
          </appenders>
          <loggers>
              <root level="info">
                  <appender-ref ref="Console"/>
              </root>
          </loggers>
      </configuration>
 

 - 将 logback 切换成 log4j

   1. 首先将 logback 的桥接器排除

      ```xml
      <!-- starter-web 里面自动添加了 starter-logging 也就是 logback 的依赖 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
          <exclusions>
              <exclusion>
                  <groupId>ch.qos.logback</groupId>
                  <artifactId>logback-classic</artifactId>
              </exclusion>
              <exclusion>
                  <groupId>org.apache.logging.log4j</groupId>
                  <artifactId>log4j-slf4j-impl</artifactId>
              </exclusion>
              <exclusion>
                  <groupId>org.apache.logging.log4j</groupId>
                  <artifactId>log4j-jul</artifactId>
              </exclusion>
              <exclusion>
                  <groupId>org.slf4j</groupId>
                  <artifactId>jul-to-slf4j</artifactId>
              </exclusion>
          </exclusions>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-logging</artifactId>
          <exclusions>
              <exclusion>
                  <groupId>*</groupId>
                  <artifactId>*</artifactId>
              </exclusion>
          </exclusions>
      </dependency>
      ```

   2. 添加 log4j 的桥接器

      ```xml
      <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-log4j12</artifactId>
      </dependency>
      ```

   3. 添加 log4j 的配置文件

      ```properties
      log4j.rootLogger=trace, stdout
      log4j.appender.stdout=org.apache.log4j.ConsoleAppender
      log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
      log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] ======== %m%n
      ```

Spring Boot 的配置文件和自动配置原理

  1. 使用 Spring Initializr 快速创建 Spring Boot 项目

    • IDEA:使用 Spring Initializr 快速创建项目

      ​ 继承关系-springboot 的 maven 项目

      • 使用 spring initializr 新建一个父 maven,type 选择 POM
      • 在父 maven 项目中新建模块,使用 spring initializr 新建一个子 maven,type选择 maven project
      • 修改子项目中的继承方式:

        之前:

        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.6.6</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>

        修改后:

        <parent>
            <groupId>com.SpringBoot</groupId>
            <artifactId>springboot_parent</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>

        spring_initializr 继承 springboot_parent 继承 spring-boot-starter-parent

    • 默认生成的 Spring Boot 项目:

      resource文件夹中的目录结构

      • static:保存所有的静态资源,js css images;
      • template:保存所有的模板页面;(Spring Boot 默认 jar 包使用嵌入式的 Tomcat,默认不支持 JSP 页面);可以使用模板引擎(freemarker、thymeleaf)
      • application.properties:Spring Boot 应用的配置文件;可以修改一下默认设置;
  2. 自定义 SpringApplication

    其他的上官网看。

    • lazy-initialization:懒加载

      # 懒加载设置,要在使用 bean 的时候才会去创建 bean 实例,否则不创建
      spring.main.lazy-initialization=true
  3. 配置文件的使用

    SpringBoot 使用一个全局的配置文件或者叫核心配置文件,配置文件名在约定的情况下 名字是固定的

    配置文件的作用:修改 SpringBoot 自动配置的默认值;SpringBoot 在底层都给我们自动配置好;

    两种配置文件的格式:

    1. application.properties 的用法:扁平的 k/v 格式。

      server.port=8081
      server.servlet.context-path=/wyu
    2. application.yml 的用法:树型结构。

      server:
          port: 8081
          servlet:
              context-path: /wyu

      建议使用 yml,因为它的可读性更强。

  • YAML语法:

    k:(空格)v:表示一对键值对(空格必须有);

    以空格的缩进来空值层级关系;只要是左对齐的一列数据,都是同一个层级的;

    属性和值也是大小写敏感;

  • 配置文件加载顺序:

    • application.yml
    • application.yaml
    • application.properties
  • 外部约定配置文件加载顺序:

    springboot 启动还会扫描以下位置的 application.properties 或者 application.yml 文件作为 Spring Boot 的默认配置文件

    下面无序列表优先级从低到高

    • classpath 根目录下的

      image-20220412141330192

    • classpath 根 config/

      image-20220412141257617

    • 项目根目录

      如果当前项目是继承/耦合关系 maven 项目的话,项目根目录为父 maven 项目的根目录。

      image-20220412141847161

    • 项目根目录 /config

      image-20220412142104682

    • 直接子目录 /config

      image-20220412143826880

  • 配置文件值注入

    image-20220425024303286

    可以使用 @Value 标签一个一个属性绑定,也可以使用 @ConfigurationProperties 标签根据 yml 或者 properties 文件一次性绑定

    • 松散绑定

      user:
        user-name: 啊章
      user:
        userName: 啊章
      user:
        user_name: 啊章
      user:
        USERNAME: 啊章

      以上四种命名是可以自动绑定 bean 属性 User.username

    • 如果输出中文乱码,则需要调一下setting:

    image-20220425024608880

    • @Value@ConfigurationProperties 的获取值比较:
    @ConfigurationProperties@Value
    功能批量注入配置文件中的属性一个个指定
    松散绑定支持支持,有限
    SpEL不支持支持
    JSR303数据校验支持不支持
    复杂类型封装支持不支持
    自动提示支持不支持
    • 导入配置文件处理器并且勾选如图设置,以后编写配置就有提示了:
    <!-- 会生成 META-INF 元数据  用于提供 idea 自动提示配置文件的 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <!-- option: 如果依赖不会传播  就是说别的项目依赖本项目,那么本项目的这个依赖不会被别的项目依赖 -->
        <optional>true</optional>
    </dependency>

    image-20220425025520512

    • 配置文件赋值:

      • 对象、Map(属性和值)(键值对):

        • 对象是 k : v 的方式

            girl-friend:
              18 : IU
              20 : 庄达菲
        • 行内写法:

            girl-friend: {18: IU,20: 庄达菲}
      • 数组(List、Set):

        • 用 - 值 表示数组中的一个元素

            hobbies:
              - 唱
              - 跳
              - rap
              - 篮球
        • 行内写法

            hobbies: [唱,跳,rap,篮球]
      • JSR303数据校验

        image-20220425033655210

        <!-- 数据校验 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        @NotNull 要使用 javax.validation 包下面的验证注解

  • 配置文件占位符

    • 随机数

      #随机值
      ${random.value}
      #随机 int 值
      ${random.int}
      #随机值
      ${random.long}
      #随机值
      ${random.uuid}
      # 10 以内的随机 int 值
      ${random.int(10)}
      #范围为 1024 到 65536 的随机 int 值
      ${random.}int[1024,65536]}
    • 占位符获取之前配置的值,如果没有可以使用:指定默认值

  • Profile文件的加载

    对于应用程序来说,不同的环境需要不同的配置。

    1. 多 Profile 文件

      • Spring 官方给出的语法规则是 application-{profile}.properties(.yaml/.yml)
      • 如果需要创建自定义的 properties 文件时,可以用 application-xxx.properties 的命名方式。

        • 开发环境下:

          image-20220412154909418

        • 生产环境下:

          image-20220412154920749

      • 若我们需要在两种环境下进行切换,只需要在 application.properties 中加入如下内容即可。

        spring.profiles.active=prod
    2. 激活指定 profile

      • 在配置文件中指定 spring.profiles.active=dev
      • 命令行:

        java -jar configuration_file-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev;
        • 还可以通过 spring.config.location 来改变默认的配置文件

          使用 spring.config.location 指定的配置文件,是不会进行互补。

          java -jar configuration_file-0.0.1-SNAPSHOT.jar --spring.profiles.location=D:/application.properties
        • 还可以通过 spring.config.name 来改变默认的配置文件

          java -jar configuration_file-0.0.1-SNAPSHOT.jar --spring.profiles.name=application-prod;
    3. 所有配置文件按以下顺序考虑:优先级从低到高

      1. 打包在 jar 中配置文件
      2. 打包在 jar 中 profile
      3. 打包在 jar 之外的配置文件
      4. 打包在 jar 之外的 profile
    4. 通配符位置

      如果具有某些 Redis 配置和某些 mysql 配置,则可能需要将这两个配置分开,同时要求这两个配置都存在于 application.properties 文件中。这可能会导致两个不同的 application.properties 文件安装在诸如不同的位置 /config/redis/application.properties 和 /config/mysql/application.properties。在这种情况下,通配符位置为 config/*/,将导致两个文件都被处理。

    5. 配置文件读取方式:优先级从低到高

      • @PropertySource@Configuration 类上的注解。请注意,Environment 在刷新应用程序上下文之前,不会将此类属性源添加到中。现在配置某些属性(如 logging. 和 spring.main. 在刷新开始之前先读取)为时已晚。

        • 会和约定的配置文件形成互补
        • 一定要指定 .properties 配置
        @SpringBootApplication
        @PropertySource("classpath:appSource.properties")
        public class Application {
        
            public static void main(String[] args) throws IOException {}
      

    - 默认属性(通过设置指定 SpringApplication.setDefaultProperties)

      - 会和约定的配置文件形成互补

      ```java
      @SpringBootApplication
      public class Application {
      
          public static void main(String[] args) throws IOException {
              SpringApplication springApplication = new SpringApplication(Application.class);
      
              // 创建 properties
              Properties properties = new Properties();
              // 通过当前类的 ClassLoader
              InputStream is = Application.class.getClassLoader().getResourceAsStream("app.properties");
              // 将输入流读取成 properties 对象
              properties.load(is);
      
              springApplication.setDefaultProperties(properties);
              springApplication.run(args);
          }
      
      }
      ```

      

    - 配置数据(例如 application.properties 文件)

      - 约定配置文件

    - 操作系统环境变量。

      - 会使约定配置文件失效

      - idea 

        ![image-20220422040551533](https://static-upyun.hackerjk.top/markdown/image-20220422040551533.png!)

      - windows

        ![image-20220422040837561](https://static-upyun.hackerjk.top/markdown/image-20220422040837561.png!)

    - Java 系统属性(System.getProperties())。

      - 会使约定配置文件失效

      - idea

        ![image-20220422041130067](https://static-upyun.hackerjk.top/markdown/image-20220422041130067.png!)

      - 命令行 java 属性

        ![image-20220422041323887](https://static-upyun.hackerjk.top/markdown/image-20220422041323887.png!)

    - JNDI 属性 java:comp/env。

    - ServletContext 初始化参数。

      ![image-20220422041429270](https://static-upyun.hackerjk.top/markdown/image-20220422041429270.png!)

    - ServletConfig 初始化参数。

      ![image-20220422041509283](https://static-upyun.hackerjk.top/markdown/image-20220422041509283.png!)

    - 来自的属性 SPRING_APPLICATION_JSON(嵌入在环境变量或系统属性中的嵌入式 JSON)。

    - 命令行参数。

      - 会使约定配置文件失效

      ```cmd
      java -jar configuration_file-0.0.1-SNAPSHOT.jar --spring.profiles.location=D:/application.properties
      ```

      

    - properties 测试中的属性。可用于测试应用程序的特定部分 @SpringBootTest 的测试注释和注释。

    - @TestPropertySource 测试中的注释。

      - 用在单元测试上的

        ![image-20220422043022209](https://static-upyun.hackerjk.top/markdown/image-20220422043022209.png!)

    - $HOME/.config/spring-boot 当 devtools 处于活动状态时,目录中的 Devtools全局设置属性。
  • 配置文件加载位置
  1. Spring Boot 的配置和自动配置原理

    • @SpringBootApplication:Spring Boot 应用标注在某个类上说明这个类是 SpringBoot 的主配置类,SpringBoot 需要运行这个类的 main 方法来启动 SpringBoot 应用;

      image-20220427150357061

      @Target(ElementType.TYPE)    //设置当前注解可以标记在哪
      @Retention(RetentionPolicy.RUNTIME)    //当注解标注的类编译以什么方式保留
          RetentionPolicy.RUNTIME 会被 jvm 加载 | 反射的方式:class.getAnnotionbytype(Autowreid.class)
      @Documented        //java doc 会生成注解信息
      @Inherited        //是否会被继承
      
      @SpringBootConfiguration    //Spring Boot 的配置类;标注在某个类上,表示这是一个 Spring Boot 的配置类;
      
      @Configuration    //配置类上来标注这个注解;配置类——配置文件;配置类也是同期中的一个组件;@Component
      
      @EnableAutoConfiguration    //开启自动配置功能;以前我们需要配置的东西,Spring Boot 帮我们自动配置;@EnableAutoConfiguration 告诉 SpringBoot 开启自动配置功能;这样自动配置才能生效;
      
      @ComponentScan    //扫描包 相当于在 spring.xml 配置中 <context:component-scan>    但是没有指定 basepackage,如果没有指定,spring 底层会自动扫描当前配置类所有在的包
      
      TypeExcludeFilter:springboot    //对外提供的扩展类,可以供我们去按照我们的方式进行排除
      
      AutoConfigurationExcludeFilter    //排除所有配置类并且是自动配置类中里面的其中一个
      
      @AutoConfigurationPackage    //将当前配置类所在包保存在 BasePackages 的 Bean 中。供 spring 内部使用
    • 以 HttpEncodingAutoConfiguration (Http 编码自动配置)为例解释自动配置原理;

      @Configuration(proxyBeanMethods = false)
      @EnableConfigurationProperties(ServerProperties.class)
      @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
      @ConditionalOnClass(CharacterEncodingFilter.class)
      @ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
      public class HttpEncodingAutoConfiguration {
      
          private final Encoding properties;
      
          public HttpEncodingAutoConfiguration(ServerProperties properties) {
              this.properties = properties.getServlet().getEncoding();
          }
      
          @Bean
          @ConditionalOnMissingBean
          public CharacterEncodingFilter characterEncodingFilter() {
              CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
              filter.setEncoding(this.properties.getCharset().name());
              filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
              filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
              return filter;
          }
      • @Configuration(proxyBeanMethods = false)

        标记了 @Configuration,Spring 底层会给配置创建 cglib 动态代理。

        作用:防止每次调用本类的 Bean 方法而重新创建对象,Bean 是默认单例的。

      • @EnableConfigurationProperties(ServerProperties.class)

        启用可以再配置类设置的属性 对应的类

      • @xxxConditional 根据当前不同的条件判断,决定这个配置类是否生效?
      • @Conditional 派生注解(Spring 注解版原生的 @Conditional 作用)

        作用:必须是 @Conditional 指定的条件成立,才给容器中添加组件,配置配里面的所有内容才能生效;

        @Conditional 扩展注解作用(判断是否满足当前指定条件)
        @ConditionalOnJava系统的 java 版本是否符合要求
        @ConditionalOnBean容器中存在指定的 Bean;
        @ConditionalOnMissingBean容器中不存在指定的 Bean;
        @ConditionalOnExpression满足 SpEL 表达式指定
        @ConditionalOnClass系统中有指定的类
        @ConditionalOnOnMissingClass系统中没有指定的类
        @ConditionalOnSingleCandidate容器中只有一个指定的 Bean,或者这个 Bean 是首选 Bean
        @ConditionalOnProperty系统中指定的属性是否有指定的值
        @ConditionalOnResource类路径下是否存在指定资源文件
        @ConditionalOnWebApplication当前是 Web 环境
        @ConditionalOnNotWebApplication当前不是 Web 环境
        @ConditionalOnJndiJNDI 存在指定项
      • 可以通过设置配置文件中:启用 debug=true 属性来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效

        ============================
        CONDITIONS EVALUATION REPORT
        ============================
        
        
        Positive matches:    **表示自动配置类启用的**
        -----------------
        ......
        
        
        Negative matches:    **没有匹配成功的自动配置类**
        -----------------
        ......

Spring Boot 简介及快速搭建

  1. 导入依赖:

    <!-- 继承 SpringBoot 的父项目 -->
    <!-- spring boot 要加上下面的 parent 代码才能将当前项目标记为 spring boot 项目 -->
    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.6.6</version>
    </parent>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    
    
    <!-- 下面的 plugin 中的 spring-boot-maven-plugin 可以告诉服务器 SpringBoot 的启动类在哪 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  2. 建包并创建控制器

    @RestController  // 相当于 @Controller + @ResponseBody
    @RequestMapping("/hello")
    public class HelloController {
    
        @RequestMapping("/world")
        public String sayHi() {
            return "hello world!";
        }
    
    }
  3. 编写启动类

    @SpringBootApplication      // 标记为 SpringBoot 的启动类  整个工程只有一个
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    
    }
  4. 修改端口及添加项目名

    在resource文件夹中创建一个 application.properties 文件(文件名只能是这个)

    # 端口
    server.port=8080
    # 项目虚拟路径
    server.servlet.context-path=/wyu

    ​ server.port 是用来选用端口的

    ​ server.servlet.context-path 则是用来添加项目名,注意:没有使用 server.servlet.context-path 之前的访问地址为localhost:8080/hello/world,使用之后地址变为 localhost:8080/wyu/hello/world

  5. 部署服务器

    在 pom.xml 文件中加入下面的代码

    <!-- 下面的 plugin 中的 spring-boot-maven-plugin 可以告诉服务器 SpringBoot 的启动类在哪 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  6. 代码讲解

    <!-- 引入父 Maven 项目,继承父 Maven 项目中所有的配置信息
        spring-boot-starter-parent: 又引入一个父 Maven 项目
        <parent>
            <artifactId>spring-boot-dependencies</artifactId>
            <groupId>org.springframework.boot</groupId>
            <version>2.6.6</version>
        </parent>
        spring-boot-dependencies 帮我们管理了 SpringBoot 应用中所有的依赖和版本
                以后我们导入已有依赖就不需要写版本号了,它帮我们解决了第三方库之间的版本冲突问题
                名次:SpringBoot 的版本仲裁中心
    -->
    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.6.6</version>
    </parent>
    
    <!--
        starter 场景启动器:不同场景启动器维护了所对应的所有依赖,从而简化 maven 文件书写。
        spring-boot-starter-web:使用 Spring MVC 构建 Web (包括RESTful)应用程序。
            使用 Tomcat 作为默认的嵌入式容器
    -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
    
    
    <!-- 下面的 plugin 中的 spring-boot-maven-plugin 可以告诉服务器 SpringBoot 的启动类在哪 -->
    <!-- 部署 SpringBoot 的插件,只有加了这个插件,当运行 java -jar xxx.jar 才能正常启动 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
  7. 面试题:

    • 描述一下 SpringBoot 的作用
    • SpringBoot 有哪些特性?