一文帶你了解SpringBoot中常用注解的原理和使用
@AutoConfiguration
讀取所有jar包下的 /META-INF/spring.factories 并追加到一個(gè) LinkedMultiValueMap 中。每一個(gè)url中記錄的文件路徑如下:
file:/C:/Users/wangchao/apache-maven-3.5.0/repo/com/baomidou/mybatis-plus-boot-starter/3.5.1/mybatis-plus-boot-starter-3.5.1.jar!/META-INF/spring.factories
按照如下路徑查看
// 1. @EnableAutoConfiguration @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { } // 2. AutoConfigurationImportSelector.class#selectImports() public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } } // 3. AutoConfigurationImportSelector.class#getAutoConfigurationEntry() 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); } // 4. AutoConfigurationImportSelector.class#getCandidateConfigurations() protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; } // 5. org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames() public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); }
最終使用 loadSpringFactories(@Nullable ClassLoader classLoader) 方法讀取所有配置文件。
// 6. org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories() private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { // 讀取所有jar包下的 /META-INF/spring.factories URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
tomcat的自動(dòng)配置內(nèi)置于springboot的autoconfiguration中。參考tomcat的自動(dòng)配置:SpringBoot如何實(shí)現(xiàn)Tomcat自動(dòng)配置
mybatis-plus的配置沒(méi)有被springboot包括。因此mybatis-stater中包含一個(gè)包mybatis-spring-boot-autoconfigure,這其中配置了需要自動(dòng)配置的類。
因此我們也可以在自己的項(xiàng)目下新建 /META-INF/spring.factories ,并配置自動(dòng)配置類。
@Import
@Import 用于導(dǎo)入配置類或需要前置加載的類。被導(dǎo)入的類會(huì)注冊(cè)為Bean,可直接作為Bean被引用。它的 value 屬性可以支持三種類型:
- 被 @Configuration 修飾的配置類、或普通類(4.2版本之后可以)。
- ImportSelector 接口的實(shí)現(xiàn)。
- ImportBeanDefinitionRegistrar 接口的實(shí)現(xiàn)。
@Import 的配置
@Configuration @Import(value = {TestA.class, TestB.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class}) public class ConfigurationTest { }
導(dǎo)入一個(gè)普通類
package com.example.ssmpdemo.entity; public class TestA { public void fun(){ System.out.println("testA"); } }
導(dǎo)入一個(gè)配置類
package com.example.ssmpdemo.entity; import org.springframework.context.annotation.Configuration; @Configuration public class TestB { public void fun(){ System.out.println("testB"); } }
通過(guò)實(shí)現(xiàn) ImportSelector 接口
import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.example.ssmpdemo.entity.TestC"}; } }
通過(guò)重寫 ImportBeanDefinitionRegistrar 的 registerBeanDefinitions 方法。
import com.example.ssmpdemo.entity.TestD; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { RootBeanDefinition root = new RootBeanDefinition(TestD.class); registry.registerBeanDefinition("testD", root); } }
@ConfigurationProperties
- 支持常見(jiàn)的下劃線、中劃線和駝峰的轉(zhuǎn)換。支持對(duì)象引導(dǎo)。比如:user.friend.name 代表的是user對(duì)象中的friend對(duì)象中的name
- 需要有set()方法
- 只添加 @ConfigurationProperties(prefix = "xxx") 并不會(huì)生效,需要配合 @Configuration 讓容器識(shí)別到。
- @EnableConfigurationProperties(value = ConfigData.class ) 會(huì)將value中指定的類注冊(cè)為Bean,可直接用 @AutoWired 引用。
1.定義一個(gè)類用來(lái)記錄所有字段,并使用@ConfigurationProperties(prefix = "xxx")將數(shù)據(jù)注入到ConfigData中。
package com.example.ssmpdemo.entity; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; /** * 用來(lái)記錄Configuration的數(shù)據(jù) * @author wangc */ @Data @ConfigurationProperties(value = "spring.datasource.druid") public class ConfigData { private String driverClassName; private String url; private String username; private String password; } # 對(duì)應(yīng)的yml文件 spring: datasource: druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:5506/ssmpdemo?serverTimezone=UTC username: root password: xxxx
2.使用@EnableConfigurationProperties(JDBCProperties.class) 將 ConfigData 注冊(cè)為Bean,并提供給ConfigurationTest使用 。可將ConfigData作為參數(shù)注入到構(gòu)造函數(shù)和普通函數(shù)中。
3.可以用以下方式引用被@ConfigurationProperties(value = "spring.datasource.druid")修飾的ConfigData
可以直接把 ConfigData 當(dāng)成Bean使用
/** * 可直接被注入 */ @Autowired private ConfigData configData;
可以用構(gòu)造函數(shù)傳入進(jìn)來(lái)
@Data @Configuration @EnableConfigurationProperties(value = ConfigData.class ) public class ConfigurationTest { private ConfigData configData2; /** * 作為構(gòu)造函數(shù)的參數(shù)注入 * @param data */ ConfigurationTest(ConfigData data){ this.configData2 = data; }
也可以作為@Bean的方法函數(shù)的參數(shù)。只有當(dāng)前類(ConfigurationTest)才可
/** * 直接作為函數(shù)的參數(shù) * @param data * @return */ @Bean(name = "configData2") HashMap<String, String> getBean(ConfigData data){ return new HashMap<>(0); }
可以省略ConfigData直接將字段注入到返回結(jié)果中。
@Bean @ConfigurationProperties(value = "spring.datasource.druid") HashMap<String, String> getBean2(ConfigData data){ // 會(huì)自動(dòng)為hashMap賦值,或使用set方法為對(duì)象賦值 return new HashMap<>(); }
EnableConfigurationProperties注解的內(nèi)部如下,它導(dǎo)入了一個(gè)實(shí)現(xiàn)了 ImportBeanDefinitionRegistrar 接口的類。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(EnableConfigurationPropertiesRegistrar.class)
class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { registerInfrastructureBeans(registry); ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry); // 獲得@EnableConfigurationProperties的value指向的對(duì)象,并注冊(cè)。 getTypes(metadata).forEach(beanRegistrar::register); }
到此這篇關(guān)于一文帶你了解SpringBoot中常用注解的原理和使用的文章就介紹到這了,更多相關(guān)SpringBoot注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java http加簽、驗(yàn)簽實(shí)現(xiàn)方案詳解
這篇文章主要介紹了Java http加簽、驗(yàn)簽實(shí)現(xiàn)方案詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-07-07Spring IOC源碼之bean的注冊(cè)過(guò)程講解
這篇文章主要介紹了Spring IOC源碼之bean的注冊(cè)過(guò)程講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09Java wait和notifyAll實(shí)現(xiàn)簡(jiǎn)單的阻塞隊(duì)列
這篇文章主要介紹了Java wait和notifyAll實(shí)現(xiàn)簡(jiǎn)單的阻塞隊(duì)列,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10Java中OkHttp 超時(shí)設(shè)置的實(shí)現(xiàn)
超時(shí)設(shè)置是網(wǎng)絡(luò)編程中不可忽視的一部分,本文就來(lái)介紹一下Java中OkHttp 超時(shí)設(shè)置的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06java控制臺(tái)實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java控制臺(tái)實(shí)現(xiàn)學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02Java實(shí)現(xiàn)Map遍歷key-value的四種方法
本文主要介紹了Java實(shí)現(xiàn)Map遍歷key-value的四種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07Java學(xué)習(xí)關(guān)于循環(huán)和數(shù)組練習(xí)題整理
在本篇文章里小編給各位整理了關(guān)于Java學(xué)習(xí)關(guān)于循環(huán)和數(shù)組練習(xí)題相關(guān)內(nèi)容,有興趣的朋友們跟著參考學(xué)習(xí)下。2019-07-07