spring.factories配置文件的工厂模式
在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配置文件的工厂模式的更多相关文章
- Spring中如何使用工厂模式实现程序解耦?
目录 1. 啥是耦合.解耦? 2. jdbc程序进行解耦 3.传统dao.service.controller的程序耦合性 4.使用工厂模式实现解耦 5.工厂模式改进 6.结语 @ 1. 啥是耦合.解 ...
- spring管理配置文件的工厂类--PropertiesFactoryBean
使用这个工厂的配置,可以很方便的获取配置文件中的属性.具体使用如下; 对于属性配置,一般采用的是键值对的形式,如: key=value 属性配置文件一般使用的是XXX.properties,当然有时候 ...
- Spring 实现两种设计模式:工厂模式和单态模式(单例模式)
本文摘自:李刚 著 <轻量级 Java EE企业应用实战 Struts2+Spring+hibernate整合开发> 在Spring 中大量使用的以下两种设计模式:工厂模式和单态模式. 工 ...
- Spring中的工厂模式和单例模式
Spring预备知识(适合中小型项目) 作用:集成和管理其他框架 工厂模式: A a = new A( ); 将类所要创建的对象写入工厂,统一进行管理 package com.spring; pu ...
- 使用传统的三层架构出现的问题.引入Spring底层实现原理来解决(工厂模式+反射+XML配置文件/注解)
以前写的代码 mapper层 public interface PersonMapper { void selectPersonList(); } public class PersonMapperI ...
- 工厂模式模拟Spring的bean加载过程
一.前言 在日常的开发过程,经常使用或碰到的设计模式有代理.工厂.单例.反射模式等等.下面就对工厂模式模拟spring的bean加载过程进行解析,如果对工厂模式不熟悉的,具体可以先去学习一下工厂 ...
- 从基础知识到重写Spring的Bean工厂中学习java的工厂模式
1.静态工厂模式其他对象不能直接通过new得到某个类,而是通过调用getInstance()方法得到该类的对象这样,就可以控制类的产生过程.顺带提一下单例模式和多例模式: 单例模式是指控制其他对象获 ...
- Spring学习13-中IOC(工厂模式)和AOP(代理模式)的详细解释
我们是在使用Spring框架的过程中,其实就是为了使用IOC,依赖注入,和AOP,面向切面编程,这两个是Spring的灵魂. 主要用到的设计模式有工厂模式和代理模式. IOC是工厂模式参考:设计模式- ...
- 【Spring源码解析】—— 简单工厂模式的BeanFactory的超简版实现
一.什么是简单工厂模式 设计模式的核心是“分工”,通过分工将对象与职责划分的更细化,进而提升系统设计的可扩展性,使其更容易维护. 开闭原则:对扩展开放,对修改关闭:要增加一个新的处理逻辑,可以开一个新 ...
随机推荐
- 常用sql2
mysql中的NULL的判断 mysql> delete from `zhangchao` where x='NULL'; Query OK, 0 rows affected (0.00 ...
- Redis Sentinel 高可用服务架构搭建
https://www.cnblogs.com/xishuai/p/redis-sentinel.html
- java调用科大讯飞流式(websocket)语音识别接口
要使用讯飞的能力,需先注册讯飞开发平台账号(讯飞官网参见https://www.xfyun.cn/). 再创建应用,点击右上角的控制台 -> 创建新应用: 每个应用都有一个appId,由这个ap ...
- 视频软件——ffmpeg
1.官网下载地址:http://ffmpeg.org/download.html ffplay.exe ffplay是用于播放的应用程序. ffplay name.mp4 ffprobe.exe ff ...
- django模板--条件控制标签
条件控制标签 在django模板中可以通过条件控制标签进行逻辑控制,条件控制标签的语法如下: {% if condition1 %} ... {% elif condition2 %} ... {% ...
- 【Leetcode_easy】804. Unique Morse Code Words
problem 804. Unique Morse Code Words solution1: class Solution { public: int uniqueMorseRepresentati ...
- 个人总结2019 ASP.NET面试题
1.什么是面向对象? 面向对象就是把一个人或事务的属性,比如名字,年龄这些定义在一个实体类里面.存和取的时候直接使用存取实体类就把这个人的名字,年龄这些全部存了,这个实体类就叫对象,这种思想就叫面向对 ...
- 【计算机视觉】Selective Search for Object Recognition论文阅读2
Selective Search for Object Recognition 是J.R.R. Uijlings发表在2012 IJCV上的一篇文章.主要介绍了选择性搜索(Selective Sear ...
- 基于SymmetricDS的多主一从数据库同步方案
原文参照:https://blog.csdn.net/seattle0564/article/details/22096901 下面就记录下测试的一款第三方同步方案SymmetricDS(以下简称S) ...
- css3 float排序问题
css3 float排序问题 有时候发现 会错位 那是因为有个图片大小不一致才会这样 所以要确保每个图片一样