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
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
LifecycleBeanimplementations (and@Initmethods) are auto-ordered when they depend on each other. - All
@Beanmethods 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 aList<Bean>. It determines the order you get in the collection. -
LifecycleBean.index— the execution order of this lifecycle interface whenAppContextstarts.
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)