之前项目中一直使用dubbox,最近dubbo社区又开始进行维护,公司正好又有一个新项目启动,本来想用spring-boot,正好进行下相关学习,奈何领导这关没过,还是让用最原始的spring-xml的配置方式,但是spring社区已经推荐用java代码配置的方式取代xml,所以应该顺应时代的潮流,采用最新的spring配置dubbo的方式,关于spring-boot的学习只能在课下进行了。但是自己在按照相关教程配置好dubbo的结构后,发现消费端的@Reference注入的service总是为空。一开始自己以为dubbo的服务没有注册上,后来通过在服务端启动日志,查看到服务确确实实是已经注册进了zookeepe人,所以问题就是出在消费端调用上。
web即相当于dubbo的消费端(consumer),service相当于dubbo的服务端(provider),api为dubbo对外暴露的接口(interface)。
api端这里暴露的是接口与常用的配置项的注入:
先说一下Property的内容,因为自己需要在consumer与provider的@Configuration中使用这些配置类,而很多配置类是通用的,所以这里自己统一定义成了property的bean,然后采用@value的方式将属性值注入进来,这样在各个配置端采用注入的方式将这些配置Bean直接注入到@Configuration的配置类中,直接进行引用即可。
配置类(@Configuration)中:
首先需要自动将这些Bean注入进来,
@Configuration
@ComponentScan(basePackages = {"com.baheal.drools.property"})
注意我们引入api的方式是在maven中通过dependency的方式引入进来的,所以在更改了api中的property的bean之后,必须重新编译生成,这样各个引用端才可以使用最新的api的jar。而后,配置类中即可正常使用这些配置项:
@Bean
public JedisPoolConfig jedisPoolConfig(RedisProperty property){
}
好了,言归正传,讨论如何采用javaconfig的方式配置dubbo服务。
暴露接口:
public interface QuotaEstimateService {
public String testDubbo();
}
首先需要将接口实现:
@Service
public class QuotaEstimateServiceImpl implements QuotaEstimateService {
@Override
public String testDubbo() {
return "dubbo 服务提供者";
}
}
注意此处的@Service不是采用的Spring自己的注解,而是dubbo的@Service注解。
import com.alibaba.dubbo.config.annotation.Service;
而后需要进行配置类的相关设置:
@Configuration
@EnableDubbo(scanBasePackages = "com.baheal.drools.service.impl")
public class DubboConfig {
@Bean
public ProviderConfig providerConfig(){
return new ProviderConfig();
}
@Bean
public ApplicationConfig applicationConfig(){
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("estimate-service-impl");
return applicationConfig;
}
@Bean
public RegistryConfig registryConfig(){
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
return registryConfig;
}
@Bean
public ProtocolConfig protocolConfig(){
ProtocolConfig protocolConfig = new ProtocolConfig();
protocolConfig.setName("dubbo");
protocolConfig.setPort(20880);
return protocolConfig;
}
@EnableDubbo标签负责扫描包下带有@service的类并将其发布到zookeeper之上,下面的4个bean则是dubbo的相关配置,设置好以后先启动本地zookeeper,然后服务端即可启动,然后就会将服务注册到zookeeper之上。
通过查看启动日志就会发现服务以及成功注册(若是显示dubbo没有logappender,则需要将dubbo本身的log4j的jar包剔除掉因为我本地采用的是logback,只要resources文件夹下含有logback.xml,便会自动加载。
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<exclusions>
<exclusion>
<artifactId>log4jartifactId>
<groupId>log4jgroupId>
exclusion>
exclusions>
dependency>
)
2018-09-21 23:27:27 [RMI TCP Connection(127.0.0.1:2181)] INFO org.apache.zookeeper.ClientCnxn - Socket connection established to 127.0.0.1/127.0.0.1:2181, initiating session
2018-09-21 23:27:27 [RMI TCP Connection(3)-127.0.0.1] INFO com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry - [DUBBO] Register: dubbo://192.168.8.27:20880/com.baheal.drools.service.QuotaEstimateService?anyhost=true&application=estimate-service-impl&dubbo=2.6.2&generic=false&interface=com.baheal.drools.service.QuotaEstimateService&methods=testDubbo,quotaEstimateQuota&pid=3608&revision=1.0-SNAPSHOT&side=provider×tamp=1537543646427, dubbo version: 2.6.2, current host: 192.168.8.27
之前自己采用的是如下的配置,将DubboConfig通过Spring的Configuration引入,即在
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
private final static Logger logger = LoggerFactory.getLogger(WebAppInitializer.class);
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { DubboConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[]{ "/" };
}
configuration:
@Configuration
@EnableDubbo(scanBasePackages = "com.baheal.drools.controller")
public class DubboConfig {
@Bean
public ApplicationConfig applicationConfig(){
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("estimate-web");
return applicationConfig;
}
@Bean
public ConsumerConfig consumerConfig(){
ConsumerConfig consumerConfig = new ConsumerConfig();
consumerConfig.setTimeout(6000);
return consumerConfig;
}
@Bean
public RegistryConfig registryConfig(){
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
return registryConfig;
}
}
然后在使用的controller里面通过@Reference的方式进行引入:
@Reference
QuotaEstimateService quotaEstimateService;
然而在用的过程中发现注入的QuotaEstimateService 一直为null,这个问题困扰了自己很久,在上班的时候一直没有进行解决,加了一会班也是没有思路,后来在公交车上用手机查询相关问题时忽然看见可能是springMVC与Dubbo的注解冲突的问题。@Reference注解实质上是将暴露的服务以@Bean的方式注入进来,而自己的@Reference是通过Spring的componentScan扫描进来的,而SpringMVC的Controller初始化发生在@Reference之前,这就导致注入进来的服务为空的问题。
所以
@EnableDubbo(scanBasePackages = "com.baheal.drools.controller")
这项自动扫描主要是将@Reference注册的dubbo服务以Bean的方式注入进来,所以该项扫描必须改为由WebConfig进行加载进来。
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.baheal.drools.controller")
@Import({DubboConfig.class})
public class WebConfig extends WebMvcConfigurerAdapter {
}
@Configuration
@EnableDubbo(scanBasePackages = "com.baheal.drools.controller")
public class DubboConfig {
.................
}
然后启动Consumer端,Controller端的@Reference已经可以成功注入Dubbo的Service,问题解决。