Springboot常用功能特性
应用启动的三种主要方式
首先应该在程序入口处加上 @SpringBootApplication
@SpringBootApplication
public class SpringBootStudyApplication {}
1、SpringApplication静态方法run
SpringApplication.run(SpringBootStudyApplication.class, args);
2、通过Api调整应用行为
SpringApplication application =
new SpringApplication(SpringBootStudyApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.setWebApplicationType(WebApplicationType.NONE);//设置了非web环境程序会结束
application.run(args);
3、SpringApplication的Fluent Api,实现链式调用
new SpringApplicationBuilder(SpringBootStudyApplication.class)
.bannerMode(Banner.Mode.OFF)
// .web(WebApplicationType.NONE)
.run(args);
SpringBoot自动配置原理
@SpringBootConfiguration //相当于Configuration,实际上也是继承自它
@EnableAutoConfiguration //开启自动配置核心注解
@ComponentScan(excludeFilters = {//扫描注解 Service Component...
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {}
分析 EnableAutoConfiguration
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class) // 导入AutoConfigurationImportSelector
public @interface EnableAutoConfiguration {}
分析 AutoConfigurationPackage
@Import(AutoConfigurationPackages.Registrar.class) // 导入Registrar
public @interface AutoConfigurationPackage {}
分析 Registrar
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
// 注册了主应用程序bean
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
分析 AutoConfigurationImportSelector
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);//点下去
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);//点进去
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(//点进去
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
可追踪到 org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
Enumeration<URL> urls = (classLoader != null ?
//public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
SpringBoot配置文件
同一目录下的application和bootstrap
bootstrap优先级高于application,优先被加载
bootstrap用于应用程序上下文的引导阶段,由父ApplicationContext加载
bootstrap是系统级别的配置(不变的参数),application是应用级别的配置
不同位置的配置文件再加顺序(优先级)
高优先级覆盖低优先级相同配置,多个配置文件互补
file:./config/ - 优先级最高(项目根路径下的config)
file:./ - 优先级第二(项目根路径下)
classpath:/config/ - 优先级第三(项目resources/config下)
classpath:/ - 优先级第四(项目resources目录下)
配置注入方式
luyuni:
springboot:
version: 2.1,2.1.4
name: study
- 方式一:
@value
@Value(value = "${luyuni.springboot.version}")
private String version;
@Value(value = "${luyuni.springboot.name}")
private String name;
- 方式二:
@ConfigurationProperties
@Data
@Component
@ConfigurationProperties(prefix = "luyuni.springboot")
public class SpringBootConfig {
private String version;
private String name;
}
定时任务
- 1、开启定时任务
@EnableScheduling //开启定时任务
- 2、编写定时任务
@Slf4j
@Component
public class BootSchedule {
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
/**
* 上一次开始执行时间点之后3000毫秒再执行
* */
@Scheduled(fixedRate = 3000)
public void schedule01() {
log.info("schedule01 -> {}", LocalDateTime.now().format(formatter));
}
/**
* 上一次执行完毕时间点之后3s再执行
* */
@Scheduled(fixedDelay = 3000)
public void schedule02() {
log.info("schedule02 -> {}", LocalDateTime.now().format(formatter));
}
/**
* 第一次延迟2s之后执行, 之后按照每3s执行一次
* */
@Scheduled(initialDelay = 2000, fixedRate = 3000)
public void schedule03() {
log.info("schedule03 -> {}", LocalDateTime.now().format(formatter));
}
/**
* 每3s执行一次
* */
@Scheduled(cron = "*/3 * * * * ?")
public void schedule04() {
log.info("schedule04 -> {}", LocalDateTime.now().format(formatter));
}
}
异步任务
- 1、引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 2、开启异步任务
@EnableAsync //开启异步支持
- 3、编写异步方法
@Slf4j
@Component
public class AsyncService {
@Async //新开一个线程执行任务
public void asyncProcess() throws InterruptedException{
log.info("async process task, current thread name -> {}", Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(2); //模拟处理任务花费时间
}
@Async
public Future<Integer> asyncProcessHasReturn() throws InterruptedException{
log.info("async process task (has return), current thread name -> {}", Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(2); //模拟处理任务花费时间
return new AsyncResult<Integer>(100);
}
}
异步任务所使用线程池相关定义
@Slf4j
@Configuration
public class AsyncPoolConfig implements AsyncConfigurer {//实现AsyncConfigurer
@Bean
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(20);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("LuyuniAsync_");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
// 拒绝策略,这里是直接抛出异常
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
executor.initialize();
return executor;
}
/**
* 定义异步任务异常处理类
* */
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
log.info("AsyncError: {}, Method: {}, Param: {}", throwable.getMessage(), method.getName(), JSON.toJSONString(objects));
throwable.printStackTrace();
// TODO 发送邮件或者短信
}
}
}
SpringBoot单元测试
对上面的异步服务功能进行测试
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class AsyncServiceTest {
@Autowired
private AsyncService asyncService;
@Test
public void testAsyncProcess() throws InterruptedException{
asyncService.asyncProcess();
log.info("coming in testAsyncProcess ...");
}
@Test
public void testAsyncProcessHasReturn() throws InterruptedException, ExecutionException, TimeoutException {
long start = System.currentTimeMillis();
Future<Integer> result = asyncService.asyncProcessHasReturn();
log.info("get async task value -> {}", result.get(1, TimeUnit.SECONDS));
log.info("time spend for async task: {}ms", System.currentTimeMillis() - start);
}
}
SpringBoot开机启动
- 1、实现ApplicationRunner
@Slf4j
@Component
public class BootApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("this is BootApplicationRunner ...");
}
}
- 2、实现CommandLineRunner
@Slf4j
@Component
public class BootCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
log.info("this is BootCommandLineRunner ...");
}
}
默认ApplicationRunner优先执行、有指定需求可指定顺序 @Order(1)
值越小越先执行,或实现 Order
接口
SpringBoot - Actuator监控
Actuator是什么,它能做什么
- 是什么:是SpringBoot提供的对应用系统的自醒和监控的集成功能
- 能做什么:查看应用(配置)信息、环境信息以及对应用进行操控
Actuator监控分类
原生端点
-
应用配置类
- 自己配置的info信息
# 暴露的info信息 info: app: name: luyuni-springboot-study groupId: top.luyuni.springboot.study version: 1.0-SNAPSHOT
localhost:8000/luyuni/actuator/info
- 应用中的bean信息
localhost:8000/luyuni/actuator/beans
- 应用中的uri路径信息
localhost:8000/luyuni/actuator/mappings
-
度量指标类
- 检查应用的运行状态
localhost:8000/luyuni/actuator/health
- 当前线程活动快照
localhost:8000/luyuni/actuator/threaddump
-
操作控制类
- 关闭应用 慎用
localhost:8000/luyuni/actuator/shutdown
自定义端点
@Endpoint(id = "datetime")
public class DateTimeEndPoint {
private String format = "yyyy-MM-dd HH:mm:ss";
/**
* 用来显示监控指标
* localhost:8000/luyuni/actuator/datetime
* */
@ReadOperation
public Map<String, Object> info() {
Map<String, Object> info = new HashMap<>();
info.put("name", "yulu");
info.put("age", "19");
info.put("datetime", new SimpleDateFormat(format).format(new Date()));
return info;
}
/**
* 动态更改监控指标 Post方式传入{"format":"yyyy-MM-dd"}
* */
@WriteOperation
public void setFormat(String format) {
this.format = format;
}
}
自定义端点配置类
@Configuration
public class DateTimeEndpointConfig {
@Bean
@ConditionalOnMissingBean //这个bean缺少时就注入bean
@ConditionalOnEnabledEndpoint //只有开启时才注入
public DateTimeEndPoint dateTimeEndPoint() {
return new DateTimeEndPoint();
}
}
自定义Starter
主要是定义 spring.factories
,让其加载自动配置类 XxxAutoConfigure
- 创建maven工程定义好相应坐标
<groupId>top.luyuni.springboot</groupId>
<artifactId>split-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
- 编写服务功能
// ...
- 定义自实现
XxxAutoConfigure
@Configuration
@ConditionalOnClass(value = {ISplitService.class, SplitServiceImpl.class})//classpath发现这些类就进行自动配置
public class SplitAutoConfigure {
@Bean
@ConditionalOnMissingBean //上下文环境中不存在就配置
ISplitService starterService(){
return new SplitServiceImpl();
}
}
- 定义
spring.factories
META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=top.luyuni.springboot.configure.SplitAutoConfigure
- 放入maven仓库
split-spring-boot-starter>mvn clean install -Dmaven.test.skip=true