Dubbo之——改造Dubbo,使其能够兼容Spring 4注解配置

简介: Dubbo之——改造Dubbo,使其能够兼容Spring 4注解配置

Dubbo之——改造Dubbo,使其能够兼容Spring 4注解配置

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/70040928

而随着Spring Boot的大热,Java-Base方式配置Spring也变得越来越流行。
Dubbo + Boot的开发模式,也是较为常见的组合方式。
但是,当使用Dubbo在高版本Spring环境中使用注解方式配置时,会因为一些代码版本的原因导致整合出现问题。

  1. Dubbo原生的注解配置
    Dubbo本身就是基于Spring的,而且原生就提供注解配置:

服务提供方配置:

服务提供方注解:
[java] view plain copy
import com.alibaba.dubbo.config.annotation.Service;

@Service(version=”1.0.0”)
public class FooServiceImpl implements FooService {
// ……
}
服务消费方注解:
[java] view plain copy
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Component;

@Component
public class BarAction {

@Reference(version="1.0.0")  
private FooService fooService;

}
服务消费方配置:
[html] view plain copy

通过官方的例子,就可以看出Dubbo使用xml配置 来开启注解配置,并提供 com.alibaba.dubbo.config.annotation.Service注解进行服务注册,提供com.alibaba.dubbo.config.annotation.Reference注解进行服务注入。
2.实现机制
可以看出,内部机制都是依托于标签。 通过源码分析,Dubbo对于Spring xml解析处理由com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler提供:
DubboNamespaceHandler.java
[java] view plain copy
package com.alibaba.dubbo.config.spring.schema;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

import com.alibaba.dubbo.common.Version;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ConsumerConfig;
import com.alibaba.dubbo.config.ModuleConfig;
import com.alibaba.dubbo.config.MonitorConfig;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.ProviderConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.spring.AnnotationBean;
import com.alibaba.dubbo.config.spring.ReferenceBean;
import com.alibaba.dubbo.config.spring.ServiceBean;

/**

  • DubboNamespaceHandler
  • @author william.liangf
  • @export
    */

public class DubboNamespaceHandler extends NamespaceHandlerSupport {

static {  
    Version.checkDuplicate(DubboNamespaceHandler.class);  
}  

public void init() {  
    registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));  
    registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));  
    registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));  
    registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));  
    registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));  
    registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));  
    registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));  
    registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));  
    registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));  
    registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));  
}

}
通过上面的代码可以很直观的发现,标签实际是由com.alibaba.dubbo.config.spring.schema.DubboBeanDefinitionParser解析:
DubboBeanDefinitionParser.java
[java] view plain copy
/**

  • AbstractBeanDefinitionParser
  • @author william.liangf
  • @export
    */

public class DubboBeanDefinitionParser implements BeanDefinitionParser {

private static final Logger logger = LoggerFactory.getLogger(DubboBeanDefinitionParser.class);  

private final Class<?> beanClass;  

private final boolean required;  

public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {  
    this.beanClass = beanClass;  
    this.required = required;  
}  

public BeanDefinition parse(Element element, ParserContext parserContext) {  
    return parse(element, parserContext, beanClass, required);  
}  

@SuppressWarnings("unchecked")  
private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {  
    //略  
}

可以看到这个类实现了Spring的org.springframework.beans.factory.xml.BeanDefinitionParser接口,从而完成Spring Bean的解析工作。
而registerBeanDefinitionParser(“annotation”, new DubboBeanDefinitionParser(AnnotationBean.class, true));就是将标签,解析成com.alibaba.dubbo.config.spring.AnnotationBean并注册到Spring中。
3.AnnotationBean分析
先来看看源码:
AnnotationBean.java
[java] view plain copy
package com.alibaba.dubbo.config.spring;

/**

  • AnnotationBean
  • @author william.liangf
  • @export
    */

public class AnnotationBean extends AbstractConfig implements DisposableBean, BeanFactoryPostProcessor, BeanPostProcessor, ApplicationContextAware {

private static final long serialVersionUID = -7582802454287589552L;  

private static final Logger logger = LoggerFactory.getLogger(Logger.class);  

private String annotationPackage;  

private String[] annotationPackages;  

private final Set<ServiceConfig<?>> serviceConfigs = new ConcurrentHashSet<ServiceConfig<?>>();  

private final ConcurrentMap<String, ReferenceBean<?>> referenceConfigs = new ConcurrentHashMap<String, ReferenceBean<?>>();  

public String getPackage() {  
    return annotationPackage;  
}  

public void setPackage(String annotationPackage) {  
    this.annotationPackage = annotationPackage;  
    this.annotationPackages = (annotationPackage == null || annotationPackage.length() == 0) ? null  
            : Constants.COMMA_SPLIT_PATTERN.split(annotationPackage);  
}  

private ApplicationContext applicationContext;  

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {  
    this.applicationContext = applicationContext;  
}  

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)  
        throws BeansException {  
    if (annotationPackage == null || annotationPackage.length() == 0) {  
        return;  
    }  
    if (beanFactory instanceof BeanDefinitionRegistry) {  
        try {  
            // init scanner  
            Class<?> scannerClass = ReflectUtils.forName("org.springframework.context.annotation.ClassPathBeanDefinitionScanner");  
            Object scanner = scannerClass.getConstructor(new Class<?>[] {BeanDefinitionRegistry.class, boolean.class}).newInstance(new Object[] {(BeanDefinitionRegistry) beanFactory, true});  
            // add filter  
            Class<?> filterClass = ReflectUtils.forName("org.springframework.core.type.filter.AnnotationTypeFilter");  
            Object filter = filterClass.getConstructor(Class.class).newInstance(Service.class);  
            Method addIncludeFilter = scannerClass.getMethod("addIncludeFilter", ReflectUtils.forName("org.springframework.core.type.filter.TypeFilter"));  
            addIncludeFilter.invoke(scanner, filter);  
            // scan packages  
            String[] packages = Constants.COMMA_SPLIT_PATTERN.split(annotationPackage);  
            Method scan = scannerClass.getMethod("scan", new Class<?>[]{String[].class});  
            scan.invoke(scanner, new Object[] {packages});  
        } catch (Throwable e) {  
            // spring 2.0  
        }  
    }  
}  

public void destroy() throws Exception {  
    for (ServiceConfig<?> serviceConfig : serviceConfigs) {  
        try {  
            serviceConfig.unexport();  
        } catch (Throwable e) {  
            logger.error(e.getMessage(), e);  
        }  
    }  
    for (ReferenceConfig<?> referenceConfig : referenceConfigs.values()) {  
        try {  
            referenceConfig.destroy();  
        } catch (Throwable e) {  
            logger.error(e.getMessage(), e);  
        }  
    }  
}  

public Object postProcessAfterInitialization(Object bean, String beanName)  
        throws BeansException {  
    if (! isMatchPackage(bean)) {  
        return bean;  
    }  
    Service service = bean.getClass().getAnnotation(Service.class);  
    if (service != null) {  
        ServiceBean<Object> serviceConfig = new ServiceBean<Object>(service);  
        if (void.class.equals(service.interfaceClass())  
                && "".equals(service.interfaceName())) {  
            if (bean.getClass().getInterfaces().length > 0) {  
                serviceConfig.setInterface(bean.getClass().getInterfaces()[0]);  
            } else {  
                throw new IllegalStateException("Failed to export remote service class " + bean.getClass().getName() + ", cause: The @Service undefined interfaceClass or interfaceName, and the service class unimplemented any interfaces.");  
            }  
        }  
        if (applicationContext != null) {  
            serviceConfig.setApplicationContext(applicationContext);  
            if (service.registry() != null && service.registry().length > 0) {  
                List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();  
                for (String registryId : service.registry()) {  
                    if (registryId != null && registryId.length() > 0) {  
                        registryConfigs.add((RegistryConfig)applicationContext.getBean(registryId, RegistryConfig.class));  
                    }  
                }  
                serviceConfig.setRegistries(registryConfigs);  
            }  
            if (service.provider() != null && service.provider().length() > 0) {  
                serviceConfig.setProvider((ProviderConfig)applicationContext.getBean(service.provider(),ProviderConfig.class));  
            }  
            if (service.monitor() != null && service.monitor().length() > 0) {  
                serviceConfig.setMonitor((MonitorConfig)applicationContext.getBean(service.monitor(), MonitorConfig.class));  
            }  
            if (service.application() != null && service.application().length() > 0) {  
                serviceConfig.setApplication((ApplicationConfig)applicationContext.getBean(service.application(), ApplicationConfig.class));  
            }  
            if (service.module() != null && service.module().length() > 0) {  
                serviceConfig.setModule((ModuleConfig)applicationContext.getBean(service.module(), ModuleConfig.class));  
            }  
            if (service.provider() != null && service.provider().length() > 0) {  
                serviceConfig.setProvider((ProviderConfig)applicationContext.getBean(service.provider(), ProviderConfig.class));  
            } else {  

            }  
            if (service.protocol() != null && service.protocol().length > 0) {  
                List<ProtocolConfig> protocolConfigs = new ArrayList<ProtocolConfig>();  
                for (String protocolId : service.registry()) {  
                    if (protocolId != null && protocolId.length() > 0) {  
                        protocolConfigs.add((ProtocolConfig)applicationContext.getBean(protocolId, ProtocolConfig.class));  
                    }  
                }  
                serviceConfig.setProtocols(protocolConfigs);  
            }  
            try {  
                serviceConfig.afterPropertiesSet();  
            } catch (RuntimeException e) {  
                throw (RuntimeException) e;  
            } catch (Exception e) {  
                throw new IllegalStateException(e.getMessage(), e);  
            }  
        }  
        serviceConfig.setRef(bean);  
        serviceConfigs.add(serviceConfig);  
        serviceConfig.export();  
    }  
    return bean;  
}  

public Object postProcessBeforeInitialization(Object bean, String beanName)  
        throws BeansException {  
    if (! isMatchPackage(bean)) {  
        return bean;  
    }  
    Method[] methods = bean.getClass().getMethods();  
    for (Method method : methods) {  
        String name = method.getName();  
        if (name.length() > 3 && name.startsWith("set")  
                && method.getParameterTypes().length == 1  
                && Modifier.isPublic(method.getModifiers())  
                && ! Modifier.isStatic(method.getModifiers())) {  
            try {  
                Reference reference = method.getAnnotation(Reference.class);  
                if (reference != null) {  
                    Object value = refer(reference, method.getParameterTypes()[0]);  
                    if (value != null) {  
                        method.invoke(bean, new Object[] {  });  
                    }  
                }  
            } catch (Throwable e) {  
                logger.error("Failed to init remote service reference at method " + name + " in class " + bean.getClass().getName() + ", cause: " + e.getMessage(), e);  
            }  
        }  
    }  
    Field[] fields = bean.getClass().getDeclaredFields();  
    for (Field field : fields) {  
        try {  
            if (! field.isAccessible()) {  
                field.setAccessible(true);  
            }  
            Reference reference = field.getAnnotation(Reference.class);  
            if (reference != null) {  
                Object value = refer(reference, field.getType());  
                if (value != null) {  
                    field.set(bean, value);  
                }  
            }  
        } catch (Throwable e) {  
            logger.error("Failed to init remote service reference at filed " + field.getName() + " in class " + bean.getClass().getName() + ", cause: " + e.getMessage(), e);  
        }  
    }  
    return bean;  
}  

private Object refer(Reference reference, Class<?> referenceClass) { //method.getParameterTypes()[0]  
    String interfaceName;  
    if (! "".equals(reference.interfaceName())) {  
        interfaceName = reference.interfaceName();  
    } else if (! void.class.equals(reference.interfaceClass())) {  
        interfaceName = reference.interfaceClass().getName();  
    } else if (referenceClass.isInterface()) {  
        interfaceName = referenceClass.getName();  
    } else {  
        throw new IllegalStateException("The @Reference undefined interfaceClass or interfaceName, and the property type " + referenceClass.getName() + " is not a interface.");  
    }  
    String key = reference.group() + "/" + interfaceName + ":" + reference.version();  
    ReferenceBean<?> referenceConfig = referenceConfigs.get(key);  
    if (referenceConfig == null) {  
        referenceConfig = new ReferenceBean<Object>(reference);  
        if (void.class.equals(reference.interfaceClass())  
                && "".equals(reference.interfaceName())  
                && referenceClass.isInterface()) {  
            referenceConfig.setInterface(referenceClass);  
        }  
        if (applicationContext != null) {  
            referenceConfig.setApplicationContext(applicationContext);  
            if (reference.registry() != null && reference.registry().length > 0) {  
                List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();  
                for (String registryId : reference.registry()) {  
                    if (registryId != null && registryId.length() > 0) {  
                        registryConfigs.add((RegistryConfig)applicationContext.getBean(registryId, RegistryConfig.class));  
                    }  
                }  
                referenceConfig.setRegistries(registryConfigs);  
            }  
            if (reference.consumer() != null && reference.consumer().length() > 0) {  
                referenceConfig.setConsumer((ConsumerConfig)applicationContext.getBean(reference.consumer(), ConsumerConfig.class));  
            }  
            if (reference.monitor() != null && reference.monitor().length() > 0) {  
                referenceConfig.setMonitor((MonitorConfig)applicationContext.getBean(reference.monitor(), MonitorConfig.class));  
            }  
            if (reference.application() != null && reference.application().length() > 0) {  
                referenceConfig.setApplication((ApplicationConfig)applicationContext.getBean(reference.application(), ApplicationConfig.class));  
            }  
            if (reference.module() != null && reference.module().length() > 0) {  
                referenceConfig.setModule((ModuleConfig)applicationContext.getBean(reference.module(), ModuleConfig.class));  
            }  
            if (reference.consumer() != null && reference.consumer().length() > 0) {  
                referenceConfig.setConsumer((ConsumerConfig)applicationContext.getBean(reference.consumer(), ConsumerConfig.class));  
            }  
            try {  
                referenceConfig.afterPropertiesSet();  
            } catch (RuntimeException e) {  
                throw (RuntimeException) e;  
            } catch (Exception e) {  
                throw new IllegalStateException(e.getMessage(), e);  
            }  
        }  
        referenceConfigs.putIfAbsent(key, referenceConfig);  
        referenceConfig = referenceConfigs.get(key);  
    }  
    return referenceConfig.get();  
}  

private boolean isMatchPackage(Object bean) {  
    if (annotationPackages == null || annotationPackages.length == 0) {  
        return true;  
    }  
    String beanClassName = bean.getClass().getName();  
    for (String pkg : annotationPackages) {  
        if (beanClassName.startsWith(pkg)) {  
            return true;  
        }  
    }  
    return false;  
}

}
这个AnnotationBean实现了几个Spring生命周期接口,从而完成Dubbo整合Spring 的操作。
org.springframework.beans.factory.config.BeanFactoryPostProcessor
先来看看Spring文档中的介绍:
[plain] view plain copy
BeanFactoryPostProcessor operates on the bean configuration metadata; that is, the Spring IoC container allows a BeanFactoryPostProcessor to read the configuration metadata and potentially change it before the container instantiates any beans other than BeanFactoryPostProcessors.
BeanFactoryPostProcessor可以用于在Spring IoC容器实例化Bean之前,对Spring Bean配置信息进行一些操作
通过Spring文档,可以清楚这个接口的功能,那再来看看Dubbo的AnnotationBean是如何实现这个接口的:
[java] view plain copy
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
if (annotationPackage == null || annotationPackage.length() == 0) {
return;
}
if (beanFactory instanceof BeanDefinitionRegistry) {
try {
// init scanner
Class

相关文章
|
1月前
|
安全 Java API
深入解析 Spring Security 配置中的 CSRF 启用与 requestMatchers 报错问题
本文深入解析了Spring Security配置中CSRF启用与`requestMatchers`报错的常见问题。针对CSRF,指出默认已启用,无需调用`enable()`,只需移除`disable()`即可恢复。对于`requestMatchers`多路径匹配报错,分析了Spring Security 6.x中方法签名的变化,并提供了三种解决方案:分次调用、自定义匹配器及降级使用`antMatchers()`。最后提醒开发者关注版本兼容性,确保升级平稳过渡。
128 2
|
2月前
|
缓存 Java API
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
本文介绍了在Spring Boot中配置Swagger2的方法。通过创建一个配置类,添加`@Configuration`和`@EnableSwagger2`注解,使用Docket对象定义API文档的详细信息,包括标题、描述、版本和包路径等。配置完成后,访问`localhost:8080/swagger-ui.html`即可查看接口文档。文中还提示了可能因浏览器缓存导致的问题及解决方法。
98 0
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
|
2月前
|
Java 关系型数据库 数据库
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——Spring Boot 事务配置
本文介绍了 Spring Boot 中的事务配置与使用方法。首先需要导入 MySQL 依赖,Spring Boot 会自动注入 `DataSourceTransactionManager`,无需额外配置即可通过 `@Transactional` 注解实现事务管理。接着通过创建一个用户插入功能的示例,展示了如何在 Service 层手动抛出异常以测试事务回滚机制。测试结果表明,数据库中未新增记录,证明事务已成功回滚。此过程简单高效,适合日常开发需求。
143 0
|
2月前
|
Java 测试技术 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——少量配置信息的情形
本课主要讲解Spring Boot项目中的属性配置方法。在实际开发中,测试与生产环境的配置往往不同,因此不应将配置信息硬编码在代码中,而应使用配置文件管理,如`application.yml`。例如,在微服务架构下,可通过配置文件设置调用其他服务的地址(如订单服务端口8002),并利用`@Value`注解在代码中读取这些配置值。这种方式使项目更灵活,便于后续修改和维护。
40 0
|
2月前
|
SQL Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— application.yml 中对日志的配置
在 Spring Boot 项目中,`application.yml` 文件用于配置日志。通过 `logging.config` 指定日志配置文件(如 `logback.xml`),实现日志详细设置。`logging.level` 可定义包的日志输出级别,例如将 `com.itcodai.course03.dao` 包设为 `trace` 级别,便于开发时查看 SQL 操作。日志级别从高到低为 ERROR、WARN、INFO、DEBUG,生产环境建议调整为较高级别以减少日志量。本课程采用 yml 格式,因其层次清晰,但需注意格式要求。
176 0
|
1月前
|
人工智能 缓存 自然语言处理
保姆级Spring AI 注解式开发教程,你肯定想不到还能这么玩!
这是一份详尽的 Spring AI 注解式开发教程,涵盖从环境配置到高级功能的全流程。Spring AI 是 Spring 框架中的一个模块,支持 NLP、CV 等 AI 任务。通过注解(如自定义 `@AiPrompt`)与 AOP 切面技术,简化了 AI 服务集成,实现业务逻辑与 AI 基础设施解耦。教程包含创建项目、配置文件、流式响应处理、缓存优化及多任务并行执行等内容,助你快速构建高效、可维护的 AI 应用。
|
2月前
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于注解的整合
本文介绍了Spring Boot集成MyBatis的两种方式:基于XML和注解的形式。重点讲解了注解方式,包括@Select、@Insert、@Update、@Delete等常用注解的使用方法,以及多参数时@Param注解的应用。同时,针对字段映射不一致的问题,提供了@Results和@ResultMap的解决方案。文章还提到实际项目中常结合XML与注解的优点,灵活使用两者以提高开发效率,并附带课程源码供下载学习。
62 0
|
2月前
|
Java 数据库连接 数据库
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——MyBatis 介绍和配置
本文介绍了Spring Boot集成MyBatis的方法,重点讲解基于注解的方式。首先简述MyBatis作为持久层框架的特点,接着说明集成时的依赖导入,包括`mybatis-spring-boot-starter`和MySQL连接器。随后详细展示了`properties.yml`配置文件的内容,涵盖数据库连接、驼峰命名规范及Mapper文件路径等关键设置,帮助开发者快速上手Spring Boot与MyBatis的整合开发。
145 0
|
2月前
|
缓存 Java 应用服务中间件
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——依赖导入和Thymeleaf相关配置
在Spring Boot中使用Thymeleaf模板,需引入依赖`spring-boot-starter-thymeleaf`,并在HTML页面标签中声明`xmlns:th=&quot;http://www.thymeleaf.org&quot;`。此外,Thymeleaf默认开启页面缓存,开发时建议关闭缓存以实时查看更新效果,配置方式为`spring.thymeleaf.cache: false`。这可避免因缓存导致页面未及时刷新的问题。
65 0
|
2月前
|
Java 数据库 微服务
微服务——SpringBoot使用归纳——Spring Boot中的项目属性配置——指定项目配置文件
在实际项目中,开发环境和生产环境的配置往往不同。为简化配置切换,可通过创建 `application-dev.yml` 和 `application-pro.yml` 分别管理开发与生产环境配置,如设置不同端口(8001/8002)。在 `application.yml` 中使用 `spring.profiles.active` 指定加载的配置文件,实现环境快速切换。本节还介绍了通过配置类读取参数的方法,适用于微服务场景,提升代码可维护性。课程源码可从 [Gitee](https://gitee.com/eson15/springboot_study) 下载。
71 0