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