文章目录
- 如果没时间看的话,在这里直接看总结
- 一、Java SPI的概念和术语
- 二、看看Java SPI是如何诞生的
- 三、Java SPI应该如何应用
- 四、从0开始,手撸一个SPI的应用实例
- 五、SpringBoot自动装配
- 六、Spring SPI机制与Spring Factories机制做对比
- 七、这里是给我自己提个醒
如果没时间看的话,在这里直接看总结
1. SPI是一个机制,流程由三个组件构成
- ServiceLoader,就是ClassLoader;
- Service,是接口,作为文件(在META-INF/services目录下)的名称
- ServiceProvider,是接口的实现类,作为文件(在META-INF/services目录下)的内容
2. SPI执行流程
- ServiceLoader通过classpath路径,加载指定的Service文件,然后使用里面合适的内容ServiceProvider
一、Java SPI的概念和术语
SPI(Service Provider Interface):基于ClassLoader,发现并加载服务,机制
SPI由三个组件构成:Service、Service Provider、ServiceLoader
- Service:是一个公开的接口或抽象类,定义了一个抽象的功能模块(文件名称)
- Service Provider:是Service的实现类(文件内容)
- ServiceLoader:是SPI机制中的核心组件,负责在运行时发现并加载Service Provider
二、看看Java SPI是如何诞生的
-
在Java SPI出现之前,Class.forName()要自己根据需求写驱动类
-
JDBC要求Driver实现类在类加载的时候,能将自身的实例对象自动注册到DriverManager中,从而加载数据库驱动。
-
Java SPI逐渐融入JDBC
三、Java SPI应该如何应用
- 规范的配置文件
- Service Provider类必须具备无参的默认构造方法
在JDBC中的对应实现
- 保证能加载到配置文件和Service Provider类
在JDBC中的对应实现
总结:上述除了导包需要自己动手以外,其他的手续都是导包之后,Java SPI自动完成的
四、从0开始,手撸一个SPI的应用实例
总体流程
五、SpringBoot自动装配
参考视频:每一帧都是干货!15分钟的视频花2小时看
参考文章:springboot自动装配到底是什么意思?
参考文章:建立META-INF/spring.factories文件的意义何在
参考文章:springboot自动装配原理-以redis为例
参考文章:聊聊 SpringBoot 自动装配原理
参考文章:spring.factories 文件的位置
1. 手动装配Redis实例
- 加入pom依赖
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
- 配置xml的bean的配置
//配置连接池
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="minIdle" value="10"></property>
<property name="maxTotal" value="20"></property>
</bean>
//配置连接工厂
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="47.104.128.12"></property>
<property name="password" value="123456"></property>
<property name="database" value="0"></property>
<property name="poolConfig" ref="poolConfig"></property>
</bean>
//配置 redisTemplate 模版类
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"/>
<!--如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!! -->
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
</property>
</bean>
- 导入配置
@ImportResource(locations = “classpath:beans.xml”) 可以导入xml的配置文件
2. SpringBoot自动配置Redis实例
- 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 配置Redis服务器
spring:
redis:
database:0
host:127.0.0.1
port:6379
password:123456
- 直接使用RedisTemplate或StringRedisTemplate
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
- 提出问题:自动配置
- 我们除了通过maven引入一个starter外,其他什么也没有做,但是呢,SpringBoot就自动完成了Redis的配置,将相关的Bean对象注册到IOC容器中了。那么SpringBoot是如何做到这一点的呢?这就是这篇博客所要说明的问题了。
2. 自动配置,一切从注解@SpringBootApplicaiton说起
- @SpringBootApplication注解
- 下面我们逐步分析@EnableAutoConfiguration的自动配置
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
AutoConfigurationImportSelector.class的selectImports方法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata){
if(!isEnabled(annotationMetadata))
return NO_IMPORTS;
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
//SpringBoot自动配置的入口方法
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationErtadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
- selectImports()方法中引用的getAutoConfigurationEntry()
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata){
//1. 获取annotationMetadata的注解@EnableAutoConfiguration的属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//2. 从资源文件Spring.factories中获取EnableAutoConfiguration对应的所有的类
List<String> configurations = getCandidateConfigurations(
annotationMetadata, attributes);
//3. 通过在注解@EnableAutoConfiguration设置exclude的相关属性,可以排除指定的自动配置类
Set<String> exclusions = getExclusions(anntationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//4. 根据注解@Conditional来判断是否需要排除某些自动配置类
configurations filter = filter(configurations, autoConfigurationMetadata);
//5. 触发AutoConfiguration导入的相关事件
fireAutoCOnfigurationImportEvents(configurations, exclusions);
return new AutofigurationEntry(configurations, exclusions);
}
- getAutoConfigurationEntry()引用的getCandidateConfigurations()
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes){
//通过SpringFactories机制,从配置文件Spring.factories中找出所有的自动配置类
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
EnableAutoConfiguration.class, getBeanClassLoader());
Assert.notEmpty(configurations,"No auto configuration classes found");
return configurations;
}
SpringFactoriesLoader.loadFactoryNames方法调用loadSpringFactories方法从所有的jar包中读取META-INF/spring.factories文件信息。
// 参数:
// Class<?> factoryType:需要被加载的工厂类的class
// ClassLoader classLoader:类加载器
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
// 若没传入类加载器,使用该本类的类加载器
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
// class.getName():获取该类的全类限定名字
String factoryTypeName = factoryType.getName();
// loadSpringFactories(classLoaderToUse) 返回是Map
// Map.getOrDefault(A,B): A为Key,从Map中获取Value,若Value为Null,则返回B 当作返回值
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
loadSpringFactories()方法调用ClassLoader.getSystemResources()获取META-INF/spring.factories文件
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap result = (MultiValueMap)cache.get(classLoader);
if(result != null) {
return result;
} else {
try {
Enumeration ex = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result1 = new LinkedMultiValueMap();
while(ex.hasMoreElements()) {
URL url = (URL)ex.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry entry = (Entry)var6.next();
List factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
result1.addAll((String)entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result1);
return result1;
} catch (IOException var9) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
}
}
}
下面是spring-boot-autoconfigure这个jar中spring.factories文件部分内容,选择带有EnableAutoConfiguration自动配置类。
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
六、Spring SPI机制与Spring Factories机制做对比
- 联系:Spring Factories自动装配借用了SPI机制,SPI机制本身就是一种思想,不是特定的技术。
- 区别:如下
七、这里是给我自己提个醒
META-IF/spring.factories是在Maven引入的Jar包中,每一个Jar都有自己META-IF/spring.factories,所以SpringBoot是去每一个Jar包里面寻找META-IF/spring.factories,而不是我的项目中存在META-IF/spring.factories(当然也可以存在,但是我项目的META-IF/spring.factories肯定没有类似以下这些东西)