Skip to content

Commit

Permalink
Fix DubboConfigEarlyInitializationPostProcessor registered twice in S…
Browse files Browse the repository at this point in the history
…pring Framework (#9397)

fixes #9370
  • Loading branch information
gitchenjh authored Dec 15, 2021
1 parent 7cb6bff commit 9cfb1ae
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 132 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.dubbo.config.spring.beans.factory.config;

import org.apache.dubbo.config.AbstractConfig;
import org.apache.dubbo.config.context.ConfigManager;

import com.alibaba.spring.beans.factory.config.GenericBeanPostProcessorAdapter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
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.DefaultListableBeanFactory;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.PriorityOrdered;

import javax.annotation.PostConstruct;

/**
* Generally, {@link AbstractConfig Dubbo Config} Bean will be added into {@link ConfigManager} on the bean initialization
* life cycle through {@link CommonAnnotationBeanPostProcessor} executing the callback of
* {@link PostConstruct @PostConstruct}. However, the instantiation and initialization of
* {@link AbstractConfig Dubbo Config} Bean could be too early before {@link CommonAnnotationBeanPostProcessor}, e.g,
* execution, thus it's required to register the {@link DubboConfigEarlyInitializationPostProcessor
* DubboConfigEarlyInitializationPostProcessor} instance as a {@link BeanPostProcessor} into
* {@link DefaultListableBeanFactory the BeanFatory} using {@link BeanDefinitionRegistryPostProcessor} as early as
* possible.
*
* @author <a href="mailto:842761733@qq.com">chenjh</a>
* @see DubboConfigEarlyInitializationPostProcessor
* @since 2.7.15
*/
public class DubboConfigEarlyRegistrationPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {

public static final String BEAN_NAME = "dubboConfigEarlyRegistrationPostProcessor";

private static final Log logger = LogFactory.getLog(DubboConfigEarlyRegistrationPostProcessor.class.getName());

private DefaultListableBeanFactory beanFactory;

private DubboConfigEarlyInitializationPostProcessor configEarlyInitializationPostProcessor =
new DubboConfigEarlyInitializationPostProcessor();

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
this.beanFactory = unwrap(registry);
registryConfigEarlyInitializationPostProcessor(beanFactory);
}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.beanFactory == null) { // try again if postProcessBeanDefinitionRegistry method does not effect.
this.beanFactory = unwrap(beanFactory);
registryConfigEarlyInitializationPostProcessor(this.beanFactory);
}
}

@Override
public int getOrder() {
return HIGHEST_PRECEDENCE;
}

/**
* Register DubboConfigEarlyInitializationPostProcessor as BeanPostProcessor manually
* before {@link AbstractApplicationContext#registerBeanPostProcessors(ConfigurableListableBeanFactory)
* Spring Framework automatically register}
*/
private void registryConfigEarlyInitializationPostProcessor(DefaultListableBeanFactory beanFactory) {
if (beanFactory != null) {
// Register DubboConfigEarlyInitializationPostProcessor
beanFactory.addBeanPostProcessor(configEarlyInitializationPostProcessor);
if (logger.isInfoEnabled()) {
logger.info("DubboConfigEarlyInitializationPostProcessor has bean registered");
}
}
}

private DefaultListableBeanFactory unwrap(Object registry) {
if (registry instanceof DefaultListableBeanFactory) {
return (DefaultListableBeanFactory) registry;
}
return null;
}

class DubboConfigEarlyInitializationPostProcessor extends GenericBeanPostProcessorAdapter<AbstractConfig> {

protected void processBeforeInitialization(AbstractConfig config, String beanName) throws BeansException {
if (beanFactory == null) {
if (logger.isErrorEnabled()) {
logger.error("Current Processor is not running in Spring container, next action will be skipped!");
}
return;
}

// If CommonAnnotationBeanPostProcessor is already registered, the method addIntoConfigManager()
// will be invoked in Bean life cycle.
if (!hasRegisteredCommonAnnotationBeanPostProcessor()) {
if (logger.isWarnEnabled()) {
logger.warn("CommonAnnotationBeanPostProcessor is not registered yet, " +
"the method addIntoConfigManager() will be invoked directly");
}
config.addIntoConfigManager();
}
}

/**
* {@link DefaultListableBeanFactory} has registered {@link CommonAnnotationBeanPostProcessor} or not?
*
* @return if registered, return <code>true</code>, or <code>false</code>
*/
private boolean hasRegisteredCommonAnnotationBeanPostProcessor() {
for (BeanPostProcessor beanPostProcessor : beanFactory.getBeanPostProcessors()) {
if (CommonAnnotationBeanPostProcessor.class.equals(beanPostProcessor.getClass())) {
return true;
}
}
return false;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@
import org.apache.dubbo.config.spring.beans.factory.annotation.DubboConfigAliasPostProcessor;
import org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor;
import org.apache.dubbo.config.spring.beans.factory.config.DubboConfigDefaultPropertyValueBeanPostProcessor;
import org.apache.dubbo.config.spring.beans.factory.config.DubboConfigEarlyInitializationPostProcessor;
import org.apache.dubbo.config.spring.beans.factory.config.DubboConfigEarlyRegistrationPostProcessor;
import org.apache.dubbo.config.spring.context.DubboApplicationListenerRegistrar;
import org.apache.dubbo.config.spring.context.DubboBootstrapApplicationListener;
import org.apache.dubbo.config.spring.context.DubboLifecycleComponentApplicationListener;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
Expand Down Expand Up @@ -89,9 +90,9 @@ public static void registerCommonBeans(BeanDefinitionRegistry registry) {
registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
DubboConfigDefaultPropertyValueBeanPostProcessor.class);

// Since 2.7.9 Register DubboConfigEarlyInitializationPostProcessor as an infrastructure Bean
registerInfrastructureBean(registry, DubboConfigEarlyInitializationPostProcessor.BEAN_NAME,
DubboConfigEarlyInitializationPostProcessor.class);
// Since 2.7.15 Register DubboConfigEarlyRegistrationPostProcessor as an infrastructure Bean
registerInfrastructureBean(registry, DubboConfigEarlyRegistrationPostProcessor.BEAN_NAME,
DubboConfigEarlyRegistrationPostProcessor.class);
}

/**
Expand Down Expand Up @@ -126,7 +127,7 @@ private static <T> T getOptionalBeanByType(ListableBeanFactory beanFactory, Clas
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, beanType, true, false);
if (beanNames == null || beanNames.length == 0) {
return null;
} else if (beanNames.length > 1){
} else if (beanNames.length > 1) {
throw new NoUniqueBeanDefinitionException(beanType, Arrays.asList(beanNames));
}
return (T) beanFactory.getBean(beanNames[0]);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.dubbo.config.spring.beans.factory.config;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = DubboConfigEarlyInitializationTest.class)
@ImportResource(locations = "classpath:/META-INF/spring/dubbo-config-early-initialization.xml")
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class DubboConfigEarlyInitializationTest {

@Autowired
private ApplicationContext applicationContext;

@Test
public void testDubboConfigEarlyInitializationPostProcessor() {
assertTrue(applicationContext instanceof GenericApplicationContext);
ConfigurableListableBeanFactory clBeanFactory = ((GenericApplicationContext) applicationContext).getBeanFactory();
assertTrue(clBeanFactory instanceof DefaultListableBeanFactory);
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) clBeanFactory;
List<BeanPostProcessor> beanPostProcessorList = beanFactory.getBeanPostProcessors();
assertEquals(beanFactory.getBeanPostProcessorCount(), beanPostProcessorList.size());
boolean containsDubboConfigEarlyInitializationPostProcessor = false;
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
if (beanPostProcessor instanceof DubboConfigEarlyRegistrationPostProcessor.DubboConfigEarlyInitializationPostProcessor) {
containsDubboConfigEarlyInitializationPostProcessor = true;
break;
}
}
assertTrue(containsDubboConfigEarlyInitializationPostProcessor);
}
}
Loading

0 comments on commit 9cfb1ae

Please sign in to comment.