做者:追梦1819
原文:https://www.cnblogs.com/yanfei1819/p/11058502.html
版权声明:本文为博主原创文章,转载请附上博文连接!
html
这一段时间项目赶进度,故该系列博客更新没有以前那么频繁,望谅解。java
SpringBoot 用起来方便,它默认集成了 Java 的主流框架。这也是 SpringBoot 的一大特点,使用方便,须要什么框架或者技术,只须要引入对应的 starter 便可。目前官方已经集成的各大技术的启动器,能够查看 文档。git
做者最开始考虑该话题的是曾经的一个面试题:如何自定义一个自定义启动器?github
本文将围绕该面试题进行讲解。面试
在自定义 starter 以前,咱们先回顾 SpringBoot 官方已经集成的 starter 。咱们使用时,只需引入对应的 spring-boot-starter-xxx
便可使用(即咱们常说的开箱即用)。spring
同时,还预先设置了默认值,若是须要修改这些默认值,只须要在 application.properties 或 application.yml 配置文件中修改。例如:SpringBoot 默认的端口号是 8080,若是须要修改该端口号,只须要在 application.properties 中添加属性 server.port=9090
便可。springboot
建立自定义启动器,须要建立如下两个组件:app
首先,建立自定义starter工程,并引入maven依赖:框架
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>2.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <version>2.0.0.RELEASE</version> <optional>true</optional> </dependency>
而后,建立实体类。前缀加上字段名称能够在application.properties文件中建立属性的名称。maven
package com.yanfei1819.springbootstarter.entity; import org.springframework.boot.context.properties.ConfigurationProperties; /** * Created by 追梦1819 on 2019-05-10. */ @ConfigurationProperties(prefix = "spring.person") public class PersonProperties { private String name; private int age; private double salary; // set/get 省略 }
第三步,定义核心服务类,该类主要定义了 starter 的核心功能。
package com.yanfei1819.springbootstarter.service; import com.yanfei1819.springbootstarter.entity.PersonProperties; /** * Created by 追梦1819 on 2019-05-10. */ public class PersonService { private PersonProperties properties; public PersonService(PersonProperties properties) { this.properties = properties; } public PersonService() { } public void say() { System.out.println("hello,I am " + properties.getName() + ",and I am " + properties.getAge() + ",and My salary " + properties.getSalary()); } }
第四步,自定义配置类。一般状况每一个 starter 至少有一个配置类。命名规则也很明显,通常命名规则使用XxxAutoConfiguration, 例如 RedisAutoConfiguration 等。该类将核心功能注入到 SpringBoot 上下文中。
package com.yanfei1819.springbootstarter.configuration; import com.yanfei1819.springbootstarter.entity.PersonProperties; import com.yanfei1819.springbootstarter.service.PersonService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Created by 追梦1819 on 2019-05-10. */ @Configuration @EnableConfigurationProperties(PersonProperties.class) @ConditionalOnClass(PersonService.class) @ConditionalOnProperty(prefix = "spring.person", value = "enabled", matchIfMissing = true) public class PersonServiceAutoConfiguration { @Autowired private PersonProperties properties; @Bean @ConditionalOnMissingBean(PersonService.class) // 当容器中没有指定Bean的状况下,自动配置PersonService类 public PersonService personService() { PersonService personService = new PersonService(properties); return personService; } }
最后,建立 spring.factories 文件:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.yanfei1819.springbootstarter.configuration.PersonServiceAutoConfiguration
当 SpringBoot 启动的时候,它会在类路径中查找 spring.factories 文件,此条件初始化由@ConditionalOnClass注释启用。此文件将名称映射到Spring Boot将尝试运行的不一样配置类。所以,根据这个片断,Spring Boot将尝试运行RabbitMQ,Cassandra,MongoDB和Hibernate的全部配置类。
@EnableAutoConfiguration 的关键功能是使用 SpringFactoriesLoader.loadFactoryNames 方法来扫描具备 META-INF/spring.factories 文件的 jar 包,这样咱们的自动配置类才能生效,因此咱们在 autoconfigure 模块的 resources 下建立 META-INF/spring.factories 文件。
新建立一个工程,引入自定义启动器的 maven 依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.yanfei1819</groupId> <artifactId>customize-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
此处咱们要注意一下命名规则,官方命名是spring-boot-starter-xxx
, 自定义命名是 xxx-spring-boot-starter
。
而后再配置文件中写入测试数据:
spring.person.name=starter spring.person.age=26
下面咱们修改启动类作个简单的测试:
package com.yanfei1819.customizestartertest; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class CustomizeStarterTestApplication implements CommandLineRunner { @Value("${spring.person.name}") private String name; @Value("${spring.person.age}") private int age; public static void main(String[] args) { SpringApplication.run(CustomizeStarterTestApplication.class, args); } @Override public void run(String... args) throws Exception { System.out.println("姓名是:"+name+",年龄是:"+age); } }
上面的启动类实现了 CommandLineRunner 接口,并重写了 run 方法。Spring boot的CommandLineRunner
接口主要用于实如今应用初始化后,去执行一段代码块逻辑,这段初始化代码在整个应用生命周期内只会执行一次。(该接口的做用能够参照官网说明)。
此处是为了省去写测试类才实现了该接口。
最后,启动项目,能够看到以下结果:
根据以上的分析和示例,能够大概总结出 starter 的工做流程:
SpringBoot 启动时寻找含 spring.factories 文件的JAR包;
读取spring.factories文件获取配置的自动配置类AutoConfiguration;
将自动配置类下知足条件(@ConditionalOnXxx)的@Bean放入到 Springoot 上下文中;
开发者直接使用。
SpringBoot 的自定义启动器极大的方便了独立功能 jar 的开发,消除了大量的配置工做。
依葫芦画瓢,要写一个自定义的starter,其实很简单,注意几个注解就能够了。可是,咱们真正要作的,是经过源码来理解自动配置原理,原理是灵魂,知其然,知其因此然,这样去自定义 starter 才会驾轻就熟。后续咱们将继续分享 SpringBoot 的自动配置原理。