Zouyiu Ng
Zouyiu Ng

Reputation: 193

How to directly get highest ordered bean in Spring without inject list?

There are 3 classes U, V, W implements interface A and annotated by @Order annotation of Spring with different order value.

I'm now getting the highest order bean by injecting List<A> then searching for the first element in List<A>.

Is there a more direct way to get the highest priority bean without inject whole collection of A?

Upvotes: 2

Views: 937

Answers (2)

Olek
Olek

Reputation: 21

Consider javax.annotation.Priority. It seems it may help in your case.

@Service
@Priority(Ordered.HIGHEST_PRECEDENCE + 1)
class U implements A {}

@Service
@Priority(Ordered.HIGHEST_PRECEDENCE + 2)
class V implements A {}

@Service
@Priority(Ordered.HIGHEST_PRECEDENCE + 3)
class W implements A {}

interface A {}

@Service
public class Client {
    @Autowired
    private A a;

    @PostConstruct
    public void init() {
        System.out.println("--------------------------" + a.getClass().getSimpleName());
    }
}

'Client.a' will have instance of 'U' assigned.

DefaultListableBeanFactory

    /**
     * Determine the autowire candidate in the given set of beans.
     * <p>Looks for {@code @Primary} and {@code @Priority} (in that order).
     * @param candidates a Map of candidate names and candidate instances
     * that match the required type, as returned by {@link #findAutowireCandidates}
     * @param descriptor the target dependency to match against
     * @return the name of the autowire candidate, or {@code null} if none found
     */
    protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
    ...
        String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
    ...
    }
    /**
     * Determine the candidate with the highest priority in the given set of beans.
     * <p>Based on {@code @javax.annotation.Priority}. As defined by the related
     * {@link org.springframework.core.Ordered} interface, the lowest value has
     * the highest priority.
     * @param candidates a Map of candidate names and candidate instances
     * (or candidate classes if not created yet) that match the required type
     * @param requiredType the target dependency type to match against
     * @return the name of the candidate with the highest priority,
     * or {@code null} if none found
     * @see #getPriority(Object)
     */
    protected String determineHighestPriorityCandidate(Map<String, Object> candidates, Class<?> requiredType) {
        String highestPriorityBeanName = null;
        Integer highestPriority = null;
        for (Map.Entry<String, Object> entry : candidates.entrySet()) {
            String candidateBeanName = entry.getKey();
            Object beanInstance = entry.getValue();
            Integer candidatePriority = getPriority(beanInstance);
            if (candidatePriority != null) {
                if (highestPriorityBeanName != null) {
                    if (candidatePriority.equals(highestPriority)) {
                        throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
                                "Multiple beans found with the same priority ('" + highestPriority +
                                "') among candidates: " + candidates.keySet());
                    }
                    else if (candidatePriority < highestPriority) {
                        highestPriorityBeanName = candidateBeanName;
                        highestPriority = candidatePriority;
                    }
                }
                else {
                    highestPriorityBeanName = candidateBeanName;
                    highestPriority = candidatePriority;
                }
            }
        }
        return highestPriorityBeanName;
    }

Upvotes: 2

eis
eis

Reputation: 53462

As far as I know there isn't any way in spring to directly get only the highest-ordered bean. Your options thus are

  • iterate through the order beans and get the highest one - what you already do
  • create your own annotation, such as @HighestOrdered, and code your own bean autowiring postprocessor to get only the highest ordered one (example of a custom postprocessor here)

Some other options are

  • use a @Primary to signal the bean you'r prefer to use
  • use a @Qualifier for it

But neither of those are exactly the same what you're asking, and have some downsides.

If you strictly want to do what you're asking in the question, I'd just do what you're already doing and iterate them, picking the highest-ordered.

Upvotes: 0

Related Questions