转载

spring体系中控制List注入时的顺序

在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实现Ordered接口或Order注解

即在相应的bean在开发时即通过实现 org.springframework.core.Ordered 接口,通过在实现方法中描述当前bean的顺序值. 顺序值越小,则在List中的顺序越靠前.

也可以使用 org.springframework.core.annotation.Order 注解,效果与接口一样.

在定义bean时使用Order注解

对于非自己开发的类,则可以使用此种方法,即在定义时重新调整顺序值。通过此种方法定义的顺序值,可以覆盖原bean的接口定义值.

定义如下参考所示(使用configuration factoryMethod定义)

@Configuration
class Configuration1 {
    @Order(20)
    @Bean
    public Impl impl1() {
        return new Impl1();
    }
}

spring体系默认排序器

AnnotationAwareOrderComparator.INSTANCE

此对象为全局默认实例,并且在 DefaultListableBeanFactory.dependencyComparator 中的默认实现值 同样为此值。具体代码为 AnnotationConfigUtils.registerAnnotationConfigProcessors 中 对于 属性 dependencyComparator的处理。作用为在处理List及Array依赖时,对相应的多个依赖对象进行排序。

AnnotationAwareOrderComparator 对象中对于对象的排序依赖于如何来定位此对象的order值,再根据 order值来排序,其默认值为 Ordered.LOWEST_PRECEDENCE. 当多个对象的排序值均为 Ordered.LOWEST_PRECEDENCE 时,最终结果是不确定的.

order值依赖于以下信息(按优先级从高到低)

  • PriorityOrdered 接口,实现了此接口会返回一个具体的order值
  • OrderSourceProvider 对象, 此外部传入对象会返回用于计算order的对象
  • Ordered  接口
  • 对象本身为类,按类处理逻辑执行第6,第7点
  • 对象本身为Method, 查找方法上的 Order 注解
  • 类上的 Order 注解
  • 类上的 javax.annotation.Priority 注解
  • DecoratingProxy 接口中所对应的包装类

具体代码可以参考 OrderComparator.doCompare 及 AnnotationAwareOrderComparator.findOrder 的实现.

从上面的顺序来看,已经能够覆盖大部分场景。对于在applicationContext中的List<T>的顺序,即通过 外部传入的 OrderSourceProvider 来解析顺序值. 当然,如果解析不了(返回null),则会继续以上的查找顺序.

容器内的 OrderSourceProvider 实现为 类 FactoryAwareOrderSourceProvider。其实现即将对象在定义时的 Method 和 定义时的目标类(此值要求和查找类不一样才行) 加入到顺序值计算列表中. 如上的参考,即通过在定义Method上增加 @Order 注解来完成顺序值处理,并且因为查找顺序更靠前,因此可以覆盖原定义值.

覆盖spring boot体系中默认定义的类

采用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

原文  https://www.iflym.com/index.php/code/201911290001.html
正文到此结束
Loading...