在spring框架内部以及业务开发中,经常有注入List<T>的场景出现,一般情况下各个T的实现是相互不相干的,这样业务上也不会有问题,但如果实现上有重叠的部分,那么在某些场景中即会出现奇怪的问题。
如下的2个实现
class Impl1 implements Impl{ public boolean canHandle(int i) { return i < 10; } } class Impl2 implements Impl { public boolean canHandle(int i) { return i < 100; } }
上述实现中,程序中注入List<Impl>时 理想的顺序是 1 -> 2, 这样在逻辑上更满足业务的需要.但也有可能,实际注入后为 2 -> 1. 在这种情况下,如果 传入的 值,为 0,那么逻辑将导向 Impl2,在业务上就没有按预期进行。
这里不讨论 Impl本身的实现问题,这里仅为参考。在实际代码上,包括spring 相关框架本身,都存在着实现子类的逻辑互相重叠且相互影响的情况,这时候,注入正确的顺序非常重要.
本文简单描述解决问题的2种做法, 再描述一下spring框架自带的bean排序器.
即在相应的bean在开发时即通过实现 org.springframework.core.Ordered 接口,通过在实现方法中描述当前bean的顺序值. 顺序值越小,则在List中的顺序越靠前.
也可以使用 org.springframework.core.annotation.Order 注解,效果与接口一样.
对于非自己开发的类,则可以使用此种方法,即在定义时重新调整顺序值。通过此种方法定义的顺序值,可以覆盖原bean的接口定义值.
定义如下参考所示(使用configuration factoryMethod定义)
@Configuration class Configuration1 { @Order(20) @Bean public Impl impl1() { return new Impl1(); } }
AnnotationAwareOrderComparator.INSTANCE
此对象为全局默认实例,并且在 DefaultListableBeanFactory.dependencyComparator 中的默认实现值 同样为此值。具体代码为 AnnotationConfigUtils.registerAnnotationConfigProcessors 中 对于 属性 dependencyComparator的处理。作用为在处理List及Array依赖时,对相应的多个依赖对象进行排序。
AnnotationAwareOrderComparator 对象中对于对象的排序依赖于如何来定位此对象的order值,再根据 order值来排序,其默认值为 Ordered.LOWEST_PRECEDENCE. 当多个对象的排序值均为 Ordered.LOWEST_PRECEDENCE 时,最终结果是不确定的.
order值依赖于以下信息(按优先级从高到低)
具体代码可以参考 OrderComparator.doCompare 及 AnnotationAwareOrderComparator.findOrder 的实现.
从上面的顺序来看,已经能够覆盖大部分场景。对于在applicationContext中的List<T>的顺序,即通过 外部传入的 OrderSourceProvider 来解析顺序值. 当然,如果解析不了(返回null),则会继续以上的查找顺序.
容器内的 OrderSourceProvider 实现为 类 FactoryAwareOrderSourceProvider。其实现即将对象在定义时的 Method 和 定义时的目标类(此值要求和查找类不一样才行) 加入到顺序值计算列表中. 如上的参考,即通过在定义Method上增加 @Order 注解来完成顺序值处理,并且因为查找顺序更靠前,因此可以覆盖原定义值.
采用spring boot时,很多类都是通过 @ConditionalOnMissingBean 来定义的。对于定义的类,本身并没有顺序信息并且业务中需要依赖List<T> 进行依赖注入时,则只能通过 在业务系统中重新定义此bean,再显示加上 @Order 注解来解决顺序问题.
已知spring体系中逻辑重叠的类
1 spring hateoas 体系中的 EntityLinks 对象
重叠类为 ControllerEntityLinks RepositoryEntityLinks
使用类为 PluginRegistryFactoryBean
负作用即 生成的Resource中的links 会不正确
2 spring gateway 体系中的 RouteDefinitionLocator 对象
重叠类为 PropertiesRouteDefinitionLocator DiscoveryClientRouteDefinitionLocator
使用类为 CompositeRouteDefinitionLocator
负作用为 在 yaml中配置的 /x/y/z -> A 的路由 可能会因为 discover中的 /x/** -> B,直接默认到B,而不是期望的A