• SpringBoot的常用特性


    常用功能特性

    SpringBoot应用启动入口

    package com.imooc.springboot.study;
    
    import org.springframework.boot.Banner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.WebApplicationType;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    
    /**
     * @author sunchangheng
     */
    @SpringBootApplication
    public class SpringBootStudyApplication {
        public static void main(String[] args) {
            // 1. 第一种: 通过静态run方法
    //            SpringApplication.run(SpringBootStudyApplication.class, args);
            
            // 2. 通过API调整应用行为
    //        SpringApplication app = new SpringApplication(SpringBootStudyApplication.class);
    //        app.setBannerMode(Banner.Mode.OFF);
    //        app.setWebApplicationType(WebApplicationType.NONE);
    //
    //        app.run(args);
            
            // 3. SpringApplicationBuilder Fluent API 链式调用
            new SpringApplicationBuilder(SpringBootStudyApplication.class).bannerMode(Banner.Mode.OFF)
                    .web(WebApplicationType.SERVLET).run(args);
            
        }
    }
    

    自动配置原理

    配置文件

    配置注入的方式

    # 配置文件application.yml
     
    # 自定义配置
    imooc:
      springboot:
        version: 2.1, 2.1.4
        name: study
    

    方式一

    package com.imooc.springboot.study.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @author sunchangheng
     */
    @Slf4j
    @RestController
    @RequestMapping("/springboot")
    public class TestController {
    
        @Value("${imooc.springboot.version}")
        private String version;
        
        @Value("${imooc.springboot.name}")
        private String name;
        
        @GetMapping("firstConfInject")
        public String firstConfInject() {
            log.info("version: {}, name: {}", version, name);
            return "ok";
        }
    }
    

    方式二

    1. 先写一个配置类

      package com.imooc.springboot.study.config;
      
      import lombok.Data;
      import org.springframework.boot.context.properties.ConfigurationProperties;
      import org.springframework.stereotype.Component;
      
      /**
       * SpringBoot 配置文件中前缀是imooc.springboot的配置
       *
       * @author sunchangheng
       */
      @Data
      @Component
      @ConfigurationProperties(prefix = "imooc.springboot")
      public class SpringBootConfig {
          
          /**
           * 版本
           */
          private String version;
          
          /**
           * 名字
           */
          private String name;
      }
      
    2. controller注入使用

      @Resource
      private final SpringBootConfig springBootConfig;
      
      @GetMapping("/secondConfigInject")
      public String secondConfigInject() {
          log.info("second: version: {} name: {}", springBootConfig.getVersion(), springBootConfig.getName());
          return "second ok";
      }
      

    定时任务

    1. 现在启动类上开启定时任务

    2. 配置定时任务类

      package com.imooc.springboot.study.schedule;
      
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.scheduling.annotation.Scheduled;
      import org.springframework.stereotype.Component;
      
      import java.time.LocalDateTime;
      import java.time.format.DateTimeFormatter;
      
      /**
       * SpringBoot 定时任务
       *
       * @author sunchangheng
       */
      @Slf4j
      @Component
      public class BootSchedule {
          
          private final DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH;mm;ss");
          
          /**
           * 上一次开始执行时间点之后3000毫秒之后再执行
           */
          @Scheduled(fixedRate = 3000)
          public void schedule01() {
              log.info("schedule01 -> {}", LocalDateTime.now().format(fmt));
          }
          
          /**
           * 上一次执行完毕时间点之后3000毫秒之后再执行
           */
          @Scheduled(fixedDelay = 3000)
          public void schedule02() {
              log.info("schedule02 -> {}", LocalDateTime.now().format(fmt));
          }
          
          /**
           * 第一次延迟2s之后执行, 之后按照上一次开始执行时间点之后3s之后再执行
           */
          @Scheduled(initialDelay = 2000, fixedRate = 3000)
          public void schedule03() {
              log.info("schedule03 -> {}", LocalDateTime.now().format(fmt));
          }
          
          /**
           * 每3s执行一次
           */
          @Scheduled(cron = "*/3 * * * * ?")
          public void schedule04() {
              log.info("schedule04 -> {}", LocalDateTime.now().format(fmt));
          }
          
      }
      

    异步任务

    1. 先在启动类启用

    2. 编写异步处理类

      package com.imooc.springboot.study.async;
      
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.scheduling.annotation.Async;
      import org.springframework.scheduling.annotation.AsyncResult;
      import org.springframework.stereotype.Service;
      
      import java.util.concurrent.Future;
      import java.util.concurrent.TimeUnit;
      
      /**
       * 异步处理服务
       *
       * @author sunchangheng
       */
      @Slf4j
      @Service
      public class AsyncService {
          
          @Async("getAsyncExecutor")
          public void asyncProcess() {
              log.info("async process task, current thread name -> {}", Thread.currentThread().getName());
              try {
                  TimeUnit.MILLISECONDS.sleep(2000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
          
          @Async("getAsyncExecutor")
          public Future<Integer> asyncProcessHasReturn() {
              log.info("async process task (has return), current thread name -> {}", Thread.currentThread().getName());
              try {
                  TimeUnit.MILLISECONDS.sleep(2000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              return new AsyncResult<>(100);
          }
      }
      

      @Async("getAsyncExecutor") 这里指定我们需要用到的线程池, 将会在下一个步骤配置

    3. 编写异步线程池配置

      package com.imooc.springboot.study.config;
      
      import com.alibaba.fastjson.JSON;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.scheduling.annotation.AsyncConfigurer;
      import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
      
      import java.lang.reflect.Method;
      import java.util.concurrent.Executor;
      import java.util.concurrent.ThreadPoolExecutor;
      
      /**
       * 自定义异步线程池配置
       *
       * @author sunchangheng
       */
      @Slf4j
      @Configuration
      public class AsyncConfig implements AsyncConfigurer {
          
          @Bean
          @Override
          public Executor getAsyncExecutor() {
              ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
              
              // 线程池核心的数量, 默认是1, 导致异步线程池无法被重用
              executor.setCorePoolSize(10);
              executor.setMaxPoolSize(20);
              executor.setQueueCapacity(20);
              executor.setKeepAliveSeconds(60);
              executor.setThreadNamePrefix("ImoocAsync_");
              
              // 是否等待所有线程执行完毕之后才去关闭线程池, 默认是false
              executor.setWaitForTasksToCompleteOnShutdown(true);
              // 和上面这个参数一起使用, 等待60s
              executor.setAwaitTerminationSeconds(60);
              
              // 拒绝策略
              // 就是线程池满了, 怎么处理的策略
              executor.setRejectedExecutionHandler(
                      // 直接抛出异常
                      new ThreadPoolExecutor.AbortPolicy());
              
              executor.initialize();
              
              return executor;
          }
          
          /**
           * 定义异步任务异常处理类 1. 没有返回值的异步任务, 的异常将会在这里抛出 2. 有返回值的异步任务, 异常将会抛出给上一层
           */
          @Override
          public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
              
              return new AsyncExceptionHandler();
          }
          
          static class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
              
              @Override
              public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
                  log.info("Async: {}, method: {}, param: {}",
                          throwable.getMessage(),
                          method.getName(),
                          JSON.toJSONString(objects));
                  
                  throwable.printStackTrace();
                  
                  // todo 发送邮件或者短信给相关负责人...
              }
          }
      }
      

      注意: getAsyncExecutor这个方法一定要加上@Bean

      单元测试

      package com.imooc.springboot.study.service;
      
      import com.imooc.springboot.study.async.AsyncService;
      import lombok.extern.slf4j.Slf4j;
      import org.junit.Test;
      import org.junit.runner.RunWith;
      import org.springframework.boot.test.context.SpringBootTest;
      import org.springframework.test.context.junit4.SpringRunner;
      
      import javax.annotation.Resource;
      import java.util.concurrent.Future;
      
      @Slf4j
      @SpringBootTest
      @RunWith(SpringRunner.class)
      public class AsyncServiceTest {
          @Resource
          private AsyncService asyncService;
          
          @Test
          public void testAsyncProcess() {
              asyncService.asyncProcess();
              log.info("testAsyncProcess end...");
          }
          
          @Test
          public void testAsyncProcessHasReturn() throws Exception {
              long start = System.currentTimeMillis();
              Future<Integer> result = asyncService.asyncProcessHasReturn();
              log.info("异步任务返回值: {}", result.get());
              log.info("测试用例执行了: {} ms", System.currentTimeMillis() - start);
          }
      }
      

      注意一定要加这两个注解: @SpringBootTest @RunWith(SpringRunner.class)

      开机启动

      方式一

      package com.imooc.springboot.study.runer;
      
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.boot.ApplicationArguments;
      import org.springframework.boot.ApplicationRunner;
      import org.springframework.core.annotation.Order;
      import org.springframework.stereotype.Component;
      
      /**
       * 1. SpringBoot 开机启动
       * @author sunchangheng
       */
      @Order(2)
      @Slf4j
      @Component
      public class BootApplicationRunner implements ApplicationRunner {
          
          @Override
          public void run(ApplicationArguments args) throws Exception {
              log.info("this is BootApplicationRunner ...");
          }
      }
      

      方式二

      package com.imooc.springboot.study.runer;
      
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.boot.CommandLineRunner;
      import org.springframework.core.annotation.Order;
      import org.springframework.stereotype.Component;
      
      /**
       * 2. SpringBoot开机启动
       * @author sunchangheng
       */
      @Order(1)
      @Slf4j
      @Component
      public class BootCommandLineRunner implements CommandLineRunner {
          
          
          @Override
          public void run(String... args) throws Exception {
              log.info("this is BootCommandLineRunner ...");
          }
      }
      

      注意

      1. 默认ApplicationRunner 的启动优先级大于CommandLineRunner
      2. 可以使用Order 定义启动顺序, 值越小优先级越高

      Json使用技巧

      Java 实体类

      package com.imooc.springboot.study.vo;
      
      import com.fasterxml.jackson.annotation.JsonFormat;
      import com.fasterxml.jackson.annotation.JsonIgnore;
      import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
      import com.fasterxml.jackson.annotation.JsonProperty;
      import lombok.AllArgsConstructor;
      import lombok.Builder;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      import java.util.Date;
      
      /**
       * 用户
       * @author sunchangheng
       */
      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      @Builder
      @JsonIgnoreProperties({"remark", "desc"})
      public class Imoocer {
          private String name;
          
          private Integer age;
          
          @JsonIgnore
          private String address;
          
          @JsonProperty("rt")
          @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
          private Date registerTime;
          
          private String remark;
          
          private String desc;
      
      }
      

      注意

      JsonIgnoreProperties  # 在类上注释, 可以忽略多个不想序列化的字段
      JsonProperty  # 序列化时, 会将registerTime 改变成 rt
      JsonIgnore  # 注释在属性上, 当前属性将不会被序列化
      JsonFormat  # Date的序列化格式
      

      Json全局配置

      package com.imooc.springboot.study.config;
      
      import com.fasterxml.jackson.annotation.JsonInclude;
      import com.fasterxml.jackson.databind.ObjectMapper;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      
      import java.text.SimpleDateFormat;
      
      /**
       * Json的配置类
       * @author sunchangheng
       */
      @Configuration
      public class JacksonConfig {
          
          @Bean
          public ObjectMapper getObjectMapper() {
              ObjectMapper mapper = new ObjectMapper();
              // 空值不做序列化处理
              mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
              // 序列化的时间格式
              mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
              return mapper;
          }
      }
      

      controller序列化与反序列化测试

      package com.imooc.springboot.study.controller;
      
      import com.fasterxml.jackson.core.JsonProcessingException;
      import com.fasterxml.jackson.databind.ObjectMapper;
      import com.imooc.springboot.study.config.SpringBootConfig;
      import com.imooc.springboot.study.vo.Imoocer;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      import javax.annotation.Resource;
      import java.io.IOException;
      import java.util.Date;
      
      /**
       * @author sunchangheng
       */
      @Slf4j
      @RestController
      @RequestMapping("/springboot")
      public class TestController     
          @Resource
          private ObjectMapper mapper
          
          /**
           * 功能描述: Json序列化与反序列化测试
           * 127.0.0.1:8000/imooc/springboot/jackson
           */
          @GetMapping("/jackson")
          public Imoocer jackson() throws IOException {
              Imoocer imoocer = Imoocer.builder().name("qinyi").age(19).remark("hello world").desc("什么")
                      .registerTime(new Date()).build();
          
              // 序列化: object->json
              String jsonImoocer = mapper.writeValueAsString(imoocer);
          
              // 反序列化: json->object
              return mapper.readValue(jsonImoocer, Imoocer.class);
          }
      }
      

      Actuator监控

      // /actuator/info 默认返回空
      // 可以在配置文件配置info
      
      

      简单使用

      <!-- Actuator监控 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
      

      配置文件

      # Actuator监控相关
      management:
        endpoint:
          shutdown:
            enabled: true  # 最特殊的控制断点, 远程关闭应用程序的断点, 比较危险, 一般不打开
      
        endpoints:
          web:
            exposure:
              include:  "*" # 打开所有断点
      
      # /imooc/actuator
      # http://127.0.0.1:8000/imooc/actuator/health
      # http://127.0.0.1:8000/imooc/actuator/info
      
      info:
        app:
          name: imooc-springboot-study
          groupId: com.imooc.springboot.study
          version: 1.0-SNAPSHOT
      

      自定义监控端点

      1. 自定义端点

        package com.imooc.springboot.study.endpoint;
        
        import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
        import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
        import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
        
        import java.text.SimpleDateFormat;
        import java.util.Date;
        import java.util.HashMap;
        import java.util.Map;
        
        /**
         * 自定义事件端点 127.0.0.1:8000/imooc/actuator/datetime
         *
         * @author sunchangheng
         */
        @Endpoint(id = "datetime")
        public class DateTimeEndpoint {
            
            private String fmt = "yyyy-MM-dd HH:mm:ss";
            
            /**
             * 功能描述: 用来显示监控指标
             * get   http://127.0.0.1:8000/imooc/actuator/datetime
             */
            @ReadOperation
            public Map<String, Object> info() {
                Map<String, Object> info = new HashMap<>(10);
                info.put("name", "sch");
                info.put("age", 19);
                info.put("dt", new SimpleDateFormat(fmt).format(new Date()));
                return info;
            }
            
            /**
             * 功能描述: 动态更改监控指标
             * post  http://127.0.0.1:8000/imooc/actuator/datetime
             */
            @WriteOperation
            public void setFormat(String format) {
                this.fmt = format;
            }
        }
        
      2. 启用自定义配置的端点

        package com.imooc.springboot.study.endpoint;
        
        import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
        import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.Configuration;
        
        /**
         * 自定义端点配置类
         *
         * @author sunchangheng
         */
        @Configuration
        public class DateTimeEndpointConfig {
            
            @Bean
            @ConditionalOnMissingBean
            @ConditionalOnEnabledEndpoint
            public DateTimeEndpoint dateTimeEndpoint() {
                return new DateTimeEndpoint();
            }
        }
        

        注意: 需要在配置文件中打开所有端点

      自定义 Starter

      • 新建一个starter项目
        1. 依赖

          <dependencies>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-configuration-processor</artifactId>
                  <version>2.1.4.RELEASE</version>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-autoconfigure</artifactId>
                  <version>2.1.4.RELEASE</version>
              </dependency>
          </dependencies>
          
        2. 创建service

          1. 接口

            package com.imooc.springboot.service;
            
            import java.util.List;
            
            /**
             * @author sunchangheng
             */
            public interface ISplictService {
                
                /**
                 * 功能描述: 分割字符串
                 * @param str 将要分割的字符串
                 * @return 字符串列表
                 */
                List<String> split(String str);
            
            }
            
          2. 实现

            package com.imooc.springboot.service.impl;
            
            import com.imooc.springboot.service.ISplictService;
            import org.springframework.util.StringUtils;
            
            import java.util.List;
            import java.util.stream.Collectors;
            import java.util.stream.Stream;
            
            /**
             * @author sunchangheng
             */
            public class SplictServiceImpl implements ISplictService {
                
                @SuppressWarnings("all")
                @Override
                public List<String> split(String str) {
                    return Stream.of(StringUtils.split(str, ",")).collect(Collectors.toList());
                }
            }
            
        3. 配置自动注入

          package com.imooc.springboot.configure;
          
          import com.imooc.springboot.service.ISplictService;
          import com.imooc.springboot.service.impl.SplictServiceImpl;
          import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
          import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration;
          
          /**
           * @author sunchangheng
           */
          @Configuration
          @ConditionalOnClass({ISplictService.class, SplictServiceImpl.class})
          public class SplictAutoConfigure {
              
              @Bean
              @ConditionalOnMissingBean
              ISplictService starterService() {
                  return new SplictServiceImpl();
              }
          }
          
        4. 配置meta-inf

          org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.imooc.springboot.configure.SplictAutoConfigure
          
        5. maven 打包到本地 mvn install

        6. 最后就可以在其他项目引入使用了

      • 在其他项目引入使用
        1. 引入依赖

          <dependency>
              <groupId>com.imooc.springboot</groupId>
              <artifactId>split-spring-boot-starter</artifactId>
              <version>1.0-SNAPSHOT</version>
          </dependency>
          
        2. 使用测试用例测试

          package com.imooc.springboot.study.service;
          
          import com.alibaba.fastjson.JSON;
          import com.imooc.springboot.service.ISplictService;
          import com.imooc.springboot.study.config.SpringBootConfig;
          import lombok.extern.slf4j.Slf4j;
          import org.junit.Test;
          import org.junit.runner.RunWith;
          import org.springframework.boot.test.context.SpringBootTest;
          import org.springframework.test.context.junit4.SpringRunner;
          
          import javax.annotation.Resource;
          
          @Slf4j
          @SpringBootTest
          @RunWith(SpringRunner.class)
          public class SplictServiceTest {
              @Resource
              private ISplictService splictService;
              
              @Resource
              private SpringBootConfig springBootConfig;
              
              @Test
              public void testSplictVersion() {
                  log.info("splict version: {}", JSON.toJSONString(splictService.split(springBootConfig.getVersion())));
              }
          }
          

      管理 SpringBoot 应用

      # 打包到target目录
      mvn clean package -Dmaven.test.skip=true -U
      

      启动脚本

      # start.sh
      #!/usr/bin/env bash
      nohup java -jar imooc-springboot-study.jar &
      

      停止脚本

      #!/usr/bin/env bash
      
      pid=`ps -ef | grep imooc-springboot-study.jar | grep -v grep | awk '{print $2}'`
      
      if [[ -z "${pid}"]]
      then
        echo application is already stopped
      else
        echo kill ${pid}
        kill -9 ${pid}
      fi
      
  • 相关阅读:
    寒假自学进度十四
    2020.2.14
    2020.2.13
    寒假自学进度十三
    2020.2.12
    软件架构实践阅读笔记1
    一线架构师实践指南阅读笔记03
    一线架构师实践指南阅读笔记02
    python作业5
    一线架构师实践指南Refined Architecture阶段阅读笔记
  • 原文地址:https://www.cnblogs.com/sunch/p/15088095.html
Copyright © 2020-2023  润新知