SpringBoot之一:特性

自动配置

自动配置是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如下:

IDDescription默认开启默认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上的信息,进行统一的页面展示

# spring 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×