文章记录起因:有个项目中开始时用了对象存储,开始是公司一个同事写的,后来过了一段时间后,甲方要求对象存储换为了华为云存储,问题一是不确定原来的还用不用,问题二是改动的地方有点多,如果一个一个地方的去改,需要换回去的时候又要从头到尾再去改一遍。所以需要实现扩展了华为云存储的服务,同时要简单的能换回原来的服务。
实现逻辑:
- 创建一个对象存储的基本接口( IStorageService )
- 让原来的服务与现在需要服务都实现这个基本接口( IStorageService )
- 各自实现对象存储的逻辑
- 两个服务类上使用 @ConditionalOnProperty 注解标明区别
- 配置文件配置需要生效的服务
一、@ConditionalOnProperty
@ConditionalOnProperty
是 Spring 框架中的一个条件注解,它允许你根据应用程序的配置属性来控制是否应该创建一个 bean 或配置一个类。
以下是 @ConditionalOnProperty
注解的主要属性及其详细说明:
-
name
:要检查的属性的名称。这是一个必需的属性。 -
havingValue
:要求属性必须具有的值。如果未指定,那么属性只要存在就足够。 -
prefix
:属性名称的前缀。如果指定了prefix
,name
属性值将会被拼接到prefix
后面。 -
value
:name
属性的别名。通常与name
一起使用,用于提供一个额外的属性名称。 -
matchIfMissing
:当属性不存在时,默认值为false
。如果设置为true
,则即使属性不存在,也会视为匹配成功。
这些属性允许你根据应用程序的配置属性来灵活地控制 bean 或配置类的创建和激活。你可以使用这些属性来根据不同的配置设置,动态地配置和管理应用程序的行为
@ConditionalOnProperty
使用:
@Service
@ConditionalOnProperty(name = "storage.type", havingValue = "huawei")
public class HuaWeiStorImpl implements IStorageService {
@Service
@ConditionalOnProperty(name = "storage.type", havingValue = "qingyun")
public class QjepQingStorServiceImpl implements IStorageService {
@Autowired
private IStorageService storageService;
在上面的示例中,IStorageService
有两个实现类,两个实现类中使用 @ConditionalOnProperty
注解指定了 storage.type
属性,在配置文件中配置 storage.type
这个属性的值来确定生效的服务。当配置文件中的 storage.type
属性值为 huawei
时上面例子中 @Autowired
注入的服务即为 HuaWeiStorImpl
这个服务。
至此这个需求就完成了,下面就是扩展方面了。
二、@Conditional
我们还是来看 @ConditionalOnProperty
@ConditionalOnProperty
注解类中使用了 @Conditional
注解。我们现在来说一说这个注解。
@Conditional
是一个元注解,它可以用于创建自定义条件注解,你可以为自定义的条件注解提供条件评估逻辑。以根据特定条件来决定是否应该创建一个特定的 bean 或配置类。
value
:指定条件评估逻辑的类 。
@Conditional
注释可以以以下任何一种方式使用:如果 @Configuration
类被标记为 @Conditional
,那么与该类相关的所有 @Bean
方法、@Import
注释和 @ComponentScan
注释都将受到这些条件的约束。注意: @Conditional
注释不支持继承;任何来自超类或重写方法的条件都不会被考虑。@Conditional
本身没有声明为 @Inherited
;此外,任何使用 @Conditional
元注释的自定义组合注释都不能声明为 @Inherited
。
理解了 @Conditional
这个注解后,我们再来看 @ConditionalOnProperty
中 @Conditional
标注的信息为 OnPropertyCondition.class
,所以 OnPropertyCondition
这个类就是为 @ConditionalOnProperty
这个注解供条件评估逻辑的类。
从源码中我们可以看到 OnPropertyCondition
这个类继承了 SpringBootCondition
接口。我们再继续进去看
SpringBootCondition
是 Spring 框架中的一个内置条件类,Spring Boot 使用的所有 Condition
实现的基础。提供合理的日志记录,帮助用户诊断加载了哪些类。它是一个抽象类,可以理解为这个类对我们需要实现的 Condition
接口做了封装,以便于实现 Condition
接口。
SpringBootCondition
中有一个抽象方法:
getMatchOutcome (ConditionContext context, AnnotatedTypeMetadata metadata)
:根据条件的评估结果返回ConditionOutcome
对象,表示条件匹配或不匹配的结果。
在继承 SpringBootCondition
类时重写这个方法实现条件评估逻辑。
从源码中我们可以看到 SpringBootCondition
这个类实现了 Condition
接口。
我们再看看 Condition
这个接口干啥的:
Condition
接口是 Spring 框架中用于定义条件评估逻辑的接口,该接口定义了一个方法 matches
,用于评估特定条件是否匹配。通过实现该接口,你可以根据自定义的条件来动态地控制 bean 的创建和激活,从而使得 Spring 应用程序可以更加灵活地适应不同的环境和需求。
-
matches
方法:该方法用于评估特定条件是否匹配。在这个方法中,你可以编写自定义的条件评估逻辑。方法接收两个参数: -
ConditionContext
:当前条件评估的上下文对象,提供了对环境、资源加载器、类加载器等的访问。 -
AnnotatedTypeMetadata
:注解元数据对象,提供了对类、方法或字段上的注解信息的访问。
实现了 Condition
接口的类可以根据需要访问 ConditionContext
和 AnnotatedTypeMetadata
,并根据具体的条件判断逻辑来决定条件是否匹配。如果条件匹配,则返回 true
,否则返回 false
。
看到这里对于 @ConditionalOnProperty
注解的实现过程有了个清晰的认知了:
- 根据需求自定义一个条件注解
- 使用
@Conditional
注解为自定义的注解标准自定义条件评估逻辑类 - 自定义条件评估逻辑类实现
Condition
接口编写自定义的条件评估逻辑;或者继承SpringBootCondition
类时重写getMatchOutcome
方法实现条件评估逻辑。
看到这我们在简单了解下 ConditionContext
和 AnnotatedTypeMetadata
,这两个来是在 Spring 框架中用于条件评估时的两个重要接口,它们提供了访问环境和注解元数据的能力,以便在条件评估过程中做出相应的决策。
ConditionContext
ConditionContext
接口提供了对当前条件评估上下文的访问,包括访问环境、资源加载器、类加载器等。通过 ConditionContext
,你可以获得有关当前应用程序上下文和环境的信息,以便在条件评估逻辑中做出相应的判断。
一些常用的方法包括:
-
getBeanFactory ()
:获取与当前应用程序上下文关联的 Bean 工厂。 -
getEnvironment ()
:获取当前应用程序上下文的环境。 -
getClassLoader ()
:获取用于加载类的类加载器。 -
getResourceLoader ()
:获取用于加载资源的资源加载器。
AnnotatedTypeMetadata
AnnotatedTypeMetadata
接口提供了对注解元数据的访问,包括获取类、方法或字段上的注解信息。通过 AnnotatedTypeMetadata
,你可以获取与类、方法或字段相关的注解信息,以便在条件评估逻辑中做出相应的判断。
一些常用的方法包括:
-
isAnnotated (String annotationType)
:检查类、方法或字段是否被特定类型的注解所标注。 -
getAnnotationAttributes (String annotationType)
:获取特定类型的注解的属性值。 -
getMetaAnnotationAttributes (String annotationType)
:获取特定类型的注解上的元注解的属性值。
这个扩展暂时就到这了,后面如果有需求的话会加上实例啥的,现在主要是记录下笔记了解一下原理,暂时就不弄实例了。