
背景
这两个包org.springframework.beans和org.springframework.context是Spring IoC容器的基础,
重要的类之一是BeanFactory,它是IoC容器的核心接口. 它的职责包括: 实例化,定位和配置应用程序
对象并在这些对象之间建立依赖关系.
ApplicationContext作为BeanFactory的子类,在Bean管理功能上得到了极大的增强,并且更易于与Spring AOP集成.
我们今天要讨论的不是BeanFactory或ApplicationContext的实现原理,而是ApplicationContext的实际应用.
提问
在实际工作中,我们经常遇到接口和多个实现类的情况,并且在不同条件下将使用不同的实现类. 从使用的角度来看,有些用法类似于SPI,

但是,由于使用SPI并不是很方便,我该怎么办?我们可以使用ApplicationContext的getBeansOfType来获得所需的结果.
首先让我们看一下该方法的签名
<T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException;
从上面的代码中我们可以看到,此方法可以返回接口的所有实现类(前提是所有实现类都必须由Spring IoC容器管理).
让我们看看我们遇到的问题.
“假设从A点到B点有多种运输方式. 每种运输方式的成本是不同的spring常用的接口和类,可以根据乘客的需求进行选择”(嗯,我承认这很la脚需求,
但是您可以想到类似的要求,例如付款方式和快递公司. 页面上提供了几个选项,并且业务代码根据不同的选项选择要调用的不同的实现类实例.
实施

回到我们的示例,根据这种运输方式的需求,我们的设计如下: 有一个运输方式的接口,该接口有两种模式,一种查询成本,一种查询运输方式. 在运输方式上,我们同时使用枚举类型类来识别交通类型.
我们还需要一个工厂类来根据流量类型标识符找到流量类型的Bean实例,以便使用该实例获取流量类型的详细信息以及该流量类型的操作.

代码如下:
接口:
/**
* 交通方式
*/
public interface TrafficMode {
/**
* 查询交通方式编码
* @return 编码
*/
TrafficCode getCode();
/**
* 查询交通方式的费用,单位:分
* @return 费用
*/
Integer getFee();
}
枚举:
/**
* 交通类型枚举
*/
public enum TrafficCode {
TRAIN,
BUS
}

该接口有两个实现类:
/**
* 汽车方式
*/
@Component
public class BusMode implements TrafficMode {
@Override
public TrafficCode getCode() {
return TrafficCode.BUS;
}
@Override
public Integer getFee() {
return 10000;
}
}
/**
* 火车方式
*/
@Component
public class TrainMode implements TrafficMode {
@Override
public TrafficCode getCode() {
return TrafficCode.TRAIN;
}
@Override
public Integer getFee() {
return 9000;
}
}
工厂类:
/**
* 交通方式工厂类
*/
@Component
public class TrafficModeFactory implements ApplicationContextAware {
private static Map<TrafficCode, TrafficMode> trafficBeanMap;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, TrafficMode> map = applicationContext.getBeansOfType(TrafficMode.class);
trafficBeanMap = new HashMap<>();
map.forEach((key, value) -> trafficBeanMap.put(value.getCode(), value));
}
public static <T extends TrafficMode> T getTrafficMode(TrafficCode code) {
return (T)trafficBeanMap.get(code);
}
}
验证
有了上面的代码之后,我们来看一下通过单元测试的效果. 单元测试代码段如下:
@Test
public void testGetTrafficMode() {
TrafficMode mode = TrafficModeFactory.getTrafficMode(TrafficCode.BUS);
Assert.assertEquals(mode.getFee().intValue(), 10000);
mode = TrafficModeFactory.getTrafficMode(TrafficCode.TRAIN);
Assert.assertEquals(mode.getFee().intValue(), 9000);
}

运行后结果如何?它必须通过.
关于SPI
本文在这里,一些学生可能会问: 这与SPI有什么区别? SPI也可以实现相同的功能. 首先,让我解释一下SPI是JDK的内置函数. 尽管历史比较悠久,但这并不意味着它不好,并且某些方案不是SPI(例如JDBC).
我们阐明了SPI是什么以及它的设计用途. SPI的全名是服务提供商接口(Service Provider Interface),因为它是针对制造商或插件的. 更为经典的用法是JDBC. Java提供了一个标准的JDBC接口spring常用的接口和类,每个供应商都提供了自己的驱动程序实现. 以下是JDBC驱动程序接口:
public interface Driver {
Connection connect(String var1, Properties var2) throws SQLException;
boolean acceptsURL(String var1) throws SQLException;
DriverPropertyInfo[] getPropertyInfo(String var1, Properties var2) throws SQLException;
int getMajorVersion();
int getMinorVersion();
boolean jdbcCompliant();
Logger getParentLogger() throws SQLFeatureNotSupportedException;
}
以下是由MySQL的JDBC驱动程序实现的SPI配置:

我们已经了解了SPI. 在这里,我们只想证明前面的示例中使用的SPI和AP具有不同的适用场景.
在前面的示例中,我们使用了什么? Spring的API,API的全称是Application Programming Interface(应用程序编程接口),在应用程序内部,使用API是一种非常方便且最直接的方法.
总而言之,就是在编程中,我们使用API最多. 当然,我们也将使用SPI(尽管我们并不是严格意义上的制造商或插件),使用SPI也是解决特定情况下问题的一种方式.
有关SPI的更多信息,将来还会有专门的文章.
【附加】关于SPI的协议: 当服务提供者提供服务接口的实现时,还将在jar包的META-INF / services /目录中创建一个以服务接口命名的文件. 该文件是实现服务接口的具体实现类. 当外部程序组装此模块时,您可以通过META-INF / services / jar包中的配置文件找到特定的实现类名称,然后加载并实例化它以完成模块注入. 基于这种约定,可以很好地找到服务接口的实现类,而无需在代码中用公式表示. jdk提供了一个用于服务实现搜索的工具类: java.util.ServiceLoader,通过其load方法,您可以通过传入接口来获取其实现类.
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-213414-1.html
或者就去找他的老婆睡觉去
加表情什么鬼