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
    
posted @ 2021-08-01 23:34  孙昌恒  阅读(79)  评论(0编辑  收藏  举报