解决dubbo 中的 @Reference 引用远程服务为空。

web.xml加载spring-mvc 和 spring-dubbo 配置文件

 <context-param>  <!-- 加载配置文件  -->
	       <param-name>contextConfigLocation</param-name>
	       <param-value>classpath:spring/spring-dubbo.xml</param-value>
</context-param>
<listener>  
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
</listener> 

 <!-- 配置springMVC中央处理器  --> 
   <servlet>
      <servlet-name>SpringMVC</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	  <init-param>  <!-- 加载配置文件  -->
	       <param-name>contextConfigLocation</param-name>
	       <param-value>classpath:spring/spring-mvc.xml</param-value>
	  </init-param>
	  
      <load-on-startup>1</load-on-startup>
   </servlet>
   <!-- 指定需要过滤的请求 Mapping   【/】 表示所有请求  -->
   <servlet-mapping>
      <servlet-name>SpringMVC</servlet-name>
      <url-pattern>/</url-pattern>
   </servlet-mapping>

上面的web.xml 把 spring-dubbo 的配置文件加入到了spring 容器中(根上下文),spingmvc 的配置文件被加入到springmvc容器中。关于Spring和springmvc父子容器的关系参考我的另一篇博文https://blog.csdn.net/liuhaiquan123521/article/details/94061017

Controller 代码如下

package com.kavin.consumer.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.alibaba.dubbo.config.annotation.Reference;
import com.kavin.api.UserService;

@Controller
public class UserController {

	@Reference    //使用dubbo提供的reference注解引用远程服务
	private UserService userService;
	
	@RequestMapping("/")
	public String getUser() {
		System.out.println(userService.sayHello("张三"));
		return "index";
	}
}

当@Reference 注解在 带有 @Controller 的类中使用的时候,默认@Controller的javaBean 被加载到SpringMVC容器中,因为spingmvc容器没有加载dubbo 的配置文件,而是被spring容器加载了,从而导致@Reference 在SpringMVC容器中不能被解析,所以被@Reference 修饰的远程服务变量不能被注入成功,才会使引用的服务为空。@Reference 相当于给Controller 去 Set 一个属性。

解决办法:
1.只将dubbo服务注入到service和Repository中而不是Controller中,其实大部分时候都可如此
(先提此时件是spring容器加载dubbo配置文件和spring扫包的配置文件。springmvc 加载springmvc 的配置文件。并且spring扫描service层、dao层的javaBean,而springMVC 扫描Controller层的javaBean,包路径不重叠,避免spring加载了一遍,springmvc又重复加载一遍。
因为spring容器先加载指定包下的bean,此时的dubbo注解能被识别,(因为spring容器加载了dubbo 的配置文件,所以能识别dubbo 注解)然后注入进来。当springmvc 扫包的路径和spring容器扫包的路径一致时。则会重新加载这个包路径下的JavaBean 到springmvc容器中。但是此时springmvc容器中并没有加载dubbo的配置文件,所以无法识别dubbo 的注解,因此注入失败,出现NULL的情况)

2.去掉web.xml中listener,将全部配置文件都放到springmvc中加载,这样只生成一个上下文。
3.在spring-mvc.xml也加入duboo的配置,这样虽然有了冗余,但是可以保证两个上下文。

我采用第二种方式解决。 将上面的web,xml 改成下面的web,xml
去掉了listener 和 context-param。让springmvc加载dubbo的配置文件。因。为spring-mvc 和spring-dubbo 都是 以spring开头的。并且在spring目录下。所以修改init-param 中的
param-value 为classpath:spring/spring-*.xml

 <!-- 配置springMVC中央处理器  --> 
   <servlet>
      <servlet-name>SpringMVC</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	  <init-param>  <!-- 加载dubbo 和 mvc的配置文件  -->
	       <param-name>contextConfigLocation</param-name>
	       <param-value>classpath:spring/spring-*.xml</param-value>
	  </init-param>
	  
      <load-on-startup>1</load-on-startup>
   </servlet>
   <!-- 指定需要过滤的请求 Mapping   【/】 表示所有请求  -->
   <servlet-mapping>
      <servlet-name>SpringMVC</servlet-name>
      <url-pattern>/</url-pattern>
   </servlet-mapping>

这个问题主要是考验了spring容器和springmvc容器的区别。之前我以为他俩是一个容器,后来翻阅好多文章之后才发现他俩是父子容器关系。因为Controller 在springmvc容器中被注入,而只有spring-dubbo文件加载到springmvc 中。springmvc容器才可以解析@Reference 注解,才能注入成功。否则Springmvc 容器在注册Controller 这个javaBean 的时候,发现@Reference这个注解无法解析,属性也随之注入失败。

发布了25 篇原创文章 · 获赞 4 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/liuhaiquan123521/article/details/94062904