千家信息网

如何理解springboot加载META-INF/spring.factories方式

发表于:2024-11-29 作者:千家信息网编辑
千家信息网最后更新 2024年11月29日,本篇内容主要讲解"如何理解springboot加载META-INF/spring.factories方式",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"如何
千家信息网最后更新 2024年11月29日如何理解springboot加载META-INF/spring.factories方式

本篇内容主要讲解"如何理解springboot加载META-INF/spring.factories方式",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"如何理解springboot加载META-INF/spring.factories方式"吧!

目录
  • springboot 加载 META-INF/spring.factories

    • 用户应用程序Application

  • 建立META-INF/spring.factories文件的意义何在

    • 平常我们如何将Bean注入到容器当中

springboot 加载 META-INF/spring.factories

用户应用程序Application

ConfigurableApplicationContext context = SpringApplication.run(NacosSpringBootYamlApplication.class, args);

SpringApplication类

public static ConfigurableApplicationContext run(Class primarySource, String... args) {  return run(new Class[] { primarySource }, args); }
// 这里Class是数组public static ConfigurableApplicationContext run(Class[] primarySources, String[] args) {  return new SpringApplication(primarySources).run(args); }
public SpringApplication(Class... primarySources) {  this(null, primarySources); }
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {  this.resourceLoader = resourceLoader;  Assert.notNull(primarySources, "PrimarySources must not be null");  this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 这里就是SpringMvcApplication的实例  this.webApplicationType = WebApplicationType.deduceFromClasspath();// deduce(推断)web类型(servlet、reactive、NoWeb)  setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));// 这里会处理加载所有的spring.factories文件的内容到缓存 找到*META-INF/spring.factories*中声明的所有ApplicationContextInitializer的实现类并将其实例化  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //找到*META-INF/spring.factories*中声明的所有ApplicationListener的实现类并将其实例化  this.mainApplicationClass = deduceMainApplicationClass(); //获得当前执行main方法的类对象,这里就是SpringMvcApplication的实例 }

具体加载该classLoader下的所有spring.factories到缓存

如果缓存已经存在,则直接根据key,返回数据

/** key:是spring.factories的key    value:是根据key分组,把同个key的不同value放到list里面 */ private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {  MultiValueMap result = cache.get(classLoader);  if (result != null) { //已经处理过了  直接返回   return result;  }//url: // file:/C:/Users/kongqi/.m2/repository/org/springframework/spring-beans/5.1.9.RELEASE/spring-beans-5.1.9.RELEASE.jar!/META-INF/spring.factories  try { //得到classloader下的所有jar包中的spring.factories的文件   Enumeration urls = (classLoader != null ?     classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :     ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));   result = new LinkedMultiValueMap<>();   while (urls.hasMoreElements()) {    URL url = urls.nextElement();    UrlResource resource = new UrlResource(url);    Properties properties = PropertiesLoaderUtils.loadProperties(resource); // 得到spring.factories的内容    for (Map.Entry entry : properties.entrySet()) { // key: spring.factories的key  value: spring.factories的value     String factoryClassName = ((String) entry.getKey()).trim(); // spring.factories的key     for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {//value根据逗号,分隔      result.add(factoryClassName, factoryName.trim()); //factoryClassName其实就是spring.factories的key   由于value是List类型 MultiValueMap value有多个     }    }   }   cache.put(classLoader, result);   return result;  }  catch (IOException ex) {   throw new IllegalArgumentException("Unable to load factories from location [" +     FACTORIES_RESOURCE_LOCATION + "]", ex);  } }

流程图

建立META-INF/spring.factories文件的意义何在

平常我们如何将Bean注入到容器当中

@Configuration@EnableConfigurationProperties(HelloProperties.class)public class HelloServiceAutoConfiguration {    @Autowired    HelloProperties helloProperties;    @Bean    public HelloService helloService() {        HelloService service = new HelloService();        service.setHelloProperties( helloProperties  );        return service;    }}

一般就建立配置文件使用@Configuration,里面通过@Bean进行加载bean

或者使用@Compont注解在类上进行类的注入

注意:

在我们主程序入口的时候:

@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 {

里面注解@EnableAutoConfiguration

@ComponentScan注解指扫描@SpringBootApplication注解的入口程序类所在的basepackage下的

所有带有@Component注解的bean,从而注入到容器当中。

但是

如果是加入maven坐标依赖的jar包,就是项目根目录以外的Bean是怎么添加的??

这个时候注解@EnableAutoConfiguration的作用就来了

导入了AutoConfigurationImportSelector这个类:

这个类里面有一个方法

/**     * Return the auto-configuration class names that should be considered. By default     * this method will load candidates using {@link SpringFactoriesLoader} with     * {@link #getSpringFactoriesLoaderFactoryClass()}.     * @param metadata the source metadata     * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation     * attributes}     * @return a list of candidate configurations     */    protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {        List 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;    }

@EnableAutoConfiguration注解来注册项目包外的bean。而spring.factories文件,则是用来记录项目包外需要注册的bean类名

为什么需要spring.factories文件,

因为我们整个项目里面的入口文件只会扫描整个项目里面下的@Compont @Configuration等注解

但是如果我们是引用了其他jar包,而其他jar包只有@Bean或者@Compont等注解,是不会扫描到的。

除非你引入的jar包没有Bean加载到容器当中

所以我们是通过写/META-INF/spring.factories文件去进行加载的。

到此,相信大家对"如何理解springboot加载META-INF/spring.factories方式"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

0