DEV Community

Solon Framework
Solon Framework

Posted on

index vs priority: The Two Ordering Concepts in Solon

If you've written a few Solon apps, you've probably seen index on interceptors or priority on plugins — and wondered whether they're the same thing with different names. They're not. Solon keeps two separate ordering concepts on purpose: index (顺序位, "order position") and priority (优先级, "priority"). This post explains what each one controls, what the built-in annotations actually use, and the one case where @Bean(index) does double duty.

Two concepts, five places

The official docs map each ordering scenario to exactly one concept:

What's being ordered Concept used
Beans of the same kind index
Interceptor execution order index
Filter execution order index
Container lifecycle execution order index
Plugin loading order priority

So: anything inside the container — beans, interceptors, filters, lifecycle — is ordered with index. Plugin loading is the lone exception, ordered with priority.

index: smaller runs first

For index, the smaller the value, the earlier it runs. For around-processing (环绕处理) interceptors, a smaller value also means the handler wraps outside — it enters the call chain earlier and exits later.

One thing the docs warn about explicitly: don't use Integer.MAX_VALUE. It's reserved by the framework. If you reach for it to mean "run last", you may collide with framework-internal ordering slots. Use a normal number instead.

The built-in interceptor order table

Solon ships several annotations that register around-interceptors, and their order positions are fixed:

Annotation Type Kind Order position
@Transaction Interceptor Around 120
@CachePut Interceptor Around 110
@CacheRemove Interceptor Around 110
@Cache Interceptor Around 111
@Valid Interceptor Around 1
@DynamicDs Interceptor Around 100

Reading the table from outside in (smaller index = more outer), a method annotated with all of them would be wrapped roughly like this:

@Valid (1)          → validate parameters, outermost
  @DynamicDs (100)  → switch datasource
    @CachePut/@CacheRemove (110) → write cache
      @Cache (111)  → read cache
        @Transaction (120) → transaction, innermost
          your business method
Enter fullscreen mode Exit fullscreen mode

The ordering makes sense if you think about dependencies: validate input first, then pick the datasource, then check/write cache, and finally open the transaction around the actual database work. If your own interceptor needs to sit before or after these, you now know the exact numbers to position against.

Implicit ordering

You don't always have to set index yourself. Two cases are sorted automatically when there are dependencies:

  • All LifecycleBean implementations (and @Init methods) are auto-ordered when they depend on each other.
  • All @Bean methods are auto-ordered when they depend on each other (supported since v2.5.8).

So index is more of an override — the framework figures out the common dependency cases on its own.

priority: larger runs first

For priority, the larger the value, the earlier it runs — the opposite direction from index. It's used exclusively for plugin loading order, as described in the SPI plugin extension doc.

Why two different systems instead of one? The official docs are refreshingly honest: the framework could have unified them, but wanted to give plugins their own flavor. Practical advice from the docs: for your own plugins, 1 is usually enough.

@bean(index) vs LifecycleBean.index

This is the subtle one. Both look like "index", but they mean different things:

  • @Bean(index) — the position of this bean among beans of the same kind, e.g. when injected as a List<Bean>. It determines the order you get in the collection.
  • LifecycleBean.index — the execution order of this lifecycle interface when AppContext starts.

The catch: if a bean is also a LifecycleBean, then @Bean(index) doubles as its LifecycleBean.index. One annotation, two effects. If you set index on a @Bean method whose bean implements LifecycleBean, you're controlling both the collection order and the start-up order at once — keep that in mind when you see a bean not starting in the order you expected.

The one-liner

Use index (smaller first, outer for around) for beans, interceptors, filters and lifecycle; use priority (larger first) for plugin loading; and remember that for a LifecycleBean, @Bean(index) controls both worlds. The full details live in the official docs — Solon v4.0.4.

Top comments (0)