spingboot自动装配原理

q1871901600 发布于 2024-11-21 56 次阅读


自动配置主要是同过@SpringBootApplication这个类注解实现的,这个注解是一个合成注解,里面由多个注解组成。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

一、 @SpringBootConfiguration

使用@Configuration修饰这个注解。代表当前这个注解是一个配置类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
 
}

二、@ComponentScan

@ComponentScan(excludeFilters = { 
        @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
 
excludeFilters :排除指定的组件
FilterType.CUSTOM:按照自定义规则排除
TypeExcludeFilter.class:自定义规则组件

三、@EnableAutoConfiguration(开启自动配置)

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

四、@AutoConfigurationPackage

自动导包配置注解,里面是@Import(AutoConfigurationPackages.Registrar.class)

1、AutoConfigurationPackages.Registrar.class

	@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			register(registry, new PackageImport(metadata).getPackageName());
		}
 
metadata:注解元信息,也就是这个注解修饰的类的全路径类名
register:扫描注解所在的类的包下的所有组件,并注册进容器,也就是SpringAnnotateApplication所在的包。所以springboot,默认自动导入SpringAnnotateApplication所在的包下的所有组件。

metadata:注解元信息,也就是这个注解修饰的类的全路径类名
register:扫描注解所在的类的包下的所有组件,并注册进容器,也就是SpringAnnotateApplication所在的包。所以springboot,默认自动导入SpringAnnotateApplication所在的包下的所有组件。

五、@Import({AutoConfigurationImportSelector.class}

主要是AutoConfigurationImportSelector类中getAutoConfigurationEntry方法给容器进行批量导入组件。


1、进入getAutoConfigurationEntry方法,
找到List configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
这行代码,这里就把指定路径下文件中的配置类加载进入容器了。

2、进入getCandidateConfigurations,只有一行
List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());是主要代码,从这里进入可以看到具体加载逻辑
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 先从缓存中获取配置文件
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
        // 如果没有从指定classLoader,则默认加载系统下所有META-INF/spring.factories位置中的文件
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                MultiValueMap<String, String> 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()) {
                        Map.Entry<?, ?> entry = (Map.Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;
 
                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryImplementationName = var9[var11];
                            result.add(factoryTypeName, factoryImplementationName.trim());
                        }
                    }
                }
 
                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }

spring-boot-autoconfigure这个包里面也有META-INF/spring.factories文件,文件里面写死了spring-boot启动就要给容器中加载的所有配置类。

虽然springboot启动时,就默认加载META-INF/spring.factories下的所有配置文件,但是这些配置文件并不是所有都会启动,他们只会按条件装配,只有符合某些条件,才会开启。以下是几个springboot底层按条件开启配置的栗子:

1、springboot底层给容器中加入了文件上传解析器

  @Bean
        @ConditionalOnBean(MultipartResolver.class)  //容器中有这个类型组件
        @ConditionalOnMissingBean(name = DispatcherServlet.) //容器中没有这个名字 multipartResolver 的组件
        public MultipartResolver multipartResolver(MultipartResolver resolver) {
            //给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
            //SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范
            // 有些用户可能配置了MultipartResolver这个类型的组件,但是组件名字不规范
            return resolver;
        }

2、优先使用用户配置

@Bean
@ConditionalOnMissingBean // 如果容器中没有CharacterEncodingFilter 这个bean才开启,相当于优先使用用户的,用户没有会配置,将会开启这个兜底
public CharacterEncodingFilter characterEncodingFilter() {
    CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
    filter.setEncoding(this.properties.getCharset().name());
    filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.REQUEST));
    filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE));
    return filter;
}

参考文章:

springboot系列--自动配置原理_springboot自动配置原理-CSDN博客

一个会写python的Java工程师
最后更新于 2024-11-21