自动配置
自动配置是SpringBoot最重要的属性,它大大降低了配置的复杂性,使开发者更注重编码本身
原理
先来看看静态的
-> @SpringBootApplication
-> @EnableAutoConfiguration
-> AutoConfigurationImportSelector
AutoConfigurationImportSelector
在spring-boot-autoconfigure包下,在这里进行了自动配置的管理。
通过getCandidateConfigurations
获取了在根目录下META=INF/spring.factoies中配置的org.springframework.boot.autoconfigure.EnableAutoConfiguration
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;
}
动态看:
-> AbstractApplicationContext::refresh
-> PostProcessorRegistrationDelegate::invokeBeanDefinitionRegistryPostProcessors
-> ConfigurationClassPostProcessor::processConfigBeanDefinitions
-> ConfigurationClassParser::processGroupImports
-> AutoConfigurationImportSelctor::process
->AutoConfigurationImportSelctor::getAutoConfigurationEntry
-> AutoConfigurationImportSelctor::getCandidateConfigurations
总之是在AppliactionContext进行刷新时,进行了自动注入的加载与配置
条件注解
以DataSourceAutoConfiguration
为例,来看看自动配置的实现
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
@Configuration
@Conditional(EmbeddedDatabaseCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import(EmbeddedDataSourceConfiguration.class)
protected static class EmbeddedDatabaseConfiguration {
}
@Configuration
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {
}
...
}
这个是DataSourceAutoConfiguration的配置,自定义配置是建立在条件注解:@Conditional基础之上的。
来看看它们:
分类 | 注解 | 描述 |
| @Conditional | |
类条件 | @ConditionalOnClass | 当容器有指定类的条件下,在类上使用 |
| @ConditionalOnMissingClass | 当容器没有指定类的情况下 |
Bean条件 | @ConditionalOnBean | 当容器有指定Bean的条件下,在Bean方法上使用 |
| @ConditionalOnMissingBean | 当容器没有指定Bean的情况下 |
属性条件 | @ConditionalOnProperty | 指定的属性是否有指定的值 |
Resource条件 | @ConditionalOnResource | 注释仅允许在存在特定资源时执行自动配置 |
自定义自动配置
基本步骤如下:
- 编写Java Config,使用@Configutation注解
- 添加条件,@Conditional
- 定位自动配置,在META-INF/spring.factories
示例:
使用的极客时间的示例
@Slf4j
public class GreetingApplicationRunner implements ApplicationRunner {
public GreetingApplicationRunner() {
log.info("Initializing GreetingApplicationRunner.");
}
public void run(ApplicationArguments args) throws Exception {
log.info("Hello everyone! We all like Spring! ");
}
}
这个类没有注入
自动配置类:
对于GreetingApplicationRunner进行注入,当没有GreetingApplicationRunner的bean且greeting.enabled属性为true时,自动注入GreetingApplicationRunner
@Configuration
@ConditionalOnClass(GreetingApplicationRunner.class)
public class GreetingAutoConfiguration {
@Bean
@ConditionalOnMissingBean(GreetingApplicationRunner.class)
@ConditionalOnProperty(name = "greeting.enabled", havingValue = "true", matchIfMissing = true)
public GreetingApplicationRunner greetingApplicationRunner() {
return new GreetingApplicationRunner();
}
}
并且在resouse/META-INF下,增加spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
geektime.spring.hello.greeting.GreetingAutoConfiguration
这样在SpringBootApplication 启动时,就可以自动注入GreetingApplicationRunner了。
起步依赖
基本概念
起步依赖,也就是starter特性,这也是SpringBoot的重要特性,通过它可以简化项目初始搭建以及开发过程。
在Spring
时代,搭建一个 Web 应用通常需要在 pom 文件中引入多个 Web 模块相关的 Maven 依赖,如:SpringMvc、Tomcat
等依赖,而 SpringBoot 则只需引入spring-boot-starter-web
依赖即可,它是一个功能模块的所有 Maven 依赖集合体。
下面列出常用的几个:
名称 | 功能 |
spring-boot-starter-web | 支持 Web 开发,包括 Tomcat 和 spring-webmvc |
spring-boot-starter-redis | 支持 Redis 键值存储数据库,包括 spring-redis |
spring-boot-starter-test | 支持常规的测试依赖,包括 JUnit、Hamcrest、Mockito 以及 spring-test 模块 |
spring-boot-starter-aop | 支持面向切面的编程即 AOP,包括 spring-aop 和 AspectJ |
spring-boot-starter-data-elasticsearch | 支持 ElasticSearch 搜索和分析引擎,包括 spring-data-elasticsearch |
spring-boot-starter-jdbc | 支持JDBC数据库 |
spring-boot-starter-data-jpa | 支持 JPA ,包括 spring-data-jpa、spring-orm、Hibernate |
引入方式
在项目的pom.xml中,都会存在父级依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
而它的父级为spring-boot-dependencies,在其中管理很多的starter依赖
PS: 多module,将依赖都集中管理在主pom中,定义统一的dependencyManagement ,对version号以及exclusion进行管理
运行监控
SpringBoot提供运行监控的特性:Actuator。前一段时间,研究过promethus,通过它可以对操作系统资源、容器、k8s、网络、中间件等各层进行监控,这里Actuator也可以融入到promethus的监控体系中。
Actuator提供了针对应用程序的监控与控制,这些监控与控制 被封装成了EndPoint,Metics是其中一种重要的EndPoint,它专门一些度量信息。
EndPoint
EndPoint这个概念用处挺广,可以代表一个端口,这里理解成地址或者访问点。
常用的EndPonit如下:
ID | Description | 默认开启 | 默认HTTP | 默认JMX |
beans | 显示应用中的全部Bean | | | |
caches | 显示应用中的缓存 | | | |
conditions | 显示自动注入时的匹配条件情况,类似于--debug | | | |
configprops | 展示@ConfigurationProperties 信息 | | | |
env | 展示 ConfigurableEnvironment 的信息 | | | |
health | 显示健康检查信息 | | Y | |
httptrace | 显示Http Trace信息,默认最后100个请求 | | | |
info | 显示设置好的应用信息 | | Y | |
loggers | 显示并更新日志配置 | | | |
metrics | 显示metrics | | | |
mappings | 显示全部RequestMapping | | | |
quartz | 显示Quartz的调度任务 | | | |
scheduledtasks | 显示应用的调度任务 | | | |
sessions | 允许从Spring Session store中获取或删除session | | | |
shutdown | 关闭应用 | N | | |
startup | 启动应用 | | | |
threaddump | 显示线程信息 | | | |
heapdump | 显示堆栈 | | | N/A |
prometheus | 将metrics暴露给Promethues | | | N/A |
jmx可以通过JConsole来进行访问
配置:
management.server.address=
//将Actuator的端口配置成与系统不同的端口,然后将该关口不开放到外部。
management.server.port=
management.endpoints.web.base-path = /actuator
management.endpoints.web.path-mapping.<endpoint>=路径
//开启endpoint
management.endpoint.<endpoint>.enabled=true
management.endpoints.enabled-by-default=false
//暴露endpoint
management.endpoints.jmx.exposure.exclude=
management.endpoints.jmx.exposure.include=*
management.endpoints.web.exposure.exclude=
management.endpoints.web.exposure.include=info,health
访问:
/actuator/<endpoint>
自定义健康指标:
-
引入依赖
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
-
配置
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
-
实现HealthIndicator接口
@Component
public class CoffeeIndicator implements HealthIndicator {
@Autowired
private CoffeeService coffeeService;
@Override
public Health health() {
long count = coffeeService.getCoffeeCount();
Health health;
if (count > 0) {
health = Health.up()
.withDetail("count", count)
.withDetail("message", "We have enough coffee.")
.build();
} else {
health = Health.down()
.withDetail("count", 0)
.withDetail("message", "We are out of coffee.")
.build();
}
return health;
}
}
Metrics
大部分的EndPoint都是对SpringBoot资源的监控,而Metrics不同,将CPU、内存、JVM、线程、文件句柄等都进行了监控。
通过/actual/metrics访问
{
"mem": 71529,
"mem.free": 15073,
"processors": 4,
"instance.uptime": 6376,
"uptime": 9447,
"systemload.average": -1.0,
"heap.committed": 48024,
"heap.init": 16384,
"heap.used": 32950,
"heap": 506816,
"nonheap.committed": 23840,
"nonheap.init": 160,
"nonheap.used": 23506,
"nonheap": 0,
"threads.peak": 25,
"threads.daemon": 23,
"threads.totalStarted": 28,
"threads": 25,
"classes": 6129,
"classes.loaded": 6129,
"classes.unloaded": 0,
"gc.copy.count": 74,
"gc.copy.time": 173,
"gc.marksweepcompact.count": 3,
"gc.marksweepcompact.time": 88,
"httpsessions.max": -1,
"httpsessions.active": 0
}
一些配置:
management.metrics.export.*
management.metrics.tags.*
management.metrics.enable.*
management.metrics.distribution.*
management.metrics.web.server.auto-time-requests
可以自定义度量接口,有3种方式:
- 通过MeterRegistry注册Meter(Meter是核心接口)
- 通过MeterBinder Bean让Spring Boot自动绑定
- 通过MeterFilter进行定制
以下实现MeterBinder接口,通过bindTo进行配置。
@Service
@Transactional
@Slf4j
public class CoffeeOrderService implements MeterBinder {
@Autowired
private CoffeeOrderRepository orderRepository;
private Counter orderCounter = null;
public CoffeeOrder get(Long id) {
return orderRepository.getOne(id);
}
public CoffeeOrder createOrder(String customer, Coffee...coffee) {
CoffeeOrder order = CoffeeOrder.builder()
.customer(customer)
.items(new ArrayList<>(Arrays.asList(coffee)))
.state(OrderState.INIT)
.build();
CoffeeOrder saved = orderRepository.save(order);
log.info("New Order: {}", saved);
orderCounter.increment();
return saved;
}
public boolean updateState(CoffeeOrder order, OrderState state) {
if (state.compareTo(order.getState()) <= 0) {
log.warn("Wrong State order: {}, {}", state, order.getState());
return false;
}
order.setState(state);
orderRepository.save(order);
log.info("Updated Order: {}", order);
return true;
}
@Override
public void bindTo(MeterRegistry meterRegistry) {
this.orderCounter = meterRegistry.counter("order.count");
}
}
Admin
在Actuator基础上,提供了一套管理界面,将监控的信息能够更好的展现出来。在单应用方面会更好一些,而在集群方面,最好是通过Promethues来实现更全面的监控。
下边简单做个说明:
-
服务端
依赖: spring-boot-admin-starter-server
配置:@EnableAdminServer
-
客户端
依赖:spring-boot-admin-starter-client
配置:
spring.boot.admin.client.url = http://localhost:8081
management.endpoints.web.exposure.include=*
admin可以通过spring-security进行保护
server收集各个client上的信息,进行统一的页面展示