|
17 | 17 | package org.springframework.boot;
|
18 | 18 |
|
19 | 19 | import java.lang.StackWalker.StackFrame;
|
| 20 | +import java.lang.reflect.Method; |
20 | 21 | import java.time.Duration;
|
21 | 22 | import java.util.ArrayList;
|
22 | 23 | import java.util.Arrays;
|
23 | 24 | import java.util.Collection;
|
24 | 25 | import java.util.Collections;
|
| 26 | +import java.util.Comparator; |
25 | 27 | import java.util.HashMap;
|
| 28 | +import java.util.IdentityHashMap; |
26 | 29 | import java.util.LinkedHashSet;
|
27 | 30 | import java.util.List;
|
28 | 31 | import java.util.Map;
|
|
38 | 41 |
|
39 | 42 | import org.springframework.aot.AotDetector;
|
40 | 43 | import org.springframework.beans.BeansException;
|
| 44 | +import org.springframework.beans.factory.NoSuchBeanDefinitionException; |
41 | 45 | import org.springframework.beans.factory.config.BeanDefinition;
|
42 | 46 | import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
| 47 | +import org.springframework.beans.factory.config.ConfigurableBeanFactory; |
43 | 48 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
44 | 49 | import org.springframework.beans.factory.groovy.GroovyBeanDefinitionReader;
|
45 | 50 | import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
|
46 | 51 | import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
47 | 52 | import org.springframework.beans.factory.support.BeanNameGenerator;
|
48 | 53 | import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
| 54 | +import org.springframework.beans.factory.support.RootBeanDefinition; |
49 | 55 | import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
50 | 56 | import org.springframework.boot.Banner.Mode;
|
51 | 57 | import org.springframework.boot.context.properties.bind.Bindable;
|
|
68 | 74 | import org.springframework.context.support.AbstractApplicationContext;
|
69 | 75 | import org.springframework.context.support.GenericApplicationContext;
|
70 | 76 | import org.springframework.core.GenericTypeResolver;
|
| 77 | +import org.springframework.core.OrderComparator; |
| 78 | +import org.springframework.core.OrderComparator.OrderSourceProvider; |
71 | 79 | import org.springframework.core.Ordered;
|
72 | 80 | import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
73 | 81 | import org.springframework.core.annotation.Order;
|
@@ -746,35 +754,42 @@ protected void refresh(ConfigurableApplicationContext applicationContext) {
|
746 | 754 | protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
|
747 | 755 | }
|
748 | 756 |
|
749 |
| - private void callRunners(ApplicationContext context, ApplicationArguments args) { |
750 |
| - context.getBeanProvider(Runner.class).orderedStream().forEach((runner) -> { |
751 |
| - if (runner instanceof ApplicationRunner applicationRunner) { |
752 |
| - callRunner(applicationRunner, args); |
753 |
| - } |
754 |
| - if (runner instanceof CommandLineRunner commandLineRunner) { |
755 |
| - callRunner(commandLineRunner, args); |
756 |
| - } |
757 |
| - }); |
| 757 | + private void callRunners(ConfigurableApplicationContext context, ApplicationArguments args) { |
| 758 | + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); |
| 759 | + String[] beanNames = beanFactory.getBeanNamesForType(Runner.class); |
| 760 | + Map<Runner, String> instancesToBeanNames = new IdentityHashMap<>(); |
| 761 | + for (String beanName : beanNames) { |
| 762 | + instancesToBeanNames.put(beanFactory.getBean(beanName, Runner.class), beanName); |
| 763 | + } |
| 764 | + Comparator<Object> comparator = getOrderComparator(beanFactory) |
| 765 | + .withSourceProvider(new FactoryAwareOrderSourceProvider(beanFactory, instancesToBeanNames)); |
| 766 | + instancesToBeanNames.keySet().stream().sorted(comparator).forEach((runner) -> callRunner(runner, args)); |
758 | 767 | }
|
759 | 768 |
|
760 |
| - private void callRunner(ApplicationRunner runner, ApplicationArguments args) { |
761 |
| - try { |
762 |
| - (runner).run(args); |
763 |
| - } |
764 |
| - catch (Exception ex) { |
765 |
| - throw new IllegalStateException("Failed to execute ApplicationRunner", ex); |
766 |
| - } |
| 769 | + private OrderComparator getOrderComparator(ConfigurableListableBeanFactory beanFactory) { |
| 770 | + Comparator<?> dependencyComparator = (beanFactory instanceof DefaultListableBeanFactory defaultListableBeanFactory) |
| 771 | + ? defaultListableBeanFactory.getDependencyComparator() : null; |
| 772 | + return (dependencyComparator instanceof OrderComparator orderComparator) ? orderComparator |
| 773 | + : AnnotationAwareOrderComparator.INSTANCE; |
767 | 774 | }
|
768 | 775 |
|
769 |
| - private void callRunner(CommandLineRunner runner, ApplicationArguments args) { |
770 |
| - try { |
771 |
| - (runner).run(args.getSourceArgs()); |
| 776 | + private void callRunner(Runner runner, ApplicationArguments args) { |
| 777 | + if (runner instanceof ApplicationRunner) { |
| 778 | + callRunner(ApplicationRunner.class, runner, (applicationRunner) -> applicationRunner.run(args)); |
772 | 779 | }
|
773 |
| - catch (Exception ex) { |
774 |
| - throw new IllegalStateException("Failed to execute CommandLineRunner", ex); |
| 780 | + if (runner instanceof CommandLineRunner) { |
| 781 | + callRunner(CommandLineRunner.class, runner, |
| 782 | + (commandLineRunner) -> commandLineRunner.run(args.getSourceArgs())); |
775 | 783 | }
|
776 | 784 | }
|
777 | 785 |
|
| 786 | + @SuppressWarnings("unchecked") |
| 787 | + private <R extends Runner> void callRunner(Class<R> type, Runner runner, ThrowingConsumer<R> call) { |
| 788 | + call.throwing( |
| 789 | + (message, ex) -> new IllegalStateException("Failed to execute " + ClassUtils.getShortName(type), ex)) |
| 790 | + .accept((R) runner); |
| 791 | + } |
| 792 | + |
778 | 793 | private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
|
779 | 794 | SpringApplicationRunListeners listeners) {
|
780 | 795 | try {
|
@@ -1598,4 +1613,41 @@ public SpringApplicationRunListener getRunListener(SpringApplication springAppli
|
1598 | 1613 |
|
1599 | 1614 | }
|
1600 | 1615 |
|
| 1616 | + /** |
| 1617 | + * {@link OrderSourceProvider} used to obtain factory method and target type order |
| 1618 | + * sources. Based on internal {@link DefaultListableBeanFactory} code. |
| 1619 | + */ |
| 1620 | + private class FactoryAwareOrderSourceProvider implements OrderSourceProvider { |
| 1621 | + |
| 1622 | + private final ConfigurableBeanFactory beanFactory; |
| 1623 | + |
| 1624 | + private final Map<?, String> instancesToBeanNames; |
| 1625 | + |
| 1626 | + FactoryAwareOrderSourceProvider(ConfigurableBeanFactory beanFactory, Map<?, String> instancesToBeanNames) { |
| 1627 | + this.beanFactory = beanFactory; |
| 1628 | + this.instancesToBeanNames = instancesToBeanNames; |
| 1629 | + } |
| 1630 | + |
| 1631 | + @Override |
| 1632 | + public Object getOrderSource(Object obj) { |
| 1633 | + String beanName = this.instancesToBeanNames.get(obj); |
| 1634 | + return (beanName != null) ? getOrderSource(beanName, obj.getClass()) : null; |
| 1635 | + } |
| 1636 | + |
| 1637 | + private Object getOrderSource(String beanName, Class<?> instanceType) { |
| 1638 | + try { |
| 1639 | + RootBeanDefinition beanDefinition = (RootBeanDefinition) this.beanFactory |
| 1640 | + .getMergedBeanDefinition(beanName); |
| 1641 | + Method factoryMethod = beanDefinition.getResolvedFactoryMethod(); |
| 1642 | + Class<?> targetType = beanDefinition.getTargetType(); |
| 1643 | + targetType = (targetType != instanceType) ? targetType : null; |
| 1644 | + return Stream.of(factoryMethod, targetType).filter(Objects::nonNull).toArray(); |
| 1645 | + } |
| 1646 | + catch (NoSuchBeanDefinitionException ex) { |
| 1647 | + return null; |
| 1648 | + } |
| 1649 | + } |
| 1650 | + |
| 1651 | + } |
| 1652 | + |
1601 | 1653 | }
|
0 commit comments