All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor Maven / Gradle / Ivy

There is a newer version: 3.3.0-beta.3
Show newest version
/*
 * 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.annotation;


import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.Assert;
import org.apache.dubbo.common.utils.ClassUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.spring.Constants;
import org.apache.dubbo.config.spring.ReferenceBean;
import org.apache.dubbo.config.spring.context.event.DubboConfigInitEvent;
import org.apache.dubbo.config.spring.reference.ReferenceAttributes;
import org.apache.dubbo.config.spring.reference.ReferenceBeanManager;
import org.apache.dubbo.config.spring.reference.ReferenceBeanSupport;
import org.apache.dubbo.config.spring.util.SpringCompatUtils;
import org.apache.dubbo.rpc.service.GenericService;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.InjectionMetadata;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
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.AbstractBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.MethodMetadata;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Member;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static com.alibaba.spring.util.AnnotationUtils.getAttribute;
import static org.apache.dubbo.common.constants.LoggerCodeConstants.CONFIG_DUBBO_BEAN_INITIALIZER;
import static org.apache.dubbo.common.utils.AnnotationUtils.filterDefaultValues;
import static org.springframework.util.StringUtils.hasText;

/**
 * 

* Step 1: * The purpose of implementing {@link BeanFactoryPostProcessor} is to scan the registration reference bean definition earlier, * so that it can be shared with the xml bean configuration. *

* *

* Step 2: * By implementing {@link org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor}, * inject the reference bean instance into the fields and setter methods which annotated with {@link DubboReference}. *

* * @see DubboReference * @see Reference * @see com.alibaba.dubbo.config.annotation.Reference * @since 2.5.7 */ public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements ApplicationContextAware, BeanFactoryPostProcessor { /** * The bean name of {@link ReferenceAnnotationBeanPostProcessor} */ public static final String BEAN_NAME = "referenceAnnotationBeanPostProcessor"; /** * Cache size */ private static final int CACHE_SIZE = Integer.getInteger(BEAN_NAME + ".cache.size", 32); private final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(getClass()); private final ConcurrentMap injectedFieldReferenceBeanCache = new ConcurrentHashMap<>(CACHE_SIZE); private final ConcurrentMap injectedMethodReferenceBeanCache = new ConcurrentHashMap<>(CACHE_SIZE); private ApplicationContext applicationContext; private ReferenceBeanManager referenceBeanManager; private BeanDefinitionRegistry beanDefinitionRegistry; /** * {@link com.alibaba.dubbo.config.annotation.Reference @com.alibaba.dubbo.config.annotation.Reference} has been supported since 2.7.3 *

* {@link DubboReference @DubboReference} has been supported since 2.7.7 */ public ReferenceAnnotationBeanPostProcessor() { super(DubboReference.class, Reference.class, com.alibaba.dubbo.config.annotation.Reference.class); } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { String[] beanNames = beanFactory.getBeanDefinitionNames(); for (String beanName : beanNames) { Class beanType; if (beanFactory.isFactoryBean(beanName)) { BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); if (isReferenceBean(beanDefinition)) { continue; } if (isAnnotatedReferenceBean(beanDefinition)) { // process @DubboReference at java-config @bean method processReferenceAnnotatedBeanDefinition(beanName, (AnnotatedBeanDefinition) beanDefinition); continue; } String beanClassName = beanDefinition.getBeanClassName(); beanType = ClassUtils.resolveClass(beanClassName, getClassLoader()); } else { beanType = beanFactory.getType(beanName); } if (beanType != null) { AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null); try { prepareInjection(metadata); } catch (BeansException e) { throw e; } catch (Exception e) { throw new IllegalStateException("Prepare dubbo reference injection element failed", e); } } } if (beanFactory instanceof AbstractBeanFactory) { List beanPostProcessors = ((AbstractBeanFactory) beanFactory).getBeanPostProcessors(); for (BeanPostProcessor beanPostProcessor : beanPostProcessors) { if (beanPostProcessor == this) { // This bean has been registered as BeanPostProcessor at org.apache.dubbo.config.spring.context.DubboInfraBeanRegisterPostProcessor.postProcessBeanFactory() // so destroy this bean here, prevent register it as BeanPostProcessor again, avoid cause BeanPostProcessorChecker detection error beanDefinitionRegistry.removeBeanDefinition(BEAN_NAME); break; } } } try { // this is an early event, it will be notified at org.springframework.context.support.AbstractApplicationContext.registerListeners() applicationContext.publishEvent(new DubboConfigInitEvent(applicationContext)); } catch (Exception e) { // if spring version is less than 4.2, it does not support early application event logger.warn(CONFIG_DUBBO_BEAN_INITIALIZER, "", "", "publish early application event failed, please upgrade spring version to 4.2.x or later: " + e); } } /** * check whether is @DubboReference at java-config @bean method */ private boolean isAnnotatedReferenceBean(BeanDefinition beanDefinition) { if (beanDefinition instanceof AnnotatedBeanDefinition) { AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition; String beanClassName = SpringCompatUtils.getFactoryMethodReturnType(annotatedBeanDefinition); if (beanClassName != null && ReferenceBean.class.getName().equals(beanClassName)) { return true; } } return false; } /** * process @DubboReference at java-config @bean method *

     * @Configuration
     * public class ConsumerConfig {
     *
     *      @Bean
     *      @DubboReference(group="demo", version="1.2.3")
     *      public ReferenceBean<DemoService> demoService() {
     *          return new ReferenceBean();
     *      }
     *
     * }
     * 
* * @param beanName * @param beanDefinition */ private void processReferenceAnnotatedBeanDefinition(String beanName, AnnotatedBeanDefinition beanDefinition) { MethodMetadata factoryMethodMetadata = SpringCompatUtils.getFactoryMethodMetadata(beanDefinition); // Extract beanClass from generic return type of java-config bean method: ReferenceBean // see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBeanFromMethod Class beanClass = getBeanFactory().getType(beanName); if (beanClass == Object.class) { beanClass = SpringCompatUtils.getGenericTypeOfReturnType(factoryMethodMetadata); } if (beanClass == Object.class) { // bean class is invalid, ignore it return; } if (beanClass == null) { String beanMethodSignature = factoryMethodMetadata.getDeclaringClassName() + "#" + factoryMethodMetadata.getMethodName() + "()"; throw new BeanCreationException("The ReferenceBean is missing necessary generic type, which returned by the @Bean method of Java-config class. " + "The generic type of the returned ReferenceBean must be specified as the referenced interface type, " + "such as ReferenceBean. Please check bean method: " + beanMethodSignature); } // get dubbo reference annotation attributes Map annotationAttributes = null; // try all dubbo reference annotation types for (Class annotationType : getAnnotationTypes()) { if (factoryMethodMetadata.isAnnotated(annotationType.getName())) { // Since Spring 5.2 // return factoryMethodMetadata.getAnnotations().get(annotationType).filterDefaultValues().asMap(); // Compatible with Spring 4.x annotationAttributes = factoryMethodMetadata.getAnnotationAttributes(annotationType.getName()); annotationAttributes = filterDefaultValues(annotationType, annotationAttributes); break; } } if (annotationAttributes != null) { // @DubboReference on @Bean method LinkedHashMap attributes = new LinkedHashMap<>(annotationAttributes); // reset id attribute attributes.put(ReferenceAttributes.ID, beanName); // convert annotation props ReferenceBeanSupport.convertReferenceProps(attributes, beanClass); // get interface String interfaceName = (String) attributes.get(ReferenceAttributes.INTERFACE); // check beanClass and reference interface class if (!StringUtils.isEquals(interfaceName, beanClass.getName()) && beanClass != GenericService.class) { String beanMethodSignature = factoryMethodMetadata.getDeclaringClassName() + "#" + factoryMethodMetadata.getMethodName() + "()"; throw new BeanCreationException("The 'interfaceClass' or 'interfaceName' attribute value of @DubboReference annotation " + "is inconsistent with the generic type of the ReferenceBean returned by the bean method. " + "The interface class of @DubboReference is: " + interfaceName + ", but return ReferenceBean<" + beanClass.getName() + ">. " + "Please remove the 'interfaceClass' and 'interfaceName' attributes from @DubboReference annotation. " + "Please check bean method: " + beanMethodSignature); } Class interfaceClass = beanClass; // set attribute instead of property values beanDefinition.setAttribute(Constants.REFERENCE_PROPS, attributes); beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass); beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, interfaceName); } else { // raw reference bean // the ReferenceBean is not yet initialized beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, beanClass); if (beanClass != GenericService.class) { beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, beanClass.getName()); } } // set id beanDefinition.getPropertyValues().add(ReferenceAttributes.ID, beanName); } @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) { if (beanType != null) { if (isReferenceBean(beanDefinition)) { //mark property value as optional List propertyValues = beanDefinition.getPropertyValues().getPropertyValueList(); for (PropertyValue propertyValue : propertyValues) { propertyValue.setOptional(true); } } else if (isAnnotatedReferenceBean(beanDefinition)) { // extract beanClass from java-config bean method generic return type: ReferenceBean //Class beanClass = getBeanFactory().getType(beanName); } else { AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null); metadata.checkConfigMembers(beanDefinition); try { prepareInjection(metadata); } catch (Exception e) { throw new IllegalStateException("Prepare dubbo reference injection element failed", e); } } } } /** * Alternatives to the {@link #postProcessProperties(PropertyValues, Object, String)}, that removed as of Spring * Framework 6.0.0, and in favor of {@link #postProcessProperties(PropertyValues, Object, String)}. *

In order to be compatible with the lower version of Spring, it is still retained. * @see #postProcessProperties */ public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { return postProcessProperties(pvs, bean, beanName); } /** * Alternatives to the {@link #postProcessPropertyValues(PropertyValues, PropertyDescriptor[], Object, String)}. * @see #postProcessPropertyValues */ @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { try { AnnotatedInjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs); prepareInjection(metadata); metadata.inject(bean, beanName, pvs); } catch (BeansException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName() + " dependencies is failed", ex); } return pvs; } private boolean isReferenceBean(BeanDefinition beanDefinition) { return ReferenceBean.class.getName().equals(beanDefinition.getBeanClassName()); } protected void prepareInjection(AnnotatedInjectionMetadata metadata) throws BeansException { try { //find and register bean definition for @DubboReference/@Reference for (AnnotatedFieldElement fieldElement : metadata.getFieldElements()) { if (fieldElement.injectedObject != null) { continue; } Class injectedType = fieldElement.field.getType(); AnnotationAttributes attributes = fieldElement.attributes; String referenceBeanName = registerReferenceBean(fieldElement.getPropertyName(), injectedType, attributes, fieldElement.field); //associate fieldElement and reference bean fieldElement.injectedObject = referenceBeanName; injectedFieldReferenceBeanCache.put(fieldElement, referenceBeanName); } for (AnnotatedMethodElement methodElement : metadata.getMethodElements()) { if (methodElement.injectedObject != null) { continue; } Class injectedType = methodElement.getInjectedType(); AnnotationAttributes attributes = methodElement.attributes; String referenceBeanName = registerReferenceBean(methodElement.getPropertyName(), injectedType, attributes, methodElement.method); //associate methodElement and reference bean methodElement.injectedObject = referenceBeanName; injectedMethodReferenceBeanCache.put(methodElement, referenceBeanName); } } catch (ClassNotFoundException e) { throw new BeanCreationException("prepare reference annotation failed", e); } } public String registerReferenceBean(String propertyName, Class injectedType, Map attributes, Member member) throws BeansException { boolean renameable = true; // referenceBeanName String referenceBeanName = getAttribute(attributes, ReferenceAttributes.ID); if (hasText(referenceBeanName)) { renameable = false; } else { referenceBeanName = propertyName; } String checkLocation = "Please check " + member.toString(); // convert annotation props ReferenceBeanSupport.convertReferenceProps(attributes, injectedType); // get interface String interfaceName = (String) attributes.get(ReferenceAttributes.INTERFACE); if (StringUtils.isBlank(interfaceName)) { throw new BeanCreationException("Need to specify the 'interfaceName' or 'interfaceClass' attribute of '@DubboReference' if enable generic. " + checkLocation); } // check reference key String referenceKey = ReferenceBeanSupport.generateReferenceKey(attributes, applicationContext); // find reference bean name by reference key List registeredReferenceBeanNames = referenceBeanManager.getBeanNamesByKey(referenceKey); if (registeredReferenceBeanNames.size() > 0) { // found same name and reference key if (registeredReferenceBeanNames.contains(referenceBeanName)) { return referenceBeanName; } } //check bean definition if (beanDefinitionRegistry.containsBeanDefinition(referenceBeanName)) { BeanDefinition prevBeanDefinition = beanDefinitionRegistry.getBeanDefinition(referenceBeanName); String prevBeanType = prevBeanDefinition.getBeanClassName(); String prevBeanDesc = referenceBeanName + "[" + prevBeanType + "]"; String newBeanDesc = referenceBeanName + "[" + referenceKey + "]"; if (isReferenceBean(prevBeanDefinition)) { //check reference key String prevReferenceKey = ReferenceBeanSupport.generateReferenceKey(prevBeanDefinition, applicationContext); if (StringUtils.isEquals(prevReferenceKey, referenceKey)) { //found matched dubbo reference bean, ignore register return referenceBeanName; } //get interfaceName from attribute Assert.notNull(prevBeanDefinition, "The interface class of ReferenceBean is not initialized"); prevBeanDesc = referenceBeanName + "[" + prevReferenceKey + "]"; } // bean name from attribute 'id' or java-config bean, cannot be renamed if (!renameable) { throw new BeanCreationException("Already exists another bean definition with the same bean name [" + referenceBeanName + "], " + "but cannot rename the reference bean name (specify the id attribute or java-config bean), " + "please modify the name of one of the beans: " + "prev: " + prevBeanDesc + ", new: " + newBeanDesc + ". " + checkLocation); } // the prev bean type is different, rename the new reference bean int index = 2; String newReferenceBeanName = null; while (newReferenceBeanName == null || beanDefinitionRegistry.containsBeanDefinition(newReferenceBeanName) || beanDefinitionRegistry.isAlias(newReferenceBeanName)) { newReferenceBeanName = referenceBeanName + "#" + index; index++; // double check found same name and reference key if (registeredReferenceBeanNames.contains(newReferenceBeanName)) { return newReferenceBeanName; } } newBeanDesc = newReferenceBeanName + "[" + referenceKey + "]"; logger.warn(CONFIG_DUBBO_BEAN_INITIALIZER, "", "", "Already exists another bean definition with the same bean name [" + referenceBeanName + "], " + "rename dubbo reference bean to [" + newReferenceBeanName + "]. " + "It is recommended to modify the name of one of the beans to avoid injection problems. " + "prev: " + prevBeanDesc + ", new: " + newBeanDesc + ". " + checkLocation); referenceBeanName = newReferenceBeanName; } attributes.put(ReferenceAttributes.ID, referenceBeanName); // If registered matched reference before, just register alias if (registeredReferenceBeanNames.size() > 0) { beanDefinitionRegistry.registerAlias(registeredReferenceBeanNames.get(0), referenceBeanName); referenceBeanManager.registerReferenceKeyAndBeanName(referenceKey, referenceBeanName); return referenceBeanName; } Class interfaceClass = injectedType; // TODO Only register one reference bean for same (group, interface, version) // Register the reference bean definition to the beanFactory RootBeanDefinition beanDefinition = new RootBeanDefinition(); beanDefinition.setBeanClassName(ReferenceBean.class.getName()); beanDefinition.getPropertyValues().add(ReferenceAttributes.ID, referenceBeanName); // set attribute instead of property values beanDefinition.setAttribute(Constants.REFERENCE_PROPS, attributes); beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_CLASS, interfaceClass); beanDefinition.setAttribute(ReferenceAttributes.INTERFACE_NAME, interfaceName); // create decorated definition for reference bean, Avoid being instantiated when getting the beanType of ReferenceBean // see org.springframework.beans.factory.support.AbstractBeanFactory#getTypeForFactoryBean() GenericBeanDefinition targetDefinition = new GenericBeanDefinition(); targetDefinition.setBeanClass(interfaceClass); beanDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, referenceBeanName + "_decorated")); // signal object type since Spring 5.2 beanDefinition.setAttribute(Constants.OBJECT_TYPE_ATTRIBUTE, interfaceClass); beanDefinitionRegistry.registerBeanDefinition(referenceBeanName, beanDefinition); referenceBeanManager.registerReferenceKeyAndBeanName(referenceKey, referenceBeanName); logger.info("Register dubbo reference bean: " + referenceBeanName + " = " + referenceKey + " at " + member); return referenceBeanName; } @Override protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class injectedType, AnnotatedInjectElement injectedElement) throws Exception { if (injectedElement.injectedObject == null) { throw new IllegalStateException("The AnnotatedInjectElement of @DubboReference should be inited before injection"); } return getBeanFactory().getBean((String) injectedElement.injectedObject); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; this.referenceBeanManager = applicationContext.getBean(ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class); this.beanDefinitionRegistry = (BeanDefinitionRegistry) applicationContext.getAutowireCapableBeanFactory(); } @Override public void destroy() throws Exception { super.destroy(); this.injectedFieldReferenceBeanCache.clear(); this.injectedMethodReferenceBeanCache.clear(); } /** * Gets all beans of {@link ReferenceBean} * * @deprecated use {@link ReferenceBeanManager#getReferences()} instead */ @Deprecated public Collection> getReferenceBeans() { return Collections.emptyList(); } /** * Get {@link ReferenceBean} {@link Map} in injected field. * * @return non-null {@link Map} * @since 2.5.11 */ public Map> getInjectedFieldReferenceBeanMap() { Map> map = new HashMap<>(); for (Map.Entry entry : injectedFieldReferenceBeanCache.entrySet()) { map.put(entry.getKey(), referenceBeanManager.getById(entry.getValue())); } return Collections.unmodifiableMap(map); } /** * Get {@link ReferenceBean} {@link Map} in injected method. * * @return non-null {@link Map} * @since 2.5.11 */ public Map> getInjectedMethodReferenceBeanMap() { Map> map = new HashMap<>(); for (Map.Entry entry : injectedMethodReferenceBeanCache.entrySet()) { map.put(entry.getKey(), referenceBeanManager.getById(entry.getValue())); } return Collections.unmodifiableMap(map); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy