spring.factories配置文件的工厂模式

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

1 # Initializers
2 org.springframework.context.ApplicationContextInitializer=3 org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,4 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结构

 1 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
 2     // 先试着取缓存
 3     MultiValueMap<String, String> result = cache.get(classLoader);
 4     if (result != null) {
 5         return result;
 6     }
 7
 8     try {
 9         // 获取所有spring.factories的URL
10         Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
11         result = new LinkedMultiValueMap<>();
12         // 遍历URL
13         while (urls.hasMoreElements()) {
14             URL url = urls.nextElement();
15             UrlResource resource = new UrlResource(url);
16             // 加载每个URL中的properties配置
17             Properties properties = PropertiesLoaderUtils.loadProperties(resource);
18             // 遍历每个配置
19             for (Map.Entry<?, ?> entry : properties.entrySet()) {
20                 String factoryClassName = ((String) entry.getKey()).trim();
21                 // 将实现类的配置按照","符号分割开
22                 for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
23                     // 逐个添加到接口对应的集合当中
24                     result.add(factoryClassName, factoryName.trim());
25                 }
26             }
27         }
28         // 加入缓存
29         cache.put(classLoader, result);
30         return result;
31     } catch (IOException ex) {
32         // ...
33     }
34 }

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

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

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

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

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

总结

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

原文地址:https://www.cnblogs.com/lay2017/p/11415661.html

时间: 2024-11-08 23:22:39

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

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

目录 1. 啥是耦合.解耦? 2. jdbc程序进行解耦 3.传统dao.service.controller的程序耦合性 4.使用工厂模式实现解耦 5.工厂模式改进 6.结语 @ 1. 啥是耦合.解耦? 既然是程序解耦,那我们必须要先知道啥是耦合,耦合简单来说就是程序的依赖关系,而依赖关系则主要包括 1. 类之间的依赖 2. 方法间的依赖 比如下面这段代码: public class A{ public int i; } public class B{ public void put(A a)

Spring IOC下的工厂模式

在Spring IOC容器中,我们需要创建工厂,用来管理我们需要创建的对象,因此这时就需要我们了解什么是工厂,这篇简单的小短文,可能会帮你入门,如果稍微想要仔细了解一下,可以查看这个微博https://blog.csdn.net/jason0539/article/details/23020989 在面向对象编程中,一般来说,我们需要哪个类的对象,就会自己取new 一个类的对象实例.这是根据我们需要什么,然后自己去创建什么. 在一些情况下,我们并不想直接new 一个对象,例如,我们创建一些对象前

Spring 实现两种设计模式:工厂模式和单态模式

在Spring 中大量使用的以下两种设计模式:工厂模式和单态模式. 工厂模式可将Java 对象的调用者从被调用者的实现逻辑中分离出来,调用者只需关心被调用者必须满足的规则(接口) ,而不必关心实例的具体实现过程.这是面向接口编程的优势,能提高程序的解耦,避免所有的类以硬编码方式耦合在一起. 如果所有的类直接耦合,极易形成"骨牌效应",假如B 类调用了A 类,一旦A 类需要修改,则B 类也需要修改:假如C 类调用了B 类,则C 类也需要修改......依次类推,从而导致整个系统都需要改写

工厂模式与反射

工厂模式与反射 定义一个接口以及两个实现了该接口的类: package Reflect; interface fruit{ public void eat(); } class Apple implements fruit{ public void eat(){ System.out.println("Apple Eat"); } } class Orange implements fruit{ public void eat(){ System.out.println("O

Factory(简单的工厂模式)

一句话总结:用一个方法来代替new关键字解决什么问题:对象产生过多,或者经常有子类替换生成.项目里面怎么用:对于经常生成的对象,或者父子类替换的对象.模式结构:写一个对外声明的方法,方法里面使用new关键字代替.框架里面使用:spring的核心就是工厂模式. 工厂方法模式:有四个角色,抽象工厂模式,具体工厂模式,抽象产品模式,具体产品模式.不再是由一个工厂类去实例化具体的产品,而是由抽象工厂的子类去实例化产品 // 抽象产品角色 public interface Moveable { void

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

1.静态工厂模式其他对象不能直接通过new得到某个类,而是通过调用getInstance()方法得到该类的对象这样,就可以控制类的产生过程.顺带提一下单例模式和多例模式:  单例模式是指控制其他对象获得该对象永远只有同一个对象  而多例模式则是根据需要从某个具体集合中获取所需的对象 1 import java.util.ArrayList; 2 import java.util.List; 3 4 5 public class Car implements Moveable{ 6 private

Spring工厂模式

普通工厂模式和抽象共产模式的优缺点:(1)普通工厂模式能够在单一产品维度上进行扩展,但是增加一个产品就要增加一个相应的工厂类,这样就会造成工厂的泛滥:(2)抽象工厂模式:很好的实现了一批产品的更新,但是,另一方面在单一产品维度上不好扩展. 而Spring的工厂模式原理是将Bean的信息放在配置文件当中.我们要模拟的话,可以用Java当中提供的一个特殊的类java.util.Properties,从而将类名信息在bean.properties文件中进行配置.将配置信息读取出来后,再利用反射技术进行

asp.net mvc中自行搭建实现工厂模式的spring.net+三层架构 (15-4-20)

在之前的文章中,我们已经搭好了三层框架,通过调用业务层的子接口得到实例化的业务子类,通过业务子类来调用数据访问层子接口,由子接口来实例化数据访问层子类.这里我们需要明确把框架搭这么复杂的原因是遵循了设计模式中的开闭原则:对扩展开放,对修改关闭. 搭好了框架之后,我们发现这框架并不完善:实例化对象还需要手动实现,并不能实现将对象的实例化托管.所以我们决定引入spring.net框架,它的IOC模块(依赖注入),将帮我们来完成对象的创建,管理. 1.首先我们需要在项目中引用spring.core.d

asp.net mvc中自行搭建实现工厂模式的spring.net+三层架构(更新)

首先附上本人托管在Github上的项目https://github.com/lichaojacobs/Tomato 接下来的三层结构都包含了父接口,子接口,父类,子类部分 1.编写数据访问层 数据访问层是对数据库的增删改查的操作.首先我们在数据访问层的父接口声明一些通用的对数据库统一处理的方法: 然后是业务层子接口(对应于你的数据实体模型中的Model,下面是我的项目中的Model,仅供参考),让它继承自业务层父接口 写完接口,接下来编写父接口的实现父类BaseDAL.cs 注意,在这里是通过一