日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 前端技术 > javascript >内容正文

javascript

SpringBoot自动装载

發(fā)布時(shí)間:2025/4/16 javascript 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot自动装载 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

SpringBoot中的自動(dòng)裝載

(1)ImportSelector接口

ImportSelector接口是Spring導(dǎo)入外部配置的核心接口,在SpringBoot的自動(dòng)化配置和@EnableXXX(功能性注解)中起到了決定性的作用。當(dāng)在@Configuration標(biāo)注的Class上使用@Import引入了一個(gè)ImportSelector實(shí)現(xiàn)類后,會(huì)把ImportSelector實(shí)現(xiàn)類中返回的Class名稱都定義為bean。

public interface ImportSelector {String[] selectImports(AnnotationMetadata var1); }

DeferredImportSelector接口繼承ImportSelector,他和ImportSelector的區(qū)別在于裝載bean的時(shí)機(jī)上,DeferredImportSelector需要等所有的@Configuration都執(zhí)行完畢后才會(huì)進(jìn)行裝載

public interface DeferredImportSelector extends ImportSelector {//...省略 }

(2)模擬SpringBoot自動(dòng)裝配的實(shí)現(xiàn)

接下來我們寫一個(gè)小例子,看下ImportSelector接口的用法

通過模擬實(shí)現(xiàn)ImportSelector接口來還原SpringBoot自動(dòng)裝配的原理(SpringBoot的自動(dòng)裝配原理就是通過封裝ImportSelector接口等一些列操作來實(shí)現(xiàn)的)

1)定義Bean對(duì)象

這個(gè)類模擬的是我們要自動(dòng)專配在SpringBoot容器中的Bean實(shí)例

public class User {private String username;private Integer age;//省略.. }

2)定義配置類Configuration

//定義一個(gè)configuration //注意這里并沒有使用spring注解 //spring掃描的時(shí)候并不會(huì)裝載該類 public class UserConfiguration {@Beanpublic User getUser() {return new User("張三",18);} }

3 ) 定義ImportSelector接口的實(shí)現(xiàn)類

ImportSelector 接口中的selectImports方法會(huì)將加載的類當(dāng)作一個(gè)配置類來初始化,就是相當(dāng)于@Configuration注解的功能。

public class UserImportSelector implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {//獲取配置類名稱return new String[]{UserConfiguration.class.getName()};} }

4) 定義EnableXXX注解

在SpringBoot在自動(dòng)裝配正式通過注解@EnableAutoCofiguration注解實(shí)現(xiàn)的,這里通過自定義EnableXXX注解模擬SpringBoot中的@EnableAutoConfiguration注解實(shí)現(xiàn)自動(dòng)裝配。

@SpringBoot源碼:

@Retention(RetentionPolicy.RUNTIME) @Documented @Target(ElementType.TYPE) @Import(UserImportSelector.class) public @interface EnableUserBean { }

5 ) 測(cè)試

/** * 通過在類上聲明@EnableUserBean,會(huì)自動(dòng)的加載所有對(duì)象 */ @EnableUserBean public class TestApplication {public static void main(String[] args) {//獲取sprinboot注解驅(qū)動(dòng)的容器對(duì)象AnnotationConfigApplicationContext applicationContext =new AnnotationConfigApplicationContext(TestApplication.class);//加載通過實(shí)現(xiàn)ImportSelector接口模擬SpringBoot自動(dòng)裝載功能,將聲明的User對(duì)象的實(shí)例裝配到SpringBoot的容器中//獲取SpringBoot容器中的User的實(shí)例對(duì)象User user = applicationContext.getBean(User.class);System.out.println(user);} }

由此可見,User對(duì)象并沒有使用Spring的對(duì)象創(chuàng)建注解聲明(@Controller,@Service,@Repostiroty),而是使用編程的方式動(dòng)態(tài)的載入bean。

這個(gè)接口在哪里調(diào)用呢?我們可以來看一下ConfigurationClassParser這個(gè)類的processImports方法

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection<SourceClass> importCandidates, boolean checkForCircularImports) {if (importCandidates.isEmpty()) {return;}if (checkForCircularImports && isChainedImportOnStack(configClass)) {this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));}else {this.importStack.push(configClass);try {for (SourceClass candidate : importCandidates) { //對(duì)ImportSelector的處理if (candidate.isAssignable(ImportSelector.class)) {// Candidate class is an ImportSelector -> delegate to it to determine importsClass<?> candidateClass = candidate.loadClass();ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry);if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) { //如果為延遲導(dǎo)入處理 則加入集合當(dāng)中this.deferredImportSelectors.add(new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));}else { //根據(jù)ImportSelector方法 的返回值來進(jìn)行遞歸操作String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);processImports(configClass, currentSourceClass, importSourceClasses, false);}}else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {// Candidate class is an ImportBeanDefinitionRegistrar - >// delegate to it to register additional bean definitionsClass<?> candidateClass = candidate.loadClass();ImportBeanDefinitionRegistrar registrar =BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);ParserStrategyUtils.invokeAwareMethods(registrar, this.environment, this.resourceLoader, this.registry);configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());}else { // 如果當(dāng)前的類既不是 ImportSelector也不是ImportBeanDefinitionRegistar就進(jìn)行@Configuration的解析處理// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->// process it as an @Configuration classthis.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());processConfigurationClass(candidate.asConfigClass(configClass));}}}catch (BeanDefinitionStoreException ex) {throw ex;}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +configClass.getMetadata().getClassName() + "]", ex);}finally {this.importStack.pop();}}}

(3)springBoot自動(dòng)裝載原理解析

SpringBoot開箱即用的特點(diǎn),很大程度上歸功于ImportSelector接口。接下來我們看下springBoot是如何在spring的基礎(chǔ)上做擴(kuò)展的。
在SpringBoot中最重要的一個(gè)注解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 {//... }

在SpringBootApplication注解中聲明了一個(gè) @EnableAutoConfiguration注解

@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接口中通過@Import注解引入了SpringBoot定義的AutoConfigurationImportSelector類,這個(gè)類內(nèi)容比較多,我們只需看下最主要的邏輯代碼即可

public class AutoConfigurationImportSelectorimplements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,BeanFactoryAware, EnvironmentAware, Ordered {@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);//主要邏輯在getAutoConfigurationEntry這個(gè)方法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);//通過getCandidateConfigurations方法獲取所有需要加載的beanList<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);//去重處理configurations = removeDuplicates(configurations);//獲取不需要加載的bean,這里我們可以通過spring.autoconfigure.exclude人為配置Set<String> exclusions = getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations = filter(configurations, autoConfigurationMetadata);//發(fā)送事件,通知所有的AutoConfigurationImportListener進(jìn)行監(jiān)聽fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);}//這里是獲取bean渠道的地方,重點(diǎn)看SpringFactoriesLoader#loadFactoryNamesprotected List<String> getCandidateConfigurations(AnnotationMetadata metadata,AnnotationAttributes attributes) {//這里的getSpringFactoriesLoaderFactoryClass()最終返回 EnableAutoConfiguration.classList<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());Assert.notEmpty(configurations,"No auto configuration classes found in METAINF/spring.factories. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;} }

從上面的邏輯可以看出,最終獲取bean的渠道在SpringFactoriesLoader.loadFactoryNames

public final class SpringFactoriesLoader {public static final String FACTORIES_RESOURCE_LOCATION = "METAINF/spring.factories";private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap();public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {String factoryClassName = factoryClass.getName();//通過factoryClassName獲取相應(yīng)的bean全稱//上面?zhèn)魅氲膄actoryClass是EnableAutoConfiguration.classreturn (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());}private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);if (result != null) {return result;} else {try {//獲取工程中所有META-INF/spring.factories文件,將其中的鍵值組合成MapEnumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");LinkedMultiValueMap result = new LinkedMultiValueMap();while(urls.hasMoreElements()) {URL url = (URL)urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);Iterator var6 = properties.entrySet().iterator();while(var6.hasNext()) {Entry<?, ?> entry = (Entry)var6.next();String factoryClassName = ((String)entry.getKey()).trim();String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());int var10 = var9.length;for(int var11 = 0; var11 < var10; ++var11) {String factoryName = var9[var11];result.add(factoryClassName, factoryName.trim());}}}cache.put(classLoader, result);return result;} catch (IOException var13) {throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);}}}private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {try {Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);if (!factoryClass.isAssignableFrom(instanceClass)) {throw new IllegalArgumentException("Class [" + instanceClassName + "] is not assignable to [" + factoryClass.getName() + "]");} else {return ReflectionUtils.accessibleConstructor(instanceClass, new Class[0]).newInstance();}} catch (Throwable var4) {throw new IllegalArgumentException("Unable to instantiate factory class: " + factoryClass.getName(), var4);}} }

每個(gè)jar都可以定義自己的META-INF/spring.factories ,jar被加載的同時(shí) spring.factories里面定義的bean就可以自動(dòng)被加載。

總結(jié)

以上是生活随笔為你收集整理的SpringBoot自动装载的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。