Starter機制自己實現基於SPI,不少框架都使用了java的SPI機制,如java.sql.Driver的SPI實現(mysql驅動、oracle驅動等)、common-logging的日誌接口實現、dubbo的擴展實現等等框架,Starter是Spring Boot中的一個很是重要的概念,Starter至關於模塊,它能將模塊所需的依賴整合起來並對模塊內的 Bean 根據環境( 條件)進行自動配置。使用者只須要依賴相應功能的 Starter,無需作過多的配置和依賴,SpringBoot 就能自動掃描並加載相應的模塊。SpringBoot 存在不少開箱即用的 Starter 依賴,使得咱們在開發業務代碼時可以很是方便的、不須要過多關注框架的配置,而只須要關注業務便可。要熟悉Starter機制,首先須要瞭解@SpringBootApplication註解
@SpringBootApplication注解
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { @AliasFor(annotation = EnableAutoConfiguration.class) Class<?>[] exclude() default {}; @AliasFor(annotation = EnableAutoConfiguration.class) String[] excludeName() default {}; @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") Class<?>[] scanBasePackageClasses() default {}; }
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; /** * Exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ Class<?>[] exclude() default {}; /** * Exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */ String[] excludeName() default {}; }
主要包含:@AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class)java
@Import註解:把多個分來的容器配置合併在一個配置中,xml 形式下有一個<import resource/> 形式的註解,在JavaConfig 中所表達的意義是同樣的。mysql
@Import(AutoConfigurationImportSelector.class)中AutoConfigurationImportSelector的類圖react
實現了ImportSelector接口,重寫了selectImports方法web
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } //加載 "META-INF/spring-autoconfigure-metadata.properties"文件,裏面都是加載bean的conditon 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); //記載配置文件 META-INF/spring.factories中EnableAutoConfiguration.class爲key的value集合 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); //去重和根據條件Condition加載bean到Ioc容器中
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); }
spring-autoconfigure-metadata.properties中的小部份內容spring
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration.Configuration=
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,org.springframework.data.cassandra.core.ReactiveCassandraTemplate,reactor.core.publisher.Flux
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository
ConditionalOnClass表明必須存在後面的class纔會加載,autoConfigureAfter表明後面bean實例化後再實例化當前bean,還有其餘條件,這裏只是部分,目的就是根據條件加載bean。sql
META-INF/spring.factories中EnableAutoConfiguration.class爲key的value集合apache
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
AutoConfigurationPackages.Registrar.classjson
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//實現動態註冊IOC register(registry, new PackageImport(metadata).getPackageName()); } @Override public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new PackageImport(metadata)); } }
直接用這三個註解也能夠啓動 springboot 應用,只是每次配置三個註解比較繁瑣,因此直接用一個複合註解更方便些。
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.8.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.36</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.6</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>2.0.4.RELEASE</version> </dependency> </dependencies>
定義接口JsonFormat,用來把java bean解析成Stringspringboot
public interface JsonFormat<T> { String parse (T t) throws JsonProcessingException; }
兩個實現類,一個基於FastJson,一個基於Jacksonoracle
public class fastJsonFormat<T> implements JsonFormat<T> { @Override public String parse(T o) { String s = JSON.toJSONString(o); return s; } }
public class JacksonFormat<T> implements JsonFormat<T> { @Override public String parse(T t) throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); String s = objectMapper.writeValueAsString(t); return s; } }
動態配置屬性類 FormatConfigProperties
@ConfigurationProperties(prefix = "json.format", ignoreUnknownFields = true) public class FormatConfigProperties { public FormatConfigProperties() { } public FormatConfigProperties(String name1, String name2, String name3) { this.name1 = name1;this.name2 = name2;this.name3 = name3; } private String name1; private String name2; private String name3; public String getName1() { return name1; } public void setName1(String name1) { this.name1 = name1; } public String getName2() { return name2; } public void setName2(String name2) { this.name2 = name2; } public String getName3() { return name3; } public void setName3(String name3) { this.name3 = name3; } }
實例化兩種 JsonFormat
@Configuration public class JsonFormatConfiguration { @ConditionalOnClass(name ="com.alibaba.fastjson.JSON") @Bean @Primary public JsonFormat stringFormat(){ return new fastJsonFormat(); } @ConditionalOnClass(name = "com.fasterxml.jackson.databind") @Bean public JsonFormat jsonFormat(){ return new JacksonFormat(); } }
實現動態注入的類JsonFormatAutoConfiguration
@EnableConfigurationProperties(FormatConfigProperties.class) @Import({JsonFormatConfiguration.class}) @Configuration public class JsonFormatAutoConfiguration { @Bean public JsonFormatTemplate jsonFormatTemplate(JsonFormat jsonFormat,FormatConfigProperties formatConfigProperties) { return new JsonFormatTemplate(jsonFormat,formatConfigProperties); } }
最重要的一步,也是最後一步,在當前項目的resources目錄下新建 META-INF/spring.factories 文件裏面內容以下
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.json.config.JsonFormatAutoConfiguration
基於springboot自帶的@EnableAutoConfiguration註解,就用將須要的bean注入Ioc容器中,實現動態注入bean,而後把當前項目打成jar包,須要用到此功能的項目直接pom引入便可。
@RunWith(SpringRunner.class) @SpringBootTest public class DemoApplicationTests { @Autowired private JsonFormatTemplate jsonFormatTemplate; @Test public void contextLoads() throws JsonProcessingException { Person person = new Person("meixi", "阿根廷", 18); System.out.println( jsonFormatTemplate.parse(person)); } }
上面的starter demo自己沒有實際意義,真實項目中按照這個方式定製Starter便可。