新品釋出季購物季臨近,晶片短缺問題有增無減
springboot的自動裝配是starter的基礎,簡單來說,就是將Bean裝配到Ioc,本文我們先學習redis的starter如何實現自動裝配,然後手寫一個redis的starter的,來學習spring如何通過starter實現自動裝配。
一、學習spring-boot-starter-data-redis如何實現自動裝配
首先,新建一個springboot專案,新增starter依賴
compile("org.springframework.boot:spring-boot-starter-data-redis")
在yml中新增redis資料來源:
redis: database:8 host: 127.0.0.1 # password: port: 6379 timeout: 1000ms lettuce: pool: max-active: 20 min-idle: 1 max-idle: 8 max-wait: 10000
編寫一個controller,測試
/** * @author cgg * @version 1.0 * @date 2021/4/1 */ @RestController @Slf4j @Api(tags = "測試") @RequestMapping("test") public class TestController { @Resource private RedisTemplate redisTemplate; @GetMapping("/") @ApiOperation("測試") public void helloWorld() { System.out.println("hello world"); } }
可以看到,在專案中,我們並沒有使用註解或者xml將redisTemplate注入到Ioc容器中就可以使用,說明容器中已經存在了,其實這就是springBoot的自動裝配。
其實springboot 通過一個starter依賴就能實現自動裝配,是starter遵守了約定規範,才實現了自動裝配,下面我們就學習一下原理,並學習starter的規範,為我們手寫自己的starter做準備。
springboot實現自動裝配是通過 @SpringBootApplication 註解中的@EnableAutoConfiguration實現的。
@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 { ....... }
接下來,再看@EnableAutoConfiguration的原始碼
@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 {}; }
其中使用了@import 匯入了AutoConfigurationImportSelector類,那我們我們繼續往下看AutoConfigurationImportSelector 實現了 DeferredImportSelector,而DeferredImportSelector實現了ImportSelector
其中的重寫了selectImports ,返回了一個String【】陣列,spring把返回的陣列中的類名全部裝配到容器中。繼續看AutoConfigurationImportSelector程式碼。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry(); private static final String[] NO_IMPORTS = {}; private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class); private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude"; private ConfigurableListableBeanFactory beanFactory; private Environment environment; private ClassLoader beanClassLoader; private ResourceLoader resourceLoader; private ConfigurationClassFilter configurationClassFilter; @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); //可以看到 autoConfigurationEntry.getConfigurations()才是需要裝配的類名稱陣列。那麼就需要檢視 getAutoConfigurationEntry(annotationMetadata)
} ......
繼續跟到getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) 方法中
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
由於呼叫棧過深,我說下原始碼的呼叫鏈:getCandidateConfigurations ->loadFactoryNames ->loadSpringFactories ->
在loadSpringFactories 可以發現下面的程式碼
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
而 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; 接下來我們再看一下專案啟動後,getCandidateConfigurations返回值。
這裡看到第一個返回值是MybatisPlusAutoConfiguration 那麼他是怎麼來的呢,再看下一張圖
相信看到這裡,大多數同學已經明白了,其實就是獲取了 每一個 starter 的 "META-INF/spring.factories" 中宣告類全名。
然後再說一下getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) 其他方法的含義: