天天看點

Spring cloud gatway 源碼解析之注解類 03

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的類圖

Spring cloud gatway 源碼解析之注解類 03

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 等等。