Browse Source
We're using the same subtle PriorityOrdered/Ordered/non-ordered separation as for regular BeanFactoryPostProcessors and BeanPostProcessors now. Additionally, we're re-detecting BeanDefinitionRegistryPostProcessor bean names after every invocation phase, up until no further ones appear. Issue: SPR-10630pull/347/head
5 changed files with 447 additions and 356 deletions
@ -0,0 +1,370 @@
@@ -0,0 +1,370 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.context.support; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collection; |
||||
import java.util.HashSet; |
||||
import java.util.LinkedList; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor; |
||||
import org.springframework.beans.factory.config.BeanPostProcessor; |
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; |
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry; |
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; |
||||
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; |
||||
import org.springframework.beans.factory.support.RootBeanDefinition; |
||||
import org.springframework.context.ApplicationListener; |
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
import org.springframework.core.OrderComparator; |
||||
import org.springframework.core.Ordered; |
||||
import org.springframework.core.PriorityOrdered; |
||||
|
||||
/** |
||||
* Delegate for AbstractApplicationContext's post-processor handling. |
||||
* |
||||
* @author Juergen Hoeller |
||||
* @since 4.0 |
||||
*/ |
||||
class PostProcessorRegistrationDelegate { |
||||
|
||||
public static void invokeBeanFactoryPostProcessors( |
||||
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { |
||||
|
||||
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
|
||||
Set<String> processedBeans = new HashSet<String>(); |
||||
|
||||
if (beanFactory instanceof BeanDefinitionRegistry) { |
||||
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; |
||||
List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<BeanFactoryPostProcessor>(); |
||||
List<BeanDefinitionRegistryPostProcessor> registryPostProcessors = |
||||
new LinkedList<BeanDefinitionRegistryPostProcessor>(); |
||||
|
||||
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { |
||||
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { |
||||
BeanDefinitionRegistryPostProcessor registryPostProcessor = |
||||
(BeanDefinitionRegistryPostProcessor) postProcessor; |
||||
registryPostProcessor.postProcessBeanDefinitionRegistry(registry); |
||||
registryPostProcessors.add(registryPostProcessor); |
||||
} |
||||
else { |
||||
regularPostProcessors.add(postProcessor); |
||||
} |
||||
} |
||||
|
||||
// Do not initialize FactoryBeans here: We need to leave all regular beans
|
||||
// uninitialized to let the bean factory post-processors apply to them!
|
||||
// Separate between BeanDefinitionRegistryPostProcessors that implement
|
||||
// PriorityOrdered, Ordered, and the rest.
|
||||
String[] postProcessorNames = |
||||
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); |
||||
|
||||
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
|
||||
List<BeanDefinitionRegistryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>(); |
||||
for (String ppName : postProcessorNames) { |
||||
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { |
||||
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); |
||||
processedBeans.add(ppName); |
||||
} |
||||
} |
||||
OrderComparator.sort(priorityOrderedPostProcessors); |
||||
registryPostProcessors.addAll(priorityOrderedPostProcessors); |
||||
invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry); |
||||
|
||||
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
|
||||
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); |
||||
List<BeanDefinitionRegistryPostProcessor> orderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>(); |
||||
for (String ppName : postProcessorNames) { |
||||
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) { |
||||
orderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); |
||||
processedBeans.add(ppName); |
||||
} |
||||
} |
||||
OrderComparator.sort(orderedPostProcessors); |
||||
registryPostProcessors.addAll(orderedPostProcessors); |
||||
invokeBeanDefinitionRegistryPostProcessors(orderedPostProcessors, registry); |
||||
|
||||
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
|
||||
boolean reiterate = true; |
||||
while (reiterate) { |
||||
reiterate = false; |
||||
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); |
||||
for (String ppName : postProcessorNames) { |
||||
if (!processedBeans.contains(ppName)) { |
||||
BeanDefinitionRegistryPostProcessor pp = beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class); |
||||
registryPostProcessors.add(pp); |
||||
processedBeans.add(ppName); |
||||
pp.postProcessBeanDefinitionRegistry(registry); |
||||
reiterate = true; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
|
||||
invokeBeanFactoryPostProcessors(registryPostProcessors, beanFactory); |
||||
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); |
||||
} |
||||
|
||||
else { |
||||
// Invoke factory processors registered with the context instance.
|
||||
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); |
||||
} |
||||
|
||||
// Do not initialize FactoryBeans here: We need to leave all regular beans
|
||||
// uninitialized to let the bean factory post-processors apply to them!
|
||||
String[] postProcessorNames = |
||||
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); |
||||
|
||||
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
|
||||
// Ordered, and the rest.
|
||||
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); |
||||
List<String> orderedPostProcessorNames = new ArrayList<String>(); |
||||
List<String> nonOrderedPostProcessorNames = new ArrayList<String>(); |
||||
for (String ppName : postProcessorNames) { |
||||
if (processedBeans.contains(ppName)) { |
||||
// skip - already processed in first phase above
|
||||
} |
||||
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { |
||||
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); |
||||
} |
||||
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { |
||||
orderedPostProcessorNames.add(ppName); |
||||
} |
||||
else { |
||||
nonOrderedPostProcessorNames.add(ppName); |
||||
} |
||||
} |
||||
|
||||
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
|
||||
OrderComparator.sort(priorityOrderedPostProcessors); |
||||
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); |
||||
|
||||
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
|
||||
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); |
||||
for (String postProcessorName : orderedPostProcessorNames) { |
||||
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); |
||||
} |
||||
OrderComparator.sort(orderedPostProcessors); |
||||
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory); |
||||
|
||||
// Finally, invoke all other BeanFactoryPostProcessors.
|
||||
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); |
||||
for (String postProcessorName : nonOrderedPostProcessorNames) { |
||||
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class)); |
||||
} |
||||
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory); |
||||
} |
||||
|
||||
public static void registerBeanPostProcessors( |
||||
ConfigurableListableBeanFactory beanFactory, ConfigurableApplicationContext applicationContext) { |
||||
|
||||
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false); |
||||
|
||||
// Register BeanPostProcessorChecker that logs an info message when
|
||||
// a bean is created during BeanPostProcessor instantiation, i.e. when
|
||||
// a bean is not eligible for getting processed by all BeanPostProcessors.
|
||||
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length; |
||||
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount)); |
||||
|
||||
// Separate between BeanPostProcessors that implement PriorityOrdered,
|
||||
// Ordered, and the rest.
|
||||
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>(); |
||||
List<BeanPostProcessor> internalPostProcessors = new ArrayList<BeanPostProcessor>(); |
||||
List<String> orderedPostProcessorNames = new ArrayList<String>(); |
||||
List<String> nonOrderedPostProcessorNames = new ArrayList<String>(); |
||||
for (String ppName : postProcessorNames) { |
||||
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { |
||||
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); |
||||
priorityOrderedPostProcessors.add(pp); |
||||
if (pp instanceof MergedBeanDefinitionPostProcessor) { |
||||
internalPostProcessors.add(pp); |
||||
} |
||||
} |
||||
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { |
||||
orderedPostProcessorNames.add(ppName); |
||||
} |
||||
else { |
||||
nonOrderedPostProcessorNames.add(ppName); |
||||
} |
||||
} |
||||
|
||||
// First, register the BeanPostProcessors that implement PriorityOrdered.
|
||||
OrderComparator.sort(priorityOrderedPostProcessors); |
||||
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors); |
||||
|
||||
// Next, register the BeanPostProcessors that implement Ordered.
|
||||
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>(); |
||||
for (String ppName : orderedPostProcessorNames) { |
||||
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); |
||||
orderedPostProcessors.add(pp); |
||||
if (pp instanceof MergedBeanDefinitionPostProcessor) { |
||||
internalPostProcessors.add(pp); |
||||
} |
||||
} |
||||
OrderComparator.sort(orderedPostProcessors); |
||||
registerBeanPostProcessors(beanFactory, orderedPostProcessors); |
||||
|
||||
// Now, register all regular BeanPostProcessors.
|
||||
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>(); |
||||
for (String ppName : nonOrderedPostProcessorNames) { |
||||
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class); |
||||
nonOrderedPostProcessors.add(pp); |
||||
if (pp instanceof MergedBeanDefinitionPostProcessor) { |
||||
internalPostProcessors.add(pp); |
||||
} |
||||
} |
||||
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors); |
||||
|
||||
// Finally, re-register all internal BeanPostProcessors.
|
||||
OrderComparator.sort(internalPostProcessors); |
||||
registerBeanPostProcessors(beanFactory, internalPostProcessors); |
||||
|
||||
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext)); |
||||
} |
||||
|
||||
/** |
||||
* Invoke the given BeanDefinitionRegistryPostProcessor beans. |
||||
*/ |
||||
private static void invokeBeanDefinitionRegistryPostProcessors( |
||||
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) { |
||||
|
||||
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { |
||||
postProcessor.postProcessBeanDefinitionRegistry(registry); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Invoke the given BeanFactoryPostProcessor beans. |
||||
*/ |
||||
private static void invokeBeanFactoryPostProcessors( |
||||
Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) { |
||||
|
||||
for (BeanFactoryPostProcessor postProcessor : postProcessors) { |
||||
postProcessor.postProcessBeanFactory(beanFactory); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Register the given BeanPostProcessor beans. |
||||
*/ |
||||
private static void registerBeanPostProcessors( |
||||
ConfigurableListableBeanFactory beanFactory, List<BeanPostProcessor> postProcessors) { |
||||
|
||||
for (BeanPostProcessor postProcessor : postProcessors) { |
||||
beanFactory.addBeanPostProcessor(postProcessor); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* BeanPostProcessor that logs an info message when a bean is created during |
||||
* BeanPostProcessor instantiation, i.e. when a bean is not eligible for |
||||
* getting processed by all BeanPostProcessors. |
||||
*/ |
||||
private static class BeanPostProcessorChecker implements BeanPostProcessor { |
||||
|
||||
private static final Log logger = LogFactory.getLog(BeanPostProcessorChecker.class); |
||||
|
||||
private final ConfigurableListableBeanFactory beanFactory; |
||||
|
||||
private final int beanPostProcessorTargetCount; |
||||
|
||||
public BeanPostProcessorChecker(ConfigurableListableBeanFactory beanFactory, int beanPostProcessorTargetCount) { |
||||
this.beanFactory = beanFactory; |
||||
this.beanPostProcessorTargetCount = beanPostProcessorTargetCount; |
||||
} |
||||
|
||||
@Override |
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) { |
||||
return bean; |
||||
} |
||||
|
||||
@Override |
||||
public Object postProcessAfterInitialization(Object bean, String beanName) { |
||||
if (bean != null && !(bean instanceof BeanPostProcessor) && |
||||
this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) { |
||||
if (logger.isInfoEnabled()) { |
||||
logger.info("Bean '" + beanName + "' of type [" + bean.getClass() + |
||||
"] is not eligible for getting processed by all BeanPostProcessors " + |
||||
"(for example: not eligible for auto-proxying)"); |
||||
} |
||||
} |
||||
return bean; |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* BeanPostProcessor that detects beans which implement the ApplicationListener interface. |
||||
* This catches beans that can't reliably be detected by getBeanNamesForType. |
||||
*/ |
||||
private static class ApplicationListenerDetector implements MergedBeanDefinitionPostProcessor { |
||||
|
||||
private static final Log logger = LogFactory.getLog(ApplicationListenerDetector.class); |
||||
|
||||
private final ConfigurableApplicationContext applicationContext; |
||||
|
||||
private final Map<String, Boolean> singletonNames = new ConcurrentHashMap<String, Boolean>(64); |
||||
|
||||
public ApplicationListenerDetector(ConfigurableApplicationContext applicationContext) { |
||||
this.applicationContext = applicationContext; |
||||
} |
||||
|
||||
@Override |
||||
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { |
||||
if (beanDefinition.isSingleton()) { |
||||
this.singletonNames.put(beanName, Boolean.TRUE); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) { |
||||
return bean; |
||||
} |
||||
|
||||
@Override |
||||
public Object postProcessAfterInitialization(Object bean, String beanName) { |
||||
if (bean instanceof ApplicationListener) { |
||||
// potentially not detected as a listener by getBeanNamesForType retrieval
|
||||
Boolean flag = this.singletonNames.get(beanName); |
||||
if (Boolean.TRUE.equals(flag)) { |
||||
// singleton bean (top-level or inner): register on the fly
|
||||
this.applicationContext.addApplicationListener((ApplicationListener<?>) bean); |
||||
} |
||||
else if (flag == null) { |
||||
if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) { |
||||
// inner bean with other scope - can't reliably process events
|
||||
logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " + |
||||
"but is not reachable for event multicasting by its containing ApplicationContext " + |
||||
"because it does not have singleton scope. Only top-level listener beans are allowed " + |
||||
"to be of non-singleton scope."); |
||||
} |
||||
this.singletonNames.put(beanName, Boolean.FALSE); |
||||
} |
||||
} |
||||
return bean; |
||||
} |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue