一文带你了解Spring核心接口Ordered的实现及应用

简介: 最近在看框架的时候,发现了这个接口,在此进行总结,希望能够给大家帮助,同时提升自己。

前言

最近在看框架的时候,发现了这个接口,在此进行总结,希望能够给大家帮助,同时提升自己。

order接口的大体介绍

Spring框架中有这个一个接口,名字叫Ordered,联想我们在数据库中应用的Ordered,很容易想到它的含义就是用来排序。那么问题来了,Spring中为什么要定义这样一个排序接口呢。我们知道spring框架使用了大量的策略设计模式。策略设计模式意味着我们的同一个接口,会有大量的不同实现。那么这么多实现,先执行哪个,后执行哪个呢。这就产生了一个排序和优先级的问题,于是Ordered接口登场,用来解决这一问题。

ordered接口的正式介绍

首先我们通过spring的源码看一下Ordered接口,源码如下:

public interface Ordered {
  
    int HIGHEST_PRECEDENCE = -2147483648;

    int LOWEST_PRECEDENCE = 2147483647;

    int getOrder();
  
}

从上述代码中,我们可以看到ordered接口的实现是非常简单的。有一个最高的优先级和一个最低的优先级,还提供了一个获得当前实现类的order数值的方法。
spring的order中。越小的值,优先级越高,越大的值优先级越低。

ordered接口的应用

介绍完ordered接口之后,我们来看一下实际的应用场景。
有一个典型的场景,我们知道spring的事务管理是通过aop切面来实现的。当我们自己写aop实现的时候,与事务的切面同时切到了一段代码。那么spring应该先执行谁呢。举一个具体的例子,我们写了一个切换数据源的aspect切面。如果说事务的执行在数据源切换的前面,那么切换数据源就失败了。我们肯定希望先执行切换数据源,再执行事务。
于是ordered的应用场景就来了。
假设我们写一个下面的切面。

@Component
@Aspect
public class ChangeDataBase implements Ordered {
    
    //拦截所有的service操作
    @Pointcut("execution( * com.color.*.service.*.*(..))")
    public void point() {
    }
 
    @Before("point()")
    public void onlyReadPre() {
        DataSourceContextHolder.setDataSourceType(DataSourceType.MYSQL);
        System.out.println("数据库切换MYSQL");
    }
 
    @After("point()")
    public void onlyReadPast() {
        DataSourceContextHolder.setDataSourceType(DataSourceType.ORACLE);
        System.out.println("数据库切换回ORACLE");
    }
 
    @Override
    public int getOrder() {
        return 1;
    }
}

在上述代码中,我们定义了一个切点,用于拦截所有的service的方法。然后再方法执行前,我们将数据库切换到mysql,方法执行之后,数据库切换成oracle。
最后重写了ordered接口的getOrder方法。这里我们设置order的级别为1。
这个时候,我们在配置事务切面的时候。在xml中配置order。

<tx:annotation-driven transaction-manager="transactionManager" order="2"/>

如果是使用注入bean的方式的话,直接实现接口和上方一样使用即可。
这个时候,我们就会发现。切换数据源的方法会永远在事务之前执行,这就实现了我们的目的。

order注解的使用

读到现在的读者在想,还要实现接口感觉好麻烦啊,有没有什么更方便的方法呢。当然有,我们介绍一下@Order注解。
还是先看一下order注解的源码。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Documented
public @interface Order {
    int value() default 2147483647;
}

默认的优先级是最小的。
我们在使用的时候,只要在类上面打上order注解即可。
我们模拟两个类,打上order注解,然后再spring容器启动的时候,对类进行空参构造函数加载,通过空参构造函数里面的打印情况,我们就可以看到类初始化和执行的顺序。
建立我们的第一个order类。

@Component 
 
//使用order属性,设置该类在spring容器中的加载顺序
@Order(1)  
public class Order1 {
     
    private final int ORDERED = 1;
     
    public Order1(){
        System.out.println(this);
    }
  
    @Override
    public String toString() {
        return "Order1 is  loaded @ORDERED=" + ORDERED + "]";
    }
 
}

建立我们的第二个order类。

@Component 
 
//使用order属性,设置该类在spring容器中的加载顺序
@Order(2)  
public class Order2 {
     
    private final int ORDERED = 2;
     
    public Order2(){
        System.out.println(this);
    }
  
    @Override
    public String toString() {
        return "Order2 is  loaded @ORDERED=" + ORDERED + "]";
    }
 
}

启动spring容器之后,我们看到控制台执行如下结果。

Order1 is  loaded @ORDERED=1]
Order2 is  loaded @ORDERED=2]

orderComparator的介绍

那么我们假如想知道一个类的order的值,或者想比较两个类的order值谁大谁小,这个时候要如何操作呢,Spring贴心的给我们提供了一个类。OrderComparator,通过这个类,我们获得实例后,使用它所提供的getOrder或者compare方法即可实现上述的需求。
我们照例还是先来看一下源码。

public class OrderComparator implements Comparator<Object> {
    public static final OrderComparator INSTANCE = new OrderComparator();

    public OrderComparator() {
    }

    public Comparator<Object> withSourceProvider(OrderComparator.OrderSourceProvider sourceProvider) {
        return (o1, o2) -> {
            return this.doCompare(o1, o2, sourceProvider);
        };
    }

    public int compare(@Nullable Object o1, @Nullable Object o2) {
        return this.doCompare(o1, o2, (OrderComparator.OrderSourceProvider)null);
    }

    private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderComparator.OrderSourceProvider sourceProvider) {
        boolean p1 = o1 instanceof PriorityOrdered;
        boolean p2 = o2 instanceof PriorityOrdered;
        if (p1 && !p2) {
            return -1;
        } else if (p2 && !p1) {
            return 1;
        } else {
            int i1 = this.getOrder(o1, sourceProvider);
            int i2 = this.getOrder(o2, sourceProvider);
            return Integer.compare(i1, i2);
        }
    }

    private int getOrder(@Nullable Object obj, @Nullable OrderComparator.OrderSourceProvider sourceProvider) {
        Integer order = null;
        if (obj != null && sourceProvider != null) {
            Object orderSource = sourceProvider.getOrderSource(obj);
            if (orderSource != null) {
                if (orderSource.getClass().isArray()) {
                    Object[] sources = ObjectUtils.toObjectArray(orderSource);
                    Object[] var6 = sources;
                    int var7 = sources.length;

                    for(int var8 = 0; var8 < var7; ++var8) {
                        Object source = var6[var8];
                        order = this.findOrder(source);
                        if (order != null) {
                            break;
                        }
                    }
                } else {
                    order = this.findOrder(orderSource);
                }
            }
        }

        return order != null ? order.intValue() : this.getOrder(obj);
    }

    protected int getOrder(@Nullable Object obj) {
        if (obj != null) {
            Integer order = this.findOrder(obj);
            if (order != null) {
                return order.intValue();
            }
        }

        return 2147483647;
    }

    @Nullable
    protected Integer findOrder(Object obj) {
        return obj instanceof Ordered ? ((Ordered)obj).getOrder() : null;
    }

    @Nullable
    public Integer getPriority(Object obj) {
        return null;
    }

    public static void sort(List<?> list) {
        if (list.size() > 1) {
            list.sort(INSTANCE);
        }

    }

    public static void sort(Object[] array) {
        if (array.length > 1) {
            Arrays.sort(array, INSTANCE);
        }

    }

    public static void sortIfNecessary(Object value) {
        if (value instanceof Object[]) {
            sort((Object[])((Object[])value));
        } else if (value instanceof List) {
            sort((List)value);
        }

    }

    @FunctionalInterface
    public interface OrderSourceProvider {
        @Nullable
        Object getOrderSource(Object var1);
    }
}

我们先来重点看一下doCompare方法。判断逻辑如下:
若对象o1是Ordered接口类型,o2是PriorityOrdered接口类型,那么o2的优先级高于o1
若对象o1是PriorityOrdered接口类型,o2是Ordered接口类型,那么o1的优先级高于o2
其他情况,若两者都是Ordered接口类型或两者都是PriorityOrdered接口类型,调用Ordered接口的getOrder方法得到order值,order值越大,优先级越小
那么一句话来说就是这样的。
OrderComparator比较器进行排序的时候,若2个对象中有一个对象实现了PriorityOrdered接口,那么这个对象的优先级更高。
若2个对象都是PriorityOrdered或Ordered接口的实现类,那么比较Ordered接口的getOrder方法得到order值,值越低,优先级越高。

再来看一下getOrder方法。
传入一个对象后,通过provider取得原始对象。如果不为空,继续进行判断。
如果是数组对象,对对象进行遍历,得到order后,跳出。如果不是数组则直接获得对象的order。
最后如果order如果不是空,直接返回order的int值,为空的时候,通过findOrder查看,返回的是order的最大值,也就是最低优先级。

protected int getOrder(@Nullable Object obj) {
        if (obj != null) {
            Integer order = this.findOrder(obj);
            if (order != null) {
                return order.intValue();
            }
        }

        return 2147483647;
    }

总结

至此 ordered相关的东西就介绍到此为止,文中难免有不足,希望大家提出指正,感谢。

相关文章
|
2月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
162 1
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
104 62
|
28天前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
基于开源框架Spring AI Alibaba快速构建Java应用
|
29天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
53 2
|
2月前
|
人工智能 开发框架 Java
总计 30 万奖金,Spring AI Alibaba 应用框架挑战赛开赛
Spring AI Alibaba 应用框架挑战赛邀请广大开发者参与开源项目的共建,助力项目快速发展,掌握 AI 应用开发模式。大赛分为《支持 Spring AI Alibaba 应用可视化调试与追踪本地工具》和《基于 Flow 的 AI 编排机制设计与实现》两个赛道,总计 30 万奖金。
|
2月前
|
存储 安全 Java
|
2月前
|
存储 Java 数据管理
强大!用 @Audited 注解增强 Spring Boot 应用,打造健壮的数据审计功能
本文深入介绍了如何在Spring Boot应用中使用`@Audited`注解和`spring-data-envers`实现数据审计功能,涵盖从添加依赖、配置实体类到查询审计数据的具体步骤,助力开发人员构建更加透明、合规的应用系统。
|
2月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
73 0
|
XML NoSQL Java
Spring核心(IoC) 入门解读
Spring核心(IoC) 入门解读
172 1
Spring核心(IoC) 入门解读
|
3月前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。