死磕Spring,什么是SPI机制,对SpringBoot自动装配有什么帮助

如果没时间看的话,在这里直接看总结

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是如何诞生的

  1. 在Java SPI出现之前,Class.forName()要自己根据需求写驱动类
    在这里插入图片描述

  2. JDBC要求Driver实现类在类加载的时候,能将自身的实例对象自动注册到DriverManager中,从而加载数据库驱动。
    在这里插入图片描述

  3. Java SPI逐渐融入JDBC
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

三、Java SPI应该如何应用

  1. 规范的配置文件
    在这里插入图片描述
    在这里插入图片描述
  2. Service Provider类必须具备无参的默认构造方法
    在这里插入图片描述
    在JDBC中的对应实现
    在这里插入图片描述
  3. 保证能加载到配置文件和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肯定没有类似以下这些东西)
在这里插入图片描述

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: SpringCloud可以通过多种方式来保证系统的可用性,其中包括: 1. 服务注册与发现:通过Eureka、Consul等注册中心,实现服务的自动注册与发现,保证服务的高可用性。 2. 负载均衡:通过Ribbon、Feign等负载均衡组件,实现请求的分发,避免单点故障。 3. 熔断器:通过Hystrix等熔断器组件,实现服务的自动降级,避免服务雪崩。 4. 配置中心:通过Config等配置中心,实现配置的集中管理,避免配置错误导致系统不可用。 5. 服务网关:通过Zuul、Spring Cloud Gateway等服务网关,实现请求的统一入口,提高系统的安全性和可用性。 ### 回答2: Spring Cloud 是一个开源的微服务框架,它可以帮助开发者构建具有高可用性的系统。Spring Cloud 中通过以下几种方式来保证系统的可用性: 1. 服务注册与发现:通过使用服务注册与发现组件,如Eureka、Consul等,可以让服务能够自动注册到注册中心,并能够发现其他服务的信息。服务发现的机制可以保证当某个服务发生故障或下线时,其他服务能够找到可用的服务实例,从而保证系统的可用性。 2. 负载均衡:Spring Cloud提供了负载均衡的解决方案,如Ribbon、LoadBalancer等。通过使用负载均衡器,可以将请求分发到不同的服务实例上,从而实现服务的高可用性和提升系统的性能。 3. 服务容错:通过使用Hystrix等容错组件,可以在服务之间进行隔离和容错处理。当某个服务出现故障或超时时,Hystrix可以实现快速失败和降级处理,从而保证系统的可用性。 4. 服务监控与运维:Spring Cloud提供了丰富的监控和运维工具,如Spring Boot Admin、Sleuth等。通过这些工具,可以实时监控和追踪服务的运行情况,及时发现并解决问题,从而提高系统的可用性。 5. 容器化和自动化部署:Spring Cloud可以与容器化技术如Docker等集成,通过容器化部署可以快速创建和部署服务。同时,结合自动化部署工具如Jenkins等,可以实现系统的快速更新和部署,减少系统宕机的风险,保证系统的可用性。 总之,Spring Cloud 提供了一系列的组件和工具,通过服务注册与发现、负载均衡、服务容错、监控运维以及容器化和自动化部署等方式,来保证系统的可用性和稳定性。 ### 回答3: Spring Cloud是一个用于构建分布式系统的框架,它提供了一系列的组件和工具来帮助开发人员构建具有高可用性的系统。 Spring Cloud通过以下几个方面来保证系统的可用性: 1. 服务注册与发现:Spring Cloud提供了服务注册与发现的功能,开发人员可以将微服务注册到注册中心,并通过注册中心来发现其他微服务。这样,当某个微服务出现故障或下线时,注册中心可以将新的可用实例通知给其他微服务,从而保证系统的可用性。 2. 负载均衡:Spring Cloud中集成了负载均衡的功能,通过负载均衡算法将请求分发到多个可用的服务实例上,从而均衡系统的负载。当某个服务实例出现故障时,负载均衡机制自动将请求转发给其他可用的服务实例,从而保证系统的可用性。 3. 服务熔断与降级:Spring Cloud提供了服务熔断与降级的功能,当某个微服务出现不可用或响应过慢的情况时,可以通过熔断机制来快速切换到备用服务,从而保证系统的可用性。同时,还可以通过降级策略来减少对故障服务的依赖,提高系统的可用性。 4. 分布式追踪与监控:Spring Cloud集成了分布式追踪与监控的组件,可以对系统的各个微服务进行监控和追踪。通过监控和追踪,可以实时了解系统的运行状态,并及时发现并处理潜在的故障,从而保障系统的可用性。 综上所述,Spring Cloud通过服务注册与发现、负载均衡、服务熔断与降级以及分布式追踪与监控等多个方面来保证系统的可用性。开发人员可以根据具体需求选择适合的组件和工具,并合理设计和实施架构来保证系统的稳定运行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值