You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PriorityBeanFactory.java 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2022 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.scanner.bootstrap;
  21. import java.util.ArrayList;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.stream.Collectors;
  25. import javax.annotation.CheckForNull;
  26. import javax.annotation.Nullable;
  27. import org.springframework.beans.factory.support.DefaultListableBeanFactory;
  28. public class PriorityBeanFactory extends DefaultListableBeanFactory {
  29. /**
  30. * Determines highest priority of the bean candidates.
  31. * Does not take into account the @Primary annotations.
  32. * This gets called from {@link DefaultListableBeanFactory#determineAutowireCandidate} when the bean factory is finding the beans to autowire. That method
  33. * checks for @Primary before calling this method.
  34. *
  35. * The strategy is to look at the @Priority annotations. If there are ties, we give priority to components that were added to child containers over their parents.
  36. * If there are still ties, null is returned, which will ultimately cause Spring to throw a NoUniqueBeanDefinitionException.
  37. */
  38. @Override
  39. @Nullable
  40. protected String determineHighestPriorityCandidate(Map<String, Object> candidates, Class<?> requiredType) {
  41. List<Bean> candidateBeans = candidates.entrySet().stream()
  42. .filter(e -> e.getValue() != null)
  43. .map(e -> new Bean(e.getKey(), e.getValue()))
  44. .collect(Collectors.toUnmodifiableList());
  45. List<Bean> beansAfterPriority = highestPriority(candidateBeans, b -> getPriority(b.getInstance()));
  46. if (beansAfterPriority.isEmpty()) {
  47. return null;
  48. } else if (beansAfterPriority.size() == 1) {
  49. return beansAfterPriority.get(0).getName();
  50. }
  51. List<Bean> beansAfterHierarchy = highestPriority(beansAfterPriority, b -> getHierarchyPriority(b.getName()));
  52. if (beansAfterHierarchy.size() == 1) {
  53. return beansAfterHierarchy.get(0).getName();
  54. }
  55. return null;
  56. }
  57. private static List<Bean> highestPriority(List<Bean> candidates, PriorityFunction function) {
  58. List<Bean> highestPriorityBeans = new ArrayList<>();
  59. Integer highestPriority = null;
  60. for (Bean candidate : candidates) {
  61. Integer candidatePriority = function.classify(candidate);
  62. if (candidatePriority == null) {
  63. candidatePriority = Integer.MAX_VALUE;
  64. }
  65. if (highestPriority == null) {
  66. highestPriority = candidatePriority;
  67. highestPriorityBeans.add(candidate);
  68. } else if (candidatePriority < highestPriority) {
  69. highestPriorityBeans.clear();
  70. highestPriority = candidatePriority;
  71. highestPriorityBeans.add(candidate);
  72. } else if (candidatePriority.equals(highestPriority)) {
  73. highestPriorityBeans.add(candidate);
  74. }
  75. }
  76. return highestPriorityBeans;
  77. }
  78. @CheckForNull
  79. private Integer getHierarchyPriority(String beanName) {
  80. DefaultListableBeanFactory factory = this;
  81. int i = 1;
  82. while (factory != null) {
  83. if (factory.containsBeanDefinition(beanName)) {
  84. return i;
  85. }
  86. factory = (DefaultListableBeanFactory) factory.getParentBeanFactory();
  87. i++;
  88. }
  89. return null;
  90. }
  91. private static class Bean {
  92. private final String name;
  93. private final Object instance;
  94. public Bean(String name, Object instance) {
  95. this.name = name;
  96. this.instance = instance;
  97. }
  98. public String getName() {
  99. return name;
  100. }
  101. public Object getInstance() {
  102. return instance;
  103. }
  104. }
  105. @FunctionalInterface
  106. private interface PriorityFunction {
  107. @Nullable
  108. Integer classify(Bean candidate);
  109. }
  110. }