diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanLifecycleNotice.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanLifecycleNotice.java new file mode 100644 index 000000000000..3f686cd4ab64 --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanLifecycleNotice.java @@ -0,0 +1,60 @@ +package org.springframework.beans.factory.config; + +/** + * Bean life cycle Notice + * + * @author Zen Huifer + */ +public interface BeanLifecycleNotice { + + + void notice(BeanLifecycleEnum lifecycleEnum, BeanLifecycleEvent event); + + /** + * Bean life cycle type + */ + enum BeanLifecycleEnum { + created, + destroyed, + } + + /** + * Bean life cycle event + */ + class BeanLifecycleEvent { + /** + * event name + */ + private String beanName; + + + /** + * bean instance + */ + private Object bean; + + + + public BeanLifecycleEvent(String beanName, Object bean) { + this.beanName = beanName; + this.bean = bean; + } + + public String getBeanName() { + return beanName; + } + + public void setBeanName(String beanName) { + this.beanName = beanName; + } + + public Object getBean() { + return bean; + } + + public void setBean(Object bean) { + this.bean = bean; + } + } + +} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanPostProcessor.java index 7288aa476cf4..ccfa45703196 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanPostProcessor.java @@ -101,4 +101,15 @@ default Object postProcessAfterInitialization(Object bean, String beanName) thro return bean; } + /** + * Used to determine whether the current beanPostProcess can process + * @param bean the new bean instance + * @param beanName the name of the bean + * @return true or false + */ + @Nullable + default boolean support(Object bean, String beanName) { + return true; + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java index 81f4d8760154..2b162be8fbec 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java @@ -244,6 +244,7 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single * @param beanPostProcessor the post-processor to register */ void addBeanPostProcessor(BeanPostProcessor beanPostProcessor); + void addBeanLifecycleNotice(BeanLifecycleNotice beanLifecycleNotice); /** * Return the current number of registered BeanPostProcessors, if any. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index c1e764b22a5f..3bac4acf0318 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -63,6 +63,7 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.AutowiredPropertyMarker; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanLifecycleNotice; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConstructorArgumentValues; @@ -124,22 +125,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { - /** Strategy for creating bean instances. */ - private InstantiationStrategy instantiationStrategy; - - /** Resolver strategy for method parameter names. */ - @Nullable - private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); - - /** Whether to automatically try to resolve circular references between beans. */ - private boolean allowCircularReferences = true; - - /** - * Whether to resort to injecting a raw bean instance in case of circular reference, - * even if the injected bean eventually got wrapped. - */ - private boolean allowRawInjectionDespiteWrapping = false; - /** * Dependency types to ignore on dependency check and autowire, as Set of * Class objects: for example, String. Default is none. @@ -168,6 +153,22 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac private final ConcurrentMap, PropertyDescriptor[]> filteredPropertyDescriptorsCache = new ConcurrentHashMap<>(); + /** Strategy for creating bean instances. */ + private InstantiationStrategy instantiationStrategy; + + /** Resolver strategy for method parameter names. */ + @Nullable + private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); + + /** Whether to automatically try to resolve circular references between beans. */ + private boolean allowCircularReferences = true; + + /** + * Whether to resort to injecting a raw bean instance in case of circular reference, + * even if the injected bean eventually got wrapped. + */ + private boolean allowRawInjectionDespiteWrapping = false; + /** * Create a new AbstractAutowireCapableBeanFactory. @@ -194,6 +195,12 @@ public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactor setParentBeanFactory(parentBeanFactory); } + /** + * Return the instantiation strategy to use for creating bean instances. + */ + protected InstantiationStrategy getInstantiationStrategy() { + return this.instantiationStrategy; + } /** * Set the instantiation strategy to use for creating bean instances. @@ -205,10 +212,12 @@ public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy } /** - * Return the instantiation strategy to use for creating bean instances. + * Return the ParameterNameDiscoverer to use for resolving method parameter + * names if needed. */ - protected InstantiationStrategy getInstantiationStrategy() { - return this.instantiationStrategy; + @Nullable + protected ParameterNameDiscoverer getParameterNameDiscoverer() { + return this.parameterNameDiscoverer; } /** @@ -221,12 +230,12 @@ public void setParameterNameDiscoverer(@Nullable ParameterNameDiscoverer paramet } /** - * Return the ParameterNameDiscoverer to use for resolving method parameter - * names if needed. + * Return whether to allow circular references between beans. + * @since 5.3.10 + * @see #setAllowCircularReferences */ - @Nullable - protected ParameterNameDiscoverer getParameterNameDiscoverer() { - return this.parameterNameDiscoverer; + public boolean isAllowCircularReferences() { + return this.allowCircularReferences; } /** @@ -247,12 +256,12 @@ public void setAllowCircularReferences(boolean allowCircularReferences) { } /** - * Return whether to allow circular references between beans. + * Return whether to allow the raw injection of a bean instance. * @since 5.3.10 - * @see #setAllowCircularReferences + * @see #setAllowRawInjectionDespiteWrapping */ - public boolean isAllowCircularReferences() { - return this.allowCircularReferences; + public boolean isAllowRawInjectionDespiteWrapping() { + return this.allowRawInjectionDespiteWrapping; } /** @@ -273,15 +282,6 @@ public void setAllowRawInjectionDespiteWrapping(boolean allowRawInjectionDespite this.allowRawInjectionDespiteWrapping = allowRawInjectionDespiteWrapping; } - /** - * Return whether to allow the raw injection of a bean instance. - * @since 5.3.10 - * @see #setAllowRawInjectionDespiteWrapping - */ - public boolean isAllowRawInjectionDespiteWrapping() { - return this.allowRawInjectionDespiteWrapping; - } - /** * Ignore the given dependency type for autowiring: * for example, String. Default is none. @@ -437,11 +437,13 @@ public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, S Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { - Object current = processor.postProcessBeforeInitialization(result, beanName); - if (current == null) { - return result; + if (processor.support(result, beanName)) { + Object current = processor.postProcessBeforeInitialization(result, beanName); + if (current == null) { + return result; + } + result = current; } - result = current; } return result; } @@ -452,11 +454,17 @@ public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, St Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { - Object current = processor.postProcessAfterInitialization(result, beanName); - if (current == null) { - return result; + if (processor.support(result, beanName)) { + Object current = processor.postProcessAfterInitialization(result, beanName); + if (current == null) { + return result; + } + result = current; } - result = current; + + } + for (BeanLifecycleNotice beanLifecycleNotice : getBeanLifecycleNotices()) { + beanLifecycleNotice.notice(BeanLifecycleNotice.BeanLifecycleEnum.created,new BeanLifecycleNotice.BeanLifecycleEvent(beanName,existingBean)); } return result; } @@ -464,7 +472,7 @@ public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, St @Override public void destroyBean(Object existingBean) { new DisposableBeanAdapter( - existingBean, getBeanPostProcessorCache().destructionAware, getAccessControlContext()).destroy(); + existingBean, getBeanPostProcessorCache().destructionAware, getAccessControlContext(),getBeanLifecycleNotices()).destroy(); } @@ -646,11 +654,11 @@ else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + - StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + - "] in its raw version as part of a circular reference, but has eventually been " + - "wrapped. This means that said other beans do not use the final version of the " + - "bean. This is often the result of over-eager type matching - consider using " + - "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + + "] in its raw version as part of a circular reference, but has eventually been " + + "wrapped. This means that said other beans do not use the final version of the " + + "bean. This is often the result of over-eager type matching - consider using " + + "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); } } } @@ -1811,6 +1819,11 @@ protected Object initializeBean(String beanName, Object bean, @Nullable RootBean return wrappedBean; } + /** + * todo: 生命周期通知 + * @param beanName + * @param bean + */ private void invokeAwareMethods(String beanName, Object bean) { if (bean instanceof Aware) { if (bean instanceof BeanNameAware) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index e36cef415730..92fcd525047e 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -63,6 +63,7 @@ import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.BeanExpressionContext; import org.springframework.beans.factory.config.BeanExpressionResolver; +import org.springframework.beans.factory.config.BeanLifecycleNotice; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor; @@ -159,6 +160,11 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp /** BeanPostProcessors to apply. */ private final List beanPostProcessors = new BeanPostProcessorCacheAwareList(); + /** + * BeanLifecycleNotice to apply. + */ + private final List beanLifecycleNotices = new CopyOnWriteArrayList<>(); + /** Cache of pre-filtered post-processors. */ @Nullable private volatile BeanPostProcessorCache beanPostProcessorCache; @@ -950,6 +956,13 @@ public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) { this.beanPostProcessors.add(beanPostProcessor); } + @Override + public void addBeanLifecycleNotice(BeanLifecycleNotice beanLifecycleNotice) { + Assert.notNull(beanLifecycleNotice, "BeanLifecycleNotice must not be null"); + this.beanLifecycleNotices.remove(beanLifecycleNotice); + this.beanLifecycleNotices.add(beanLifecycleNotice); + } + /** * Add new BeanPostProcessors that will get applied to beans created * by this factory. To be invoked during factory configuration. @@ -974,6 +987,10 @@ public List getBeanPostProcessors() { return this.beanPostProcessors; } + public List getBeanLifecycleNotices() { + return beanLifecycleNotices; + } + /** * Return the internal cache of pre-filtered post-processors, * freshly (re-)building it if necessary. @@ -1222,7 +1239,7 @@ public void destroyBean(String beanName, Object beanInstance) { */ protected void destroyBean(String beanName, Object bean, RootBeanDefinition mbd) { new DisposableBeanAdapter( - bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, getAccessControlContext()).destroy(); + bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, getAccessControlContext(),getBeanLifecycleNotices()).destroy(); } @Override @@ -1932,7 +1949,7 @@ protected void registerDisposableBeanIfNecessary(String beanName, Object bean, R // work for the given bean: DestructionAwareBeanPostProcessors, // DisposableBean interface, custom destroy method. registerDisposableBean(beanName, new DisposableBeanAdapter( - bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc)); + bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc,getBeanLifecycleNotices())); } else { // A bean with a custom scope... @@ -1941,7 +1958,7 @@ protected void registerDisposableBeanIfNecessary(String beanName, Object bean, R throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'"); } scope.registerDestructionCallback(beanName, new DisposableBeanAdapter( - bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc)); + bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc,getBeanLifecycleNotices())); } } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java index 4317ae901c9b..45960f8a7a36 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java @@ -32,6 +32,7 @@ import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.config.BeanLifecycleNotice; import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -103,7 +104,9 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { * (potentially DestructionAwareBeanPostProcessor), if any */ public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition beanDefinition, - List postProcessors, @Nullable AccessControlContext acc) { + List postProcessors, @Nullable AccessControlContext acc, + @Nullable List beanLifecycleNotices + ) { Assert.notNull(bean, "Disposable bean must not be null"); this.bean = bean; @@ -147,6 +150,7 @@ else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) { this.beanPostProcessors = filterPostProcessors(postProcessors, bean); this.acc = acc; + this.beanLifecycleNotices = beanLifecycleNotices; } /** @@ -156,7 +160,9 @@ else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) { * (potentially DestructionAwareBeanPostProcessor), if any */ public DisposableBeanAdapter( - Object bean, List postProcessors, AccessControlContext acc) { + Object bean, List postProcessors, AccessControlContext acc, + List beanLifecycleNotices + ) { Assert.notNull(bean, "Disposable bean must not be null"); this.bean = bean; @@ -165,14 +171,19 @@ public DisposableBeanAdapter( this.invokeDisposableBean = (this.bean instanceof DisposableBean); this.beanPostProcessors = filterPostProcessors(postProcessors, bean); this.acc = acc; + this.beanLifecycleNotices = beanLifecycleNotices; } + private List beanLifecycleNotices; + /** * Create a new DisposableBeanAdapter for the given bean. */ private DisposableBeanAdapter(Object bean, String beanName, boolean nonPublicAccessAllowed, boolean invokeDisposableBean, boolean invokeAutoCloseable, @Nullable String destroyMethodName, - @Nullable List postProcessors) { + @Nullable List postProcessors, + @Nullable List beanLifecycleNotices + ) { this.bean = bean; this.beanName = beanName; @@ -182,6 +193,7 @@ private DisposableBeanAdapter(Object bean, String beanName, boolean nonPublicAcc this.destroyMethodName = destroyMethodName; this.beanPostProcessors = postProcessors; this.acc = null; + this.beanLifecycleNotices = beanLifecycleNotices; } @@ -258,6 +270,7 @@ else if (this.destroyMethodName != null) { invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(destroyMethod, this.bean.getClass())); } } + } @@ -352,7 +365,7 @@ protected Object writeReplace() { } return new DisposableBeanAdapter( this.bean, this.beanName, this.nonPublicAccessAllowed, this.invokeDisposableBean, - this.invokeAutoCloseable, this.destroyMethodName, serializablePostProcessors); + this.invokeAutoCloseable, this.destroyMethodName, serializablePostProcessors,this.beanLifecycleNotices); } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java index 9a6f86a8382c..883cb75d7dbf 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/FactoryBeanTests.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.support.DefaultListableBeanFactory; diff --git a/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java b/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java index 493eb3789428..535e0f82dac8 100644 --- a/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java +++ b/spring-context/src/main/java/org/springframework/context/support/PostProcessorRegistrationDelegate.java @@ -28,6 +28,7 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.BeanLifecycleNotice; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanFactory; @@ -56,8 +57,7 @@ private PostProcessorRegistrationDelegate() { } - public static void invokeBeanFactoryPostProcessors( - ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) { + public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) { // WARNING: Although it may appear that the body of this method can be easily // refactored to avoid the use of multiple loops and multiple lists, the use @@ -82,8 +82,7 @@ public static void invokeBeanFactoryPostProcessors( for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { - BeanDefinitionRegistryPostProcessor registryProcessor = - (BeanDefinitionRegistryPostProcessor) postProcessor; + BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor; registryProcessor.postProcessBeanDefinitionRegistry(registry); registryProcessors.add(registryProcessor); } @@ -99,8 +98,7 @@ public static void invokeBeanFactoryPostProcessors( List currentRegistryProcessors = new ArrayList<>(); // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered. - String[] postProcessorNames = - beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); + String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); @@ -155,8 +153,7 @@ public static void invokeBeanFactoryPostProcessors( // 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); + String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); // Separate between BeanFactoryPostProcessors that implement PriorityOrdered, // Ordered, and the rest. @@ -202,8 +199,7 @@ else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { beanFactory.clearMetadataCache(); } - public static void registerBeanPostProcessors( - ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) { + public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) { // WARNING: Although it may appear that the body of this method can be easily // refactored to avoid the use of multiple loops and multiple lists, the use @@ -282,6 +278,13 @@ else if (beanFactory.isTypeMatch(ppName, Ordered.class)) { // Re-register post-processor for detecting inner beans as ApplicationListeners, // moving it to the end of the processor chain (for picking up proxies etc). beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext)); + + + Collection values = beanFactory.getBeansOfType(BeanLifecycleNotice.class).values(); + for (BeanLifecycleNotice value : values) { + beanFactory.addBeanLifecycleNotice(value); + } + } private static void sortPostProcessors(List postProcessors, ConfigurableListableBeanFactory beanFactory) { @@ -302,12 +305,10 @@ private static void sortPostProcessors(List postProcessors, ConfigurableLista /** * Invoke the given BeanDefinitionRegistryPostProcessor beans. */ - private static void invokeBeanDefinitionRegistryPostProcessors( - Collection postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) { + private static void invokeBeanDefinitionRegistryPostProcessors(Collection postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) { for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { - StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process") - .tag("postProcessor", postProcessor::toString); + StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process").tag("postProcessor", postProcessor::toString); postProcessor.postProcessBeanDefinitionRegistry(registry); postProcessBeanDefRegistry.end(); } @@ -316,12 +317,10 @@ private static void invokeBeanDefinitionRegistryPostProcessors( /** * Invoke the given BeanFactoryPostProcessor beans. */ - private static void invokeBeanFactoryPostProcessors( - Collection postProcessors, ConfigurableListableBeanFactory beanFactory) { + private static void invokeBeanFactoryPostProcessors(Collection postProcessors, ConfigurableListableBeanFactory beanFactory) { for (BeanFactoryPostProcessor postProcessor : postProcessors) { - StartupStep postProcessBeanFactory = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process") - .tag("postProcessor", postProcessor::toString); + StartupStep postProcessBeanFactory = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process").tag("postProcessor", postProcessor::toString); postProcessor.postProcessBeanFactory(beanFactory); postProcessBeanFactory.end(); } @@ -330,8 +329,7 @@ private static void invokeBeanFactoryPostProcessors( /** * Register the given BeanPostProcessor beans. */ - private static void registerBeanPostProcessors( - ConfigurableListableBeanFactory beanFactory, List postProcessors) { + private static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, List postProcessors) { if (beanFactory instanceof AbstractBeanFactory) { // Bulk addition is more efficient against our CopyOnWriteArrayList there @@ -370,12 +368,9 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) { @Override public Object postProcessAfterInitialization(Object bean, String beanName) { - if (!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) && - this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) { + if (!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) && this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) { if (logger.isInfoEnabled()) { - logger.info("Bean '" + beanName + "' of type [" + bean.getClass().getName() + - "] is not eligible for getting processed by all BeanPostProcessors " + - "(for example: not eligible for auto-proxying)"); + logger.info("Bean '" + beanName + "' of type [" + bean.getClass().getName() + "] is not eligible for getting processed by all BeanPostProcessors " + "(for example: not eligible for auto-proxying)"); } } return bean; diff --git a/spring-context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests.java b/spring-context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests.java index 13396f6628e2..2d84bfcb3f8a 100644 --- a/spring-context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests.java +++ b/spring-context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests.java @@ -46,6 +46,7 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.UnsatisfiedDependencyException; +import org.springframework.beans.factory.config.BeanLifecycleNotice; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; @@ -83,49 +84,86 @@ class XmlBeanFactoryTests { private static final Class CLASS = XmlBeanFactoryTests.class; + private static final String CLASSNAME = CLASS.getSimpleName(); private static final ClassPathResource AUTOWIRE_CONTEXT = classPathResource("-autowire.xml"); + private static final ClassPathResource CHILD_CONTEXT = classPathResource("-child.xml"); + private static final ClassPathResource CLASS_NOT_FOUND_CONTEXT = classPathResource("-classNotFound.xml"); + private static final ClassPathResource COMPLEX_FACTORY_CIRCLE_CONTEXT = classPathResource("-complexFactoryCircle.xml"); + private static final ClassPathResource CONSTRUCTOR_ARG_CONTEXT = classPathResource("-constructorArg.xml"); + private static final ClassPathResource CONSTRUCTOR_OVERRIDES_CONTEXT = classPathResource("-constructorOverrides.xml"); + private static final ClassPathResource DELEGATION_OVERRIDES_CONTEXT = classPathResource("-delegationOverrides.xml"); + private static final ClassPathResource DEP_CARG_AUTOWIRE_CONTEXT = classPathResource("-depCargAutowire.xml"); + private static final ClassPathResource DEP_CARG_INNER_CONTEXT = classPathResource("-depCargInner.xml"); + private static final ClassPathResource DEP_CARG_CONTEXT = classPathResource("-depCarg.xml"); + private static final ClassPathResource DEP_DEPENDSON_INNER_CONTEXT = classPathResource("-depDependsOnInner.xml"); + private static final ClassPathResource DEP_DEPENDSON_CONTEXT = classPathResource("-depDependsOn.xml"); + private static final ClassPathResource DEP_PROP = classPathResource("-depProp.xml"); + private static final ClassPathResource DEP_PROP_ABN_CONTEXT = classPathResource("-depPropAutowireByName.xml"); + private static final ClassPathResource DEP_PROP_ABT_CONTEXT = classPathResource("-depPropAutowireByType.xml"); + private static final ClassPathResource DEP_PROP_MIDDLE_CONTEXT = classPathResource("-depPropInTheMiddle.xml"); + private static final ClassPathResource DEP_PROP_INNER_CONTEXT = classPathResource("-depPropInner.xml"); + private static final ClassPathResource DEP_MATERIALIZE_CONTEXT = classPathResource("-depMaterializeThis.xml"); + private static final ClassPathResource FACTORY_CIRCLE_CONTEXT = classPathResource("-factoryCircle.xml"); + private static final ClassPathResource INITIALIZERS_CONTEXT = classPathResource("-initializers.xml"); + private static final ClassPathResource INVALID_CONTEXT = classPathResource("-invalid.xml"); + private static final ClassPathResource INVALID_NO_SUCH_METHOD_CONTEXT = classPathResource("-invalidOverridesNoSuchMethod.xml"); + private static final ClassPathResource COLLECTIONS_XSD_CONTEXT = classPathResource("-localCollectionsUsingXsd.xml"); + private static final ClassPathResource MISSING_CONTEXT = classPathResource("-missing.xml"); + private static final ClassPathResource OVERRIDES_CONTEXT = classPathResource("-overrides.xml"); + private static final ClassPathResource PARENT_CONTEXT = classPathResource("-parent.xml"); + private static final ClassPathResource NO_SUCH_FACTORY_METHOD_CONTEXT = classPathResource("-noSuchFactoryMethod.xml"); + private static final ClassPathResource RECURSIVE_IMPORT_CONTEXT = classPathResource("-recursiveImport.xml"); + private static final ClassPathResource RESOURCE_CONTEXT = classPathResource("-resource.xml"); + private static final ClassPathResource TEST_WITH_DUP_NAMES_CONTEXT = classPathResource("-testWithDuplicateNames.xml"); + private static final ClassPathResource TEST_WITH_DUP_NAME_IN_ALIAS_CONTEXT = classPathResource("-testWithDuplicateNameInAlias.xml"); + private static final ClassPathResource REFTYPES_CONTEXT = classPathResource("-reftypes.xml"); + private static final ClassPathResource DEFAULT_LAZY_CONTEXT = classPathResource("-defaultLazyInit.xml"); + private static final ClassPathResource DEFAULT_AUTOWIRE_CONTEXT = classPathResource("-defaultAutowire.xml"); + private static final ClassPathResource BEAN_LIFECYCLE_NOTICE_CONTEXT = classPathResource("-beanLifecycleNotice.xml"); + private static ClassPathResource classPathResource(String suffix) { return new ClassPathResource(CLASSNAME + suffix, CLASS); } - @Test // SPR-2368 + @Test + // SPR-2368 void collectionsReferredToAsRefLocals() { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(factory).loadBeanDefinitions(COLLECTIONS_XSD_CONTEXT); @@ -417,8 +455,7 @@ void abstractParentBeans() { assertThat(tbs.containsKey("inheritedTestBeanSingleton")).isTrue(); // abstract bean should throw exception on creation attempt - assertThatExceptionOfType(BeanIsAbstractException.class).isThrownBy(() -> - parent.getBean("inheritedTestBeanWithoutClass")); + assertThatExceptionOfType(BeanIsAbstractException.class).isThrownBy(() -> parent.getBean("inheritedTestBeanWithoutClass")); // non-abstract bean should work, even if it serves as parent assertThat(parent.getBean("inheritedTestBeanPrototype") instanceof TestBean).isTrue(); @@ -468,10 +505,7 @@ void bogusParentageFromParentFactory() { new XmlBeanDefinitionReader(parent).loadBeanDefinitions(PARENT_CONTEXT); DefaultListableBeanFactory child = new DefaultListableBeanFactory(parent); new XmlBeanDefinitionReader(child).loadBeanDefinitions(CHILD_CONTEXT); - assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() -> - child.getBean("bogusParent", TestBean.class)) - .withMessageContaining("bogusParent") - .withCauseInstanceOf(NoSuchBeanDefinitionException.class); + assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() -> child.getBean("bogusParent", TestBean.class)).withMessageContaining("bogusParent").withCauseInstanceOf(NoSuchBeanDefinitionException.class); } /** @@ -539,12 +573,8 @@ void circularReferencesWithConstructor() { XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(xbf); reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_NONE); reader.loadBeanDefinitions(REFTYPES_CONTEXT); - assertThatExceptionOfType(BeanCreationException.class) - .isThrownBy(() -> xbf.getBean("jenny_constructor")) - .matches(ex -> ex.contains(BeanCurrentlyInCreationException.class)); - assertThatExceptionOfType(BeanCreationException.class) - .isThrownBy(() -> xbf.getBean("david_constructor")) - .matches(ex -> ex.contains(BeanCurrentlyInCreationException.class)); + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> xbf.getBean("jenny_constructor")).matches(ex -> ex.contains(BeanCurrentlyInCreationException.class)); + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> xbf.getBean("david_constructor")).matches(ex -> ex.contains(BeanCurrentlyInCreationException.class)); } @Test @@ -553,12 +583,8 @@ void circularReferencesWithPrototype() { XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(xbf); reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_NONE); reader.loadBeanDefinitions(REFTYPES_CONTEXT); - assertThatExceptionOfType(BeanCreationException.class) - .isThrownBy(() -> xbf.getBean("jenny_prototype")) - .matches(ex -> ex.contains(BeanCurrentlyInCreationException.class)); - assertThatExceptionOfType(BeanCreationException.class) - .isThrownBy(() -> xbf.getBean("david_prototype")) - .matches(ex -> ex.contains(BeanCurrentlyInCreationException.class)); + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> xbf.getBean("jenny_prototype")).matches(ex -> ex.contains(BeanCurrentlyInCreationException.class)); + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> xbf.getBean("david_prototype")).matches(ex -> ex.contains(BeanCurrentlyInCreationException.class)); } @Test @@ -567,10 +593,8 @@ void circularReferencesWithDependOn() { XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(xbf); reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_NONE); reader.loadBeanDefinitions(REFTYPES_CONTEXT); - assertThatExceptionOfType(BeanCreationException.class) - .isThrownBy(() -> xbf.getBean("jenny_depends_on")); - assertThatExceptionOfType(BeanCreationException.class) - .isThrownBy(() -> xbf.getBean("david_depends_on")); + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> xbf.getBean("jenny_depends_on")); + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> xbf.getBean("david_depends_on")); } @Test @@ -603,9 +627,7 @@ void circularReferencesWithNotAllowed() { XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(xbf); reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_NONE); reader.loadBeanDefinitions(REFTYPES_CONTEXT); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> - xbf.getBean("jenny")) - .matches(ex -> ex.contains(BeanCurrentlyInCreationException.class)); + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> xbf.getBean("jenny")).matches(ex -> ex.contains(BeanCurrentlyInCreationException.class)); } @Test @@ -615,9 +637,7 @@ void circularReferencesWithWrapping() { reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_NONE); reader.loadBeanDefinitions(REFTYPES_CONTEXT); xbf.addBeanPostProcessor(new WrappingPostProcessor()); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> - xbf.getBean("jenny")) - .matches(ex -> ex.contains(BeanCurrentlyInCreationException.class)); + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> xbf.getBean("jenny")).matches(ex -> ex.contains(BeanCurrentlyInCreationException.class)); } @Test @@ -673,8 +693,7 @@ void complexFactoryReferenceCircle() { void noSuchFactoryBeanMethod() { DefaultListableBeanFactory xbf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(NO_SUCH_FACTORY_METHOD_CONTEXT); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> - xbf.getBean("defaultTestBean")); + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> xbf.getBean("defaultTestBean")); } @Test @@ -693,24 +712,17 @@ void initMethodIsInvoked() { void initMethodThrowsException() { DefaultListableBeanFactory xbf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(INITIALIZERS_CONTEXT); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> - xbf.getBean("init-method2")) - .withCauseInstanceOf(IOException.class) - .satisfies(ex -> { - assertThat(ex.getResourceDescription()).contains("initializers.xml"); - assertThat(ex.getBeanName()).isEqualTo("init-method2"); - }); + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> xbf.getBean("init-method2")).withCauseInstanceOf(IOException.class).satisfies(ex -> { + assertThat(ex.getResourceDescription()).contains("initializers.xml"); + assertThat(ex.getBeanName()).isEqualTo("init-method2"); + }); } @Test void noSuchInitMethod() { DefaultListableBeanFactory xbf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(INITIALIZERS_CONTEXT); - assertThatExceptionOfType(FatalBeanException.class).isThrownBy(() -> - xbf.getBean("init-method3")) - .withMessageContaining("initializers.xml") - .withMessageContaining("init-method3") - .withMessageContaining("init"); + assertThatExceptionOfType(FatalBeanException.class).isThrownBy(() -> xbf.getBean("init-method3")).withMessageContaining("initializers.xml").withMessageContaining("init-method3").withMessageContaining("init"); } /** @@ -774,15 +786,13 @@ void defaultLazyInit() { @Test void noSuchXmlFile() { DefaultListableBeanFactory xbf = new DefaultListableBeanFactory(); - assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() -> - new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(MISSING_CONTEXT)); + assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() -> new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(MISSING_CONTEXT)); } @Test void invalidXmlFile() { DefaultListableBeanFactory xbf = new DefaultListableBeanFactory(); - assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() -> - new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(INVALID_CONTEXT)); + assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() -> new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(INVALID_CONTEXT)); } @Test @@ -839,8 +849,7 @@ private void doTestAutowire(DefaultListableBeanFactory xbf) { assertThat(rod3a.getSpouse2()).isEqualTo(kerry); assertThat(rod3a.getOther()).isEqualTo(other); - assertThatExceptionOfType(FatalBeanException.class).isThrownBy(() -> - xbf.getBean("rod4", ConstructorDependenciesBean.class)); + assertThatExceptionOfType(FatalBeanException.class).isThrownBy(() -> xbf.getBean("rod4", ConstructorDependenciesBean.class)); DependenciesBean rod5 = (DependenciesBean) xbf.getBean("rod5"); // Should not have been autowired @@ -1027,16 +1036,14 @@ void constructorArgWithSingleMatch() { void throwsExceptionOnTooManyArguments() { DefaultListableBeanFactory xbf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(CONSTRUCTOR_ARG_CONTEXT); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> - xbf.getBean("rod7", ConstructorDependenciesBean.class)); + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> xbf.getBean("rod7", ConstructorDependenciesBean.class)); } @Test void throwsExceptionOnAmbiguousResolution() { DefaultListableBeanFactory xbf = new DefaultListableBeanFactory(); new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(CONSTRUCTOR_ARG_CONTEXT); - assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() -> - xbf.getBean("rod8", ConstructorDependenciesBean.class)); + assertThatExceptionOfType(UnsatisfiedDependencyException.class).isThrownBy(() -> xbf.getBean("rod8", ConstructorDependenciesBean.class)); } @Test @@ -1122,10 +1129,7 @@ void classNotFoundWithDefaultBeanClassLoader() { new XmlBeanDefinitionReader(factory).loadBeanDefinitions(CLASS_NOT_FOUND_CONTEXT); // cool, no errors, so the rubbish class name in the bean def was not resolved // let's resolve the bean definition; must blow up - assertThatExceptionOfType(CannotLoadBeanClassException.class).isThrownBy(() -> - factory.getBean("classNotFound")) - .withCauseInstanceOf(ClassNotFoundException.class) - .satisfies(ex -> assertThat(ex.getResourceDescription()).contains("classNotFound.xml")); + assertThatExceptionOfType(CannotLoadBeanClassException.class).isThrownBy(() -> factory.getBean("classNotFound")).withCauseInstanceOf(ClassNotFoundException.class).satisfies(ex -> assertThat(ex.getResourceDescription()).contains("classNotFound.xml")); } @Test @@ -1196,8 +1200,7 @@ void fileSystemResourceWithImport() throws URISyntaxException { @Test void recursiveImport() { DefaultListableBeanFactory xbf = new DefaultListableBeanFactory(); - assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() -> - new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(RECURSIVE_IMPORT_CONTEXT)); + assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() -> new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(RECURSIVE_IMPORT_CONTEXT)); } /** @@ -1260,8 +1263,7 @@ void lookupOverrideMethodsWithSetterInjection() { assertThat(tb.getName()).isEqualTo("Jenny"); } - private void lookupOverrideMethodsWithSetterInjection(BeanFactory xbf, - String beanName, boolean singleton) { + private void lookupOverrideMethodsWithSetterInjection(BeanFactory xbf, String beanName, boolean singleton) { OverrideOneMethod oom = (OverrideOneMethod) xbf.getBean(beanName); if (singleton) { @@ -1368,9 +1370,7 @@ void rejectsOverrideOfBogusMethodName() { DefaultListableBeanFactory xbf = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(xbf); reader.loadBeanDefinitions(INVALID_NO_SUCH_METHOD_CONTEXT); - assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() -> - xbf.getBean("constructorOverrides")) - .withMessageContaining("bogusMethod"); + assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() -> xbf.getBean("constructorOverrides")).withMessageContaining("bogusMethod"); } @Test @@ -1472,9 +1472,7 @@ void nonLenientDependencyMatching() { new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(CONSTRUCTOR_ARG_CONTEXT); AbstractBeanDefinition bd = (AbstractBeanDefinition) xbf.getBeanDefinition("lenientDependencyTestBean"); bd.setLenientConstructorResolution(false); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> - xbf.getBean("lenientDependencyTestBean")) - .satisfies(ex -> assertThat(ex.getMostSpecificCause().getMessage()).contains("Ambiguous")); + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> xbf.getBean("lenientDependencyTestBean")).satisfies(ex -> assertThat(ex.getMostSpecificCause().getMessage()).contains("Ambiguous")); } @Test @@ -1483,9 +1481,7 @@ void nonLenientDependencyMatchingFactoryMethod() { new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(CONSTRUCTOR_ARG_CONTEXT); AbstractBeanDefinition bd = (AbstractBeanDefinition) xbf.getBeanDefinition("lenientDependencyTestBeanFactoryMethod"); bd.setLenientConstructorResolution(false); - assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> - xbf.getBean("lenientDependencyTestBeanFactoryMethod")) - .satisfies(ex -> assertThat(ex.getMostSpecificCause().getMessage()).contains("Ambiguous")); + assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() -> xbf.getBean("lenientDependencyTestBeanFactoryMethod")).satisfies(ex -> assertThat(ex.getMostSpecificCause().getMessage()).contains("Ambiguous")); } @Test @@ -1561,17 +1557,13 @@ void constructorWithUnresolvableParameterName() { @Test void withDuplicateName() { DefaultListableBeanFactory xbf = new DefaultListableBeanFactory(); - assertThatExceptionOfType(BeansException.class).isThrownBy(() -> - new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(TEST_WITH_DUP_NAMES_CONTEXT)) - .withMessageContaining("Bean name 'foo'"); + assertThatExceptionOfType(BeansException.class).isThrownBy(() -> new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(TEST_WITH_DUP_NAMES_CONTEXT)).withMessageContaining("Bean name 'foo'"); } @Test void withDuplicateNameInAlias() { DefaultListableBeanFactory xbf = new DefaultListableBeanFactory(); - assertThatExceptionOfType(BeansException.class).isThrownBy(() -> - new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(TEST_WITH_DUP_NAME_IN_ALIAS_CONTEXT)) - .withMessageContaining("Bean name 'foo'"); + assertThatExceptionOfType(BeansException.class).isThrownBy(() -> new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(TEST_WITH_DUP_NAME_IN_ALIAS_CONTEXT)).withMessageContaining("Bean name 'foo'"); } @Test @@ -1594,6 +1586,157 @@ void overrideMethodByArgTypeElement() { assertThat(oom.replaceMe("abc")).as("should replace").isEqualTo("cba"); } + + @Test + void beanPostProcessorSupport() { + DefaultListableBeanFactory xbf = new DefaultListableBeanFactory(); + XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(xbf); + reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_NONE); + reader.loadBeanDefinitions(new EncodedResource(BEAN_LIFECYCLE_NOTICE_CONTEXT, "ISO-8859-1")); + + xbf.addBeanPostProcessor(new CustomerBeanPostProcess()); + xbf.addBeanLifecycleNotice(new CustomerBeanLifecycleNotice()); + + BeanLifecycleNoticeObj1 beanLifecycleNoticeObj1 = xbf.getBean("beanLifecycleNoticeObj1", BeanLifecycleNoticeObj1.class); + + assertThat(beanLifecycleNoticeObj1.handlerBefore).isTrue(); + assertThat(beanLifecycleNoticeObj1.handlerAfter).isTrue(); + assertThat(beanLifecycleNoticeObj1.created).isTrue(); + + + BeanLifecycleNoticeObj2 beanLifecycleNoticeObj2 = xbf.getBean("beanLifecycleNoticeObj2", BeanLifecycleNoticeObj2.class); + assertThat(beanLifecycleNoticeObj2.handlerBefore).isFalse(); + assertThat(beanLifecycleNoticeObj2.handlerAfter).isFalse(); + + + xbf.destroyBean(beanLifecycleNoticeObj1); + + + System.out.println(); + + } + + public static class CustomerBeanLifecycleNotice implements BeanLifecycleNotice { + @Override + public void notice(BeanLifecycleEnum lifecycleEnum, BeanLifecycleEvent event) { + switch (lifecycleEnum) { + case created: + handlerCreated(event); + break; + + case destroyed: + handlerDestroyed(event); + break; + + } + } + + public void handlerCreated(BeanLifecycleEvent event) { + Object bean = event.getBean(); + + if (bean instanceof BeanLifecycleNoticeObj1) { + ((BeanLifecycleNoticeObj1) bean).setCreated(true); + } + } + + public void handlerDestroyed(BeanLifecycleEvent event) { + Object bean = event.getBean(); + if (bean instanceof BeanLifecycleNoticeObj1) { + assertThat(((BeanLifecycleNoticeObj1) bean).isDestroyed()).isFalse(); + } + } + } + + + static class BeanLifecycleNoticeObj1 { + private boolean handlerBefore; + + private boolean handlerAfter; + + private boolean created; + + private boolean destroyed; + + public boolean isDestroyed() { + return destroyed; + } + + public void setDestroyed(boolean destroyed) { + this.destroyed = destroyed; + } + + public boolean isCreated() { + return created; + } + + public void setCreated(boolean created) { + this.created = created; + } + + public boolean isHandlerBefore() { + return handlerBefore; + } + + public void setHandlerBefore(boolean handlerBefore) { + this.handlerBefore = handlerBefore; + } + + public boolean isHandlerAfter() { + return handlerAfter; + } + + public void setHandlerAfter(boolean handlerAfter) { + this.handlerAfter = handlerAfter; + } + } + + static class BeanLifecycleNoticeObj2 { + private boolean handlerBefore; + + private boolean handlerAfter; + + public boolean isHandlerBefore() { + return handlerBefore; + } + + public void setHandlerBefore(boolean handlerBefore) { + this.handlerBefore = handlerBefore; + } + + public boolean isHandlerAfter() { + return handlerAfter; + } + + public void setHandlerAfter(boolean handlerAfter) { + this.handlerAfter = handlerAfter; + } + } + + + static class CustomerBeanPostProcess implements BeanPostProcessor { + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof BeanLifecycleNoticeObj1) { + ((BeanLifecycleNoticeObj1) bean).setHandlerBefore(true); + } + return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName); + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof BeanLifecycleNoticeObj1) { + ((BeanLifecycleNoticeObj1) bean).setHandlerAfter(true); + } + return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName); + } + + @Override + public boolean support(Object bean, String beanName) { + return bean instanceof BeanLifecycleNoticeObj1; + } + } + + static class DoSomethingReplacer implements MethodReplacer { public Object lastArg; @@ -1770,10 +1913,10 @@ static class HoldingBean implements DisposableBean { public static int destroyCount = 0; - private DependingBean dependingBean; - public boolean destroyed = false; + private DependingBean dependingBean; + public void setDependingBean(DependingBean dependingBean) { this.dependingBean = dependingBean; } @@ -1791,8 +1934,9 @@ public void destroy() { static class DoubleBooleanConstructorBean { - private Boolean boolean1; - private Boolean boolean2; + private final Boolean boolean1; + + private final Boolean boolean2; public DoubleBooleanConstructorBean(Boolean b1, Boolean b2) { this.boolean1 = b1; diff --git a/spring-context/src/test/resources/org/springframework/beans/factory/xml/XmlBeanFactoryTests-beanLifecycleNotice.xml b/spring-context/src/test/resources/org/springframework/beans/factory/xml/XmlBeanFactoryTests-beanLifecycleNotice.xml new file mode 100644 index 000000000000..b86adea481c7 --- /dev/null +++ b/spring-context/src/test/resources/org/springframework/beans/factory/xml/XmlBeanFactoryTests-beanLifecycleNotice.xml @@ -0,0 +1,11 @@ + + + + + + + + +