springboot 加载 META-INF/spring.factories方式

 更新时间:2021年10月08日 10:06:17   作者:kong-kong  
这篇文章主要介绍了springboot 加载 META-INF/spring.factories方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

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<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
  MultiValueMap<String, String> 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<URL> 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<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;
    }

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

为什么需要spring.factories文件,

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

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

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

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

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:

相关文章

  • springboot注解及GET、POST接口写法

    springboot注解及GET、POST接口写法

    springboot提供了@Contrller和@RestController注解,@Controller返回页面和数据而@RestController返回数据,本文重点介绍springboot注解及GET、POST接口写法,感兴趣的朋友一起看看吧
    2024-04-04
  • SpringMVC空指针异常NullPointerException解决及原理解析

    SpringMVC空指针异常NullPointerException解决及原理解析

    这篇文章主要介绍了SpringMVC空指针异常NullPointerException解决及原理解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • Java自定义类数组报null的相关问题及解决

    Java自定义类数组报null的相关问题及解决

    这篇文章主要介绍了Java自定义类数组报null的相关问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • IDEA连接MySQL数据库的4种方法图文教程

    IDEA连接MySQL数据库的4种方法图文教程

    IDEA是一种流行的Java开发工具,可以方便地连接MySQL,这篇文章主要给大家介绍了关于IDEA连接MySQL数据库的4种方法,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-12-12
  • maven中resource配置使用详解

    maven中resource配置使用详解

    这篇文章主要介绍了maven中resource配置使用,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-07-07
  • SpringBoot配置连接两个或多个数据库的常用方法

    SpringBoot配置连接两个或多个数据库的常用方法

    在Spring Boot应用中连接多个数据库或数据源可以使用多种方式,本文讲给大家介绍两种常用的方法:使用Spring Boot官方支持的多数据源配置和使用第三方库实现多数据源,文章通过代码介绍的非常详细,需要的朋友可以参考下
    2023-08-08
  • MyBatis-Plus拦截器实现数据权限控制的方法

    MyBatis-Plus拦截器实现数据权限控制的方法

    MyBatis-Plus是一款基于MyBatis的增强工具,它提供了一些便捷的功能和增强的查询能力,数据权限控制是在系统中对用户访问数据进行限制的一种机制,这篇文章主要给大家介绍了关于MyBatis-Plus拦截器实现数据权限控制的相关资料,需要的朋友可以参考下
    2024-01-01
  • Java世界中最快的JDBC连接池HikariCP详解

    Java世界中最快的JDBC连接池HikariCP详解

    在 Java 生态系统中,JDBC 连接池的选择直接影响着系统的性能和稳定性,HikariCP 是目前公认的性能最强、代码最简洁的 JDBC 连接池实现,其设计哲学以“极简主义”为核心,本文将详细给大家介绍了Java世界中最快的JDBC连接池HikariCP,需要的朋友可以参考下
    2025-06-06
  • java解析{{}}变量名以及文本内容替换操作

    java解析{{}}变量名以及文本内容替换操作

    这篇文章主要介绍了java解析{{}}变量名以及文本内容替换操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • java图形化界面实现登录窗口

    java图形化界面实现登录窗口

    这篇文章主要为大家详细介绍了java图形化界面实现登录窗口,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-05-05

最新评论