天天看点

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