SpringBoot的核心思想是約定優(yōu)于配置,它簡化了之前使用SpringMVC時候的大量配置xml,使得開發(fā)者能夠快速的創(chuàng)建一個Web項目。那么SpringBoot是如何做到的呢?
@SpringBootApplication
當我們創(chuàng)建一個SpringBoot項目完成后,會有一個啟動類,直接就可以運行web項目了。所以我們首先從這個啟動類的注解上出發(fā),看看SpringBoot是如何實現(xiàn)的。
@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
)
//...
}
可以看到@SpringBootApplication主要是三個注解的復合注解。
@SpringBootConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
這個注解最簡單,它是對@Configuration的封裝,@Configuration我們最熟悉不過了,這里就不做分析了。
@ComponentScan
這個注解的作用主要是掃描定義的包下的所有的包含@Controller、@Service、@Component、@Repository等注解的類,把他們注冊到Spring的容器中。具體是如何掃描,如何加載注解信息、如何生成Bean以及如何注冊到Spring容器中,這里的邏輯相對來說比較復雜,不是本文的重點,不具體分析了。
@EnableAutoConfiguration
EnableAutoConfiguration這個注解就是比較核心的了,實現(xiàn)自動裝配就是依賴這個注解,下面我們一步一步來看是如何實現(xiàn)的。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
可以看到EnableAutoConfiguration注解中,主要依賴兩個注解,AutoConfigurationPackage和Import(AutoConfigurationImportSelector.class),這兩個注解的作用都是根據(jù)條件動態(tài)的加載Bean到Spring容器中,下面具體分析。
@AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
這里主要是@Import(AutoConfigurationPackages.Registrar.class)注解,Import注解一定很熟悉了,主要是將import的類注入Spring容器中,下面具體分析AutoConfigurationPackages.Registrar。
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
//重寫這個方法,根據(jù)AnnotationMetadata將bean注冊到spring容器中
//這里的AnnotationMetadata就是@SpringBootApplication注解的元數(shù)據(jù)
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//new PackageImport(metadata).getPackageName()返回的是SpringBootApplication注解對應的包名
//也就是啟動類所在的包名,所以,SpringBoot項目的啟動類和包名是有一定的要求的,這也是SpringBoot約定大約配置
//的一種體現(xiàn)
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
AutoConfigurationImportSelector
下面要分析的這個類就是整個自動裝配最關(guān)鍵的類了。查看源碼可以知道,AutoConfigurationImportSelector實現(xiàn)了ImportSelector接口,ImportSelector接口中的selectImports方法會根據(jù)返回的String[]數(shù)組,然后Spring根據(jù)數(shù)組中的類的全路徑類名把響應的類注入到Spring容器中。接著我們來看一下返回了哪些類。
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//加載元數(shù)據(jù),這里面主要是一些Condition條件,目的是為了根據(jù)條件判斷是否需要注入某個類
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;
}
//根據(jù)注解獲取注解的屬性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//使用SpringFactoryLoader加載classpath下所有的META-INF/spring.factories中,key是
//org.springframework.boot.autoconfigure.EnableAutoConfiguration的值
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//刪除重復的類
configurations = removeDuplicates(configurations);
//刪除被排除的類
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//根據(jù)上面loadMetadata方法加載的condition條件信息,過濾掉不符合條件的類
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
注意:這里getCandidateConfigurations方法是重點,SpringBoot中依賴的所有的starter都是基于此實現(xiàn)自動裝配的。這里用到了SPI。
SPI全稱為Service Provier Interface,是一種服務發(fā)現(xiàn)機制。SPI的本質(zhì)是將接口實現(xiàn)類的全限定名配置在文件中,并由服務加載器讀取配置文件,加載實現(xiàn)類。
SpringBoot的各種starter依賴都是基于此實現(xiàn)的,每個maven依賴的starter的包下都會有一個META-INF/spring.factories配置文件,里面都會有org.springframework.boot.autoconfigure.EnableAutoConfiguration鍵和對應的需要自動加載的類的全限定名。
實現(xiàn)一個Starter
根據(jù)上面的分析,下面簡單實現(xiàn)一個starter。
用IDEA創(chuàng)建一個簡單的Maven項目,然后創(chuàng)建相應的包名和類。

簡單說明一下這個starter中的作用
-
FormatTemplate類提供一個模版方法doFormat,可以將傳入的泛型對象輸出一個字符串
public class FormatTemplate {
private FormatProcessor formatProcessor;
public FormatTemplate(FormatProcessor formatProcessor) {
this.formatProcessor = formatProcessor;
}
public <T>String doFormat(T data) {
return formatProcessor.format(data);
}
}
-
FormatProcessor是一個接口,提供了一個format的方法,它有兩個實現(xiàn),StringFormatProcessor直接返回傳入對象的toString,JsonFormatProcessor根據(jù)用fastjson將傳入的對象轉(zhuǎn)成json字符串。
public class JsonFormatProcessor implements FormatProcessor {
@Override
public <T> String format(T data) {
return JSON.toJSONString(data);
}
}
public class StringFormatProcessor implements FormatProcessor {
@Override
public <T> String format(T data) {
return data.toString();
}
}
-
FormatAutoConfiguration利用@Configuration分別將JsonFormatProcessor和StringFormatProcessor注入到spring容器中,這里用了@Condition條件注解,只有當項目中引用了fastjson的時候,才會注入JsonFormatProcessor
@Configuration
public class FormatAutoConfiguration {
@Bean
@Primary
@ConditionalOnClass(name = "com.alibaba.fastjson.JSON")
public FormatProcessor jsonFormat(){
return new JsonFormatProcessor();
}
@Bean
@ConditionalOnMissingClass("com.alibaba.fastjson.JSON")
public FormatProcessor stringFormat(){
return new StringFormatProcessor();
}
}
-
TemplateAutoConfiguration引用FormatAutoConfiguration并注入了一個FormatTemplate。
@Configuration
@Import(FormatAutoConfiguration.class)
public class TemplateAutoConfiguration {
@Bean
public FormatTemplate formatTemplate(FormatProcessor formatProcessor) {
return new FormatTemplate(formatProcessor);
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(TemplateAutoConfiguration.class);
FormatTemplate bean = context.getBean(FormatTemplate.class);
FormatProcessor formatProcessor = context.getBean(FormatProcessor.class);
System.out.printf(bean.doFormat("aaa"));
System.out.println(formatProcessor.format("bbb"));
}
}
-
resources/META-INF下的spring.factories中定義了要自動裝配的類的全路徑,即TemplateAutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.cross.springbootdemo.autoconfiguration.TemplateAutoConfiguration
編寫好后,將項目進行打包,然后在其他項目中,就可以引入了,使用的時候直接可以用@Autowired引入FormatTemplate了。
@RestController
public class TestController {
@Autowired
private FormatTemplate formatTemplate;
@GetMapping(value = "test")
public String test() {
User user = new User();
user.setName("crossyf---");
user.setAge(18);
return formatTemplate.doFormat(user);
}
}
一個簡單的starter就完成了。