常用功能特性
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";
}
}
方式二
-
先写一个配置类
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; }
-
在
controller
注入使用@Resource private final SpringBootConfig springBootConfig; @GetMapping("/secondConfigInject") public String secondConfigInject() { log.info("second: version: {} name: {}", springBootConfig.getVersion(), springBootConfig.getName()); return "second ok"; }
定时任务
-
现在启动类上开启定时任务
-
配置定时任务类
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)); } }
异步任务
-
先在启动类启用
-
编写异步处理类
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")
这里指定我们需要用到的线程池, 将会在下一个步骤配置 -
编写异步线程池配置
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 ..."); } }
注意
- 默认
ApplicationRunner
的启动优先级大于CommandLineRunner
- 可以使用
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
自定义监控端点
-
自定义端点
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; } }
-
启用自定义配置的端点
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项目
-
依赖
<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>
-
创建service
-
接口
package com.imooc.springboot.service; import java.util.List; /** * @author sunchangheng */ public interface ISplictService { /** * 功能描述: 分割字符串 * @param str 将要分割的字符串 * @return 字符串列表 */ List<String> split(String str); }
-
实现
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()); } }
-
-
配置自动注入
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(); } }
-
配置meta-inf
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.imooc.springboot.configure.SplictAutoConfigure
-
maven 打包到本地
mvn install
-
最后就可以在其他项目引入使用了
-
- 在其他项目引入使用
-
引入依赖
<dependency> <groupId>com.imooc.springboot</groupId> <artifactId>split-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
-
使用测试用例测试
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
- 默认