在springboot的各个依赖包下,我们经常看到META-INF/spring.factories这个文件。spring.factories文件的内容基本上都是这样的格式:

 # Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

我们看到,这个文件配置了一个key:value格式的数据

1)key是:org.springframework.context.ApplicationContextInitializer

2)value是:org.springframework.context.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,org.springframework.context.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

key声明的是一个接口,value则是这个接口对应的实现类,如果有多个则以","符号分割。

简单来说,spring.factories文件包含了一些接口相对应的实现类的配置,我们通过这些配置就可以知道接口有哪些可选的实现类,并通过反射获取对应的实例对象。就像是简单工厂模式一样,也因此spring将这个文件定义为spring.factories这个名字。

代码实例

下面以ApplicationContextInitializer接口为示例,我们看看springboot是怎么使用spring.factories的。

首先会用classLoader加载类路径下的所有spring.factories的配置内容,loadSpringFactories方法将返回一个key=接口名,value=实现类集合的Map结构

 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 先试着取缓存
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
} try {
// 获取所有spring.factories的URL
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
// 遍历URL
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 加载每个URL中的properties配置
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
// 遍历每个配置
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
// 将实现类的配置按照","符号分割开
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
// 逐个添加到接口对应的集合当中
result.add(factoryClassName, factoryName.trim());
}
}
}
// 加入缓存
cache.put(classLoader, result);
return result;
} catch (IOException ex) {
// ...
}
}

有了以上这个Map结构,就可以轻松拿到对应接口的实现类集合了,如:

 public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

这里的factoryClass是接口,通过getName()方法获取全限定名,然后根据该全限定名从Map结构中get出对应的实现类全限定名的集合。

到这里我们得到了一个实现类的集合,要获取实现类具体的实例对象只需要通过反射得到实例对象即可,如:

 private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
// 遍历实例对象的全限定名
for (String name : names) {
try {
// 加载该类
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
// 断言是否为该接口的实现类
Assert.isAssignable(type, instanceClass);
// 获取构造方法
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
// 实例化该类
T instance = (T) BeanUtils.instantiateClass(constructor, args);
// 添加到结果集当中
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}

总结

spring.factories就像是工厂一样配置了大量的接口对应的实现类,我们通过这些配置 + 反射处理就可以拿到相应的实现类。这种类似于插件式的设计方式,只要引入对应的jar包,那么对应的spring.factories就会被扫描到,对应的实现类也就会被实例化,如果不需要的时候,直接把jar包移除即可。

spring.factories配置文件的工厂模式的更多相关文章

  1. Spring中如何使用工厂模式实现程序解耦?

    目录 1. 啥是耦合.解耦? 2. jdbc程序进行解耦 3.传统dao.service.controller的程序耦合性 4.使用工厂模式实现解耦 5.工厂模式改进 6.结语 @ 1. 啥是耦合.解 ...

  2. spring管理配置文件的工厂类--PropertiesFactoryBean

    使用这个工厂的配置,可以很方便的获取配置文件中的属性.具体使用如下; 对于属性配置,一般采用的是键值对的形式,如: key=value 属性配置文件一般使用的是XXX.properties,当然有时候 ...

  3. Spring 实现两种设计模式:工厂模式和单态模式(单例模式)

    本文摘自:李刚 著 <轻量级 Java EE企业应用实战 Struts2+Spring+hibernate整合开发> 在Spring 中大量使用的以下两种设计模式:工厂模式和单态模式. 工 ...

  4. Spring中的工厂模式和单例模式

    Spring预备知识(适合中小型项目) 作用:集成和管理其他框架 工厂模式: A  a  = new A( ); 将类所要创建的对象写入工厂,统一进行管理 package com.spring; pu ...

  5. 使用传统的三层架构出现的问题.引入Spring底层实现原理来解决(工厂模式+反射+XML配置文件/注解)

    以前写的代码 mapper层 public interface PersonMapper { void selectPersonList(); } public class PersonMapperI ...

  6. 工厂模式模拟Spring的bean加载过程

    一.前言    在日常的开发过程,经常使用或碰到的设计模式有代理.工厂.单例.反射模式等等.下面就对工厂模式模拟spring的bean加载过程进行解析,如果对工厂模式不熟悉的,具体可以先去学习一下工厂 ...

  7. 从基础知识到重写Spring的Bean工厂中学习java的工厂模式

    1.静态工厂模式其他对象不能直接通过new得到某个类,而是通过调用getInstance()方法得到该类的对象这样,就可以控制类的产生过程.顺带提一下单例模式和多例模式:  单例模式是指控制其他对象获 ...

  8. Spring学习13-中IOC(工厂模式)和AOP(代理模式)的详细解释

    我们是在使用Spring框架的过程中,其实就是为了使用IOC,依赖注入,和AOP,面向切面编程,这两个是Spring的灵魂. 主要用到的设计模式有工厂模式和代理模式. IOC是工厂模式参考:设计模式- ...

  9. 【Spring源码解析】—— 简单工厂模式的BeanFactory的超简版实现

    一.什么是简单工厂模式 设计模式的核心是“分工”,通过分工将对象与职责划分的更细化,进而提升系统设计的可扩展性,使其更容易维护. 开闭原则:对扩展开放,对修改关闭:要增加一个新的处理逻辑,可以开一个新 ...

随机推荐

  1. sql中union,union all没有兼顾到的内容

    今日遇到一个问题,两张表联合取交集去重,但是需要把某一字段相同的也给去掉 union all : 联合,没有取交集 union :联合取交集(仅针对所有字段相同的去重) 解决方案:将联合的数据作为一个 ...

  2. Android:Mstar平台 HDMI OUT 静音流程

    一.framework层 1. APP调用 AudioManager 的 adjustStreamVolume() 接口实现在: frameworks\base\services\core\java\ ...

  3. Django之model详解

    Django中的页面管理后台 Djano中自带admin后台管理模块,可以通过web页面去管理,有点想php-admin,使用步骤: 在项目中models.py 中创建数据库表 class useri ...

  4. python使用退格键时出现^H解决方法

    Linux 使用退格键时出现^H解决方法 1.临时解决 按ctrl 2.永久解决 基本现象 进入 Python shell,按下 Delete/Backspace 键: Python 3.5.2 (d ...

  5. 【leetcode_easy】590. N-ary Tree Postorder Traversal

    problem 590. N-ary Tree Postorder Traversal 参考 1. Leetcode_easy_590. N-ary Tree Postorder Traversal; ...

  6. RobotFramework:发现一个大坑,当post接口入参为json时,千万不能用sojson转化后的json串(ride解析会有异常,非sojson工具问题),直接用浏览器粘过来的就行

    问题背景: 和以往一样愉快的进行着自动化测试,突然就不停的提示我,“程序异常”,查看log发现data中的json变为了数组?????? 那算了,我不先组装入参数据直接data=json入参吧,wha ...

  7. 统一建模语言UML

    目录 1. UML定义 2. UML结构 2.1 视图(View) 2.2 图(Diagram) 2.3 模型元素(Model element) 2.4 通用机制(General mechanism) ...

  8. mybatis传入map任意表增删改查,分页过滤字段

    <!--根据实体参数查询 --> <select id="selectBaseList" resultType="java.util.HashMap&q ...

  9. JPEG2000开发SDK及其特点

    JPEG2000开发SDK及其特点 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 说明:JPEG2000被开发来取代JPEG,但因为大量核心算法被专利注册, ...

  10. Flarum 安装问题 编译安装 fileinfo.so

    大部分人上传 会遇到文件没有权限这一问题 ,这个好解决 下边是服务器 php.ini 扩展 fileinfo 由于php 版本是7.1.8 最新的 又是用的一键环境安装 所以 没有安装这个扩展 1.去 ...