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

org.apache.dubbo.config.spring.ReferenceBean 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;

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.ReferenceConfig;
import org.apache.dubbo.config.spring.context.DubboConfigBeanInitializer;
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.schema.DubboBeanDefinitionParser;
import org.apache.dubbo.config.support.Parameter;
import org.apache.dubbo.rpc.proxy.AbstractProxyFactory;

import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.target.AbstractLazyCreationTargetSource;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;


/**
 * 

* Spring FactoryBean for {@link ReferenceConfig}. *

* * *

* Step 1: Register ReferenceBean in Java-config class: *
 * @Configuration
 * public class ReferenceConfiguration {
 *     @Bean
 *     @DubboReference(group = "demo")
 *     public ReferenceBean<HelloService> helloService() {
 *         return new ReferenceBean();
 *     }
 *
 *     // As GenericService
 *     @Bean
 *     @DubboReference(group = "demo", interfaceClass = HelloService.class)
 *     public ReferenceBean<GenericService> genericHelloService() {
 *         return new ReferenceBean();
 *     }
 * }
 * 
* * Or register ReferenceBean in xml: *
 * <dubbo:reference id="helloService" interface="org.apache.dubbo.config.spring.api.HelloService"/>
 * <!-- As GenericService -->
 * <dubbo:reference id="genericHelloService" interface="org.apache.dubbo.config.spring.api.HelloService" generic="true"/>
 * 
* * Step 2: Inject ReferenceBean by @Autowired *
 * public class FooController {
 *     @Autowired
 *     private HelloService helloService;
 *
 *     @Autowired
 *     private GenericService genericHelloService;
 * }
 * 
* * * @see org.apache.dubbo.config.annotation.DubboReference * @see org.apache.dubbo.config.spring.reference.ReferenceBeanBuilder */ public class ReferenceBean implements FactoryBean, ApplicationContextAware, BeanClassLoaderAware, BeanNameAware, InitializingBean, DisposableBean { private transient ApplicationContext applicationContext; private ClassLoader beanClassLoader; // lazy proxy of reference private Object lazyProxy; // beanName protected String id; // reference key private String key; /** * The interface class of the reference service */ private Class interfaceClass; /* * remote service interface class name */ // 'interfaceName' field for compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() private String interfaceName; //from annotation attributes private Map referenceProps; //from xml bean definition private MutablePropertyValues propertyValues; //actual reference config private ReferenceConfig referenceConfig; // Registration sources of this reference, may be xml file or annotation location private List> sources = new ArrayList<>(); public ReferenceBean() { super(); } public ReferenceBean(Map referenceProps) { this.referenceProps = referenceProps; } @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } @Override public void setBeanClassLoader(ClassLoader classLoader) { this.beanClassLoader = classLoader; } @Override public void setBeanName(String name) { this.setId(name); } /** * Create bean instance. * *

* Why we need a lazy proxy? * *

* When Spring searches beans by type, if Spring cannot determine the type of a factory bean, it may try to initialize it. * The ReferenceBean is also a FactoryBean. *
* (This has already been resolved by decorating the BeanDefinition: {@link DubboBeanDefinitionParser#configReferenceBean}) * *

* In addition, if some ReferenceBeans are dependent on beans that are initialized very early, * and dubbo config beans are not ready yet, there will be many unexpected problems if initializing the dubbo reference immediately. * *

* When it is initialized, only a lazy proxy object will be created, * and dubbo reference-related resources will not be initialized. *
* In this way, the influence of Spring is eliminated, and the dubbo configuration initialization is controllable. * * * @see DubboConfigBeanInitializer * @see ReferenceBeanManager#initReferenceBean(ReferenceBean) * @see DubboBeanDefinitionParser#configReferenceBean */ @Override public T getObject() { if (lazyProxy == null) { createLazyProxy(); } return (T) lazyProxy; } @Override public Class getObjectType() { return getInterfaceClass(); } @Override @Parameter(excluded = true) public boolean isSingleton() { return true; } @Override public void afterPropertiesSet() throws Exception { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); // pre init xml reference bean or @DubboReference annotation Assert.notEmptyString(getId(), "The id of ReferenceBean cannot be empty"); BeanDefinition beanDefinition = beanFactory.getBeanDefinition(getId()); this.interfaceClass = (Class) beanDefinition.getAttribute(ReferenceAttributes.INTERFACE_CLASS); this.interfaceName = (String) beanDefinition.getAttribute(ReferenceAttributes.INTERFACE_NAME); Assert.notNull(this.interfaceClass, "The interface class of ReferenceBean is not initialized"); if (beanDefinition.hasAttribute(Constants.REFERENCE_PROPS)) { // @DubboReference annotation at java-config class @Bean method // @DubboReference annotation at reference field or setter method referenceProps = (Map) beanDefinition.getAttribute(Constants.REFERENCE_PROPS); } else { if (beanDefinition instanceof AnnotatedBeanDefinition) { // Return ReferenceBean in java-config class @Bean method if (referenceProps == null) { referenceProps = new LinkedHashMap<>(); } ReferenceBeanSupport.convertReferenceProps(referenceProps, interfaceClass); if (this.interfaceName == null) { this.interfaceName = (String) referenceProps.get(ReferenceAttributes.INTERFACE); } } else { // xml reference bean propertyValues = beanDefinition.getPropertyValues(); } } Assert.notNull(this.interfaceName, "The interface name of ReferenceBean is not initialized"); ReferenceBeanManager referenceBeanManager = beanFactory.getBean(ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class); referenceBeanManager.addReference(this); } private ConfigurableListableBeanFactory getBeanFactory() { return (ConfigurableListableBeanFactory) applicationContext.getAutowireCapableBeanFactory(); } @Override public void destroy() { // do nothing } public String getId() { return id; } public void setId(String id) { this.id = id; } /** * The interface of this ReferenceBean, for injection purpose * @return */ public Class getInterfaceClass() { // Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() return interfaceClass; } /** * The interface of remote service */ public String getServiceInterface() { return interfaceName; } /** * The group of the service */ public String getGroup() { // Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() return referenceConfig.getGroup(); } /** * The version of the service */ public String getVersion() { // Compatible with seata-1.4.0: io.seata.rm.tcc.remoting.parser.DubboRemotingParser#getServiceDesc() return referenceConfig.getVersion(); } public String getKey() { return key; } public Map getReferenceProps() { return referenceProps; } public MutablePropertyValues getPropertyValues() { return propertyValues; } public ReferenceConfig getReferenceConfig() { return referenceConfig; } public void setKeyAndReferenceConfig(String key, ReferenceConfig referenceConfig) { this.key = key; this.referenceConfig = referenceConfig; } /** * Create lazy proxy for reference. */ private void createLazyProxy() { //set proxy interfaces //see also: org.apache.dubbo.rpc.proxy.AbstractProxyFactory.getProxy(org.apache.dubbo.rpc.Invoker, boolean) ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTargetSource(new DubboReferenceLazyInitTargetSource()); proxyFactory.addInterface(interfaceClass); Class[] internalInterfaces = AbstractProxyFactory.getInternalInterfaces(); for (Class anInterface : internalInterfaces) { proxyFactory.addInterface(anInterface); } if (!StringUtils.isEquals(interfaceClass.getName(), interfaceName)) { //add service interface try { Class serviceInterface = ClassUtils.forName(interfaceName, beanClassLoader); proxyFactory.addInterface(serviceInterface); } catch (ClassNotFoundException e) { // generic call maybe without service interface class locally } } this.lazyProxy = proxyFactory.getProxy(this.beanClassLoader); } private Object getCallProxy() throws Exception { if (referenceConfig == null) { throw new IllegalStateException("ReferenceBean is not ready yet, please make sure to call reference interface method after dubbo is started."); } //get reference proxy return referenceConfig.get(); } private class DubboReferenceLazyInitTargetSource extends AbstractLazyCreationTargetSource { @Override protected Object createObject() throws Exception { return getCallProxy(); } @Override public synchronized Class getTargetClass() { return getInterfaceClass(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy