在学习Spring Boot的过程中,接触最多的就是starter。可以认为starter是一种服务——使得使用某个功能的开发者不需要关注各种依赖库的处理,不需要具体的配置信息,由Spring Boot自动通过classpath路径下的类发现需要的Bean,并织入bean。举个例子, spring-boot-starter-jdbc 这个starter的存在,使得我们只需要在BookPubApplication下用 @Autowired
引入DataSource的bean就可以,Spring Boot会自动创建DataSource的实例。
这里我们会用一个不太规范的starter展示Spring Boot的自动配置的运行原理。 Spring Boot的自动配置、Command-line Runner 一文中曾利用StartupRunner类在程序运行启动后首先查询数据库中书的数目,现在换个需求: 在系统启动后打印各个实体的数量 。
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot</artifactId> <!-- version继承父模块的--> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-commons</artifactId> <version>1.9.3.RELEASE</version> </dependency></dependencies>
package com.test.bookpubstarter.dbcount; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.CommandLineRunner; import org.springframework.data.repository.CrudRepository; import java.util.Collection; public class DbCountRunner implements CommandLineRunner { protected final Logger logger = LoggerFactory.getLogger(DbCountRunner.class); private Collection<CrudRepository> repositories; public DbCountRunner(Collection<CrudRepository> repositories) { this.repositories = repositories; } @Override public void run(String... strings) throws Exception { repositories.forEach(crudRepository -> { logger.info(String.format("%s has %s entries", getRepositoryName(crudRepository.getClass()), crudRepository.count())); }); } private static String getRepositoryName(Class crudRepositoryClass) { for (Class repositoryInterface : crudRepositoryClass.getInterfaces()) { if (repositoryInterface.getName().startsWith("com.test.bookpub.repository")) { return repositoryInterface.getSimpleName(); } } return "UnknownRepository"; } }
package com.test.bookpubstarter.dbcount; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.repository.CrudRepository; import java.util.Collection; @Configuration public class DbCountAutoConfiguration { @Bean public DbCountRunner dbCountRunner(Collection<CrudRepository> repositories) { return new DbCountRunner(repositories); } }
org.springframework.boot.autoconfigure.EnableAutoConfiguration=/ com.test.bookpubstarter.dbcount.DbCountAutoConfiguration
<dependency> <groupId>com.test</groupId> <artifactId>db-count-starter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
正规的starter是一个独立的工程,然后在maven中新仓库注册发布,其他开发人员就可以使用你的starter了。
常见的starter会包括下面几个方面的内容:
在应用程序启动过程中,Spring Boot使用 SpringFactoriesLoader 类加载器查找 org.springframework.boot.autoconfigure.EnableAutoConfiguration 关键字对应的Java配置文件。Spring Boot会遍历在各个jar包种META-INF目录下的 spring.factories 文件,构建成一个配置文件链表。除了 EnableAutoConfiguration 关键字对应的配置文件,还有其他类型的配置文件:
Spring Boot的starter在编译时不需要依赖Spring Boot的库。这个例子中依赖spring boot并不是因为自动配置要用spring boot,而仅仅是因为需要实现 CommandLineRunner 接口。
Spring Boot starter如何找到自动配置文件(xxxxAutoConfiguration之类的文件)?
本号专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。