1.需要了解的知識:
1: 自定義注解,反射,泛型
2:springboot ,condition接口
3:gateway 配置格式:https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/appendix.html
2.gateway注解類的功能
Spring cloud gateway架構中一共有三個注解類 分别是ConditionalOnEnabledFilter,ConditionalOnEnabledGlobalFilter,ConditionalOnEnabledPredicate 根據名字可以知道,他們主要的功能是
當判斷配置檔案開啟 filter, gloable filter,Predicate元件功能 的才會動态組裝目标 Bean. 其中每一個注解類都對應一個condition的子類,OnEnabledFilter ,OnEnabledGlobalFilter, OnEnabledPredicate。
3.使用例子: LoadBalancerClientFilter 元件的例子
@Bean
@ConditionalOnBean(LoadBalancerClient.class)
@ConditionalOnMissingBean({ LoadBalancerClientFilter.class,
ReactiveLoadBalancerClientFilter.class })
@ConditionalOnEnabledGlobalFilter
public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client,
LoadBalancerProperties properties) {
return new LoadBalancerClientFilter(client, properties);
}
4.自定義注解代碼
4.1 ConditionalOnEnabledFilter
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnEnabledFilter.class)
public @interface ConditionalOnEnabledFilter{
/**
* The class component to check for.
* @return the class that must be enabled
*/
Class<? extends GatewayFilterFactory<?>> value() default OnEnabledFilter.DefaultValue.class;
}
4.2 ConditionalOnEnabledGlobalFilter
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnEnabledGlobalFilter.class)
public @interface ConditionalOnEnabledGlobalFilter {
/**
* The class component to check for.
* @return the class that must be enabled
*/
Class<? extends GlobalFilter> value() default OnEnabledGlobalFilter.DefaultValue.class;
}
4.3 ConditionalOnEnabledPredicate
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnEnabledPredicate.class)
public @interface ConditionalOnEnabledPredicate {
/**
* The class components to check for.
* @return the class that must be enabled
*/
Class<? extends RoutePredicateFactory<?>> value() default OnEnabledPredicate.DefaultValue.class;
}
5.Condtion的類圖
1:其中SpringBootCondition 和 ConfigurationCondition 都是spring自帶的注解,其中都是實作了condition接口,因為SpringBootCondition 已經有對 matches 方法的實作,是以在繼承類 OnEnabledComponent 中不需要再實作 matches 方法了。
2:OnEnabledComponent 這個抽象類主要封裝了不同類型的元件的檢查是否開啟的,其主要邏輯是動态擷取方法的傳回類型,然後拿到 類的 SimpleName名字,然後組裝成key,從配置檔案取出對應的value,判斷是否關閉。如果關閉不會生産對應的 Bean對象。
6.OnEnabledComponent 類的注釋
/**
* 因為元件對應的接口不同,用泛型來做通用轉換。
* * @param <T>
*/
public abstract class OnEnabledComponent<T> extends SpringBootCondition
implements ConfigurationCondition {
private static final String PREFIX = "spring.cloud.gateway.";
private static final String SUFFIX = ".enabled";
@Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN;
}
/**
* 這個方法在 SpringBootCondition 接口的 matches 有調用。
* annotationClass() 就是子類實作的注解的名字 比如:{@link ConditionalOnEnabledGlobalFilter}
* getEndpointType 方法拿到的是 org.springframework.cloud.gateway.filter.LoadBalancerClientFilter
* @param context
* @param metadata 被注解的方法資訊資料,比如方法名字,類名字
* @return
*/
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
Class<? extends T> candidate = getEndpointType(annotationClass(), context,
metadata);
return determineOutcome(candidate, context.getEnvironment());
}
/**
* 擷取 被注釋方法 傳回的類型資訊。
* @param annotationClass
* @param context
* @param metadata
* @return
*/
@SuppressWarnings("unchecked")
protected Class<? extends T> getEndpointType(Class<?> annotationClass,
ConditionContext context, AnnotatedTypeMetadata metadata) {
//代碼中沒有為注解設定 value 是以這塊邏輯不會走
Map<String, Object> attributes = metadata
.getAnnotationAttributes(annotationClass.getName());
if (attributes != null && attributes.containsKey("value")) {
Class<?> target = (Class<?>) attributes.get("value");
if (target != defaultValueClass()) {
return (Class<? extends T>) target;
}
}
Assert.state(
metadata instanceof MethodMetadata
&& metadata.isAnnotated(Bean.class.getName()),
getClass().getSimpleName()
+ " must be used on @Bean methods when the value is not specified");
MethodMetadata methodMetadata = (MethodMetadata) metadata;
try {
return (Class<? extends T>) ClassUtils.forName(
methodMetadata.getReturnTypeName(), context.getClassLoader());
}
catch (Throwable ex) {
throw new IllegalStateException("Failed to extract endpoint id for "
+ methodMetadata.getDeclaringClassName() + "."
+ methodMetadata.getMethodName(), ex);
}
}
/**
* 該方法主要作用有2點
* 1:得到配置的key然後 檢查是否設定為false 預設是開啟的。
* 2:根據配置結果組裝condition斷言資訊對象。
* //annotationClass() 就是子類實作的注解的名字 比如:ConditionalOnEnabledGlobalFilter
*
* @param componentClass
* @param resolver
* @return
*/
private ConditionOutcome determineOutcome(Class<? extends T> componentClass,
PropertyResolver resolver) {
String key = PREFIX + normalizeComponentName(componentClass) + SUFFIX;
//組裝Condition斷言結果資訊。
ConditionMessage.Builder messageBuilder = forCondition(
annotationClass().getName(), componentClass.getName());
//檢視配置檔案的配置項
if ("false".equalsIgnoreCase(resolver.getProperty(key))) {
return ConditionOutcome
.noMatch(messageBuilder.because("bean is not available"));
}
return ConditionOutcome.match();
}
//擷取元件的名字
protected abstract String normalizeComponentName(Class<? extends T> componentClass);
//這個是為了動态擷取子類所對應的注解,這樣就可以做到 注解關聯 condition類,然後類中回調
protected abstract Class<?> annotationClass();
protected abstract Class<? extends T> defaultValueClass();
}
7. 總結+收獲
通過閱讀這個代碼更加深入了解了condition接口的用法,以及condition接口如何自定義搭配使用。現在總結起來增加代碼的擴充性的法寶有 1:設計模式 2:springboot 系列注解 3: 泛型 4:spi 5: event bus mq,osgi 等等。