相关文章推荐

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement . We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Steps to reproduce this issue

xml配置dubbo:reference方式,dubbo:method配置的onreturn,onthrow方法为spring bean(以下称A)的方法,这些方法上还有诸如 @Transactional 之类的Spring AOP注解。
A里面 @Autowired 注解的成员均注入失败,使用时均为null对象。
我们采取的补救方法是去掉 @Autowired 注解,将A增加 ApplicationListener<ContextRefreshedEvent> 接口实现,在onApplicationEvent方法里重新从ApplicationContext里取得相应的bean赋给本该由 @Autowired 注入的成员。

onreturn,onthrow方法上的Spring AOP注解均失效。
我们采取的补救方法是将onreturn,onthrow方法拆分,相关业务处理代码以及Spring AOP注解移到另外一个bean,A里面的onreturn,onthrow方法只是调用另外那个bean带注解的方法。

onThrow方法捕获不了dubbo客户端抛出的RpcException。
重现方法:删除dubbo服务接口入参class上的序列化接口定义,在事件通知方式下客户端的onThrow方法未捕获到任何异常,在 @Reference 注解方式下调用会抛出dubbo入参未实现java.io.Serializable接口的RPC异常。

@Reference 注解配置oninvoke,onreturn,onthrow无效,我们看到 #6833 已经描述过这个问题,5月份已修复,我们看到最新发布的3.0代码里已包含了修复,但是修复的方法上还有 @Deprecated 注解,是否还有进一步修订计划?

@reference 注解配置oninvoke,onreturn,onthrow无效,我们看到 #6833 已经描述过这个问题,5月份已修复,我们看到最新发布的3.0代码里已包含了修复,但是修复的方法上还有 @deprecated 注解,是否还有进一步修订计划?

3.0 分支重构了ReferenceBean的逻辑,统一了xml及Annotation的初始化过程,方法回调配置解析改为: org.apache.dubbo.config.spring.reference.ReferenceCreator#createMethodConfig。 相关pr: #8109

xml配置dubbo:reference方式,dubbo:method配置的onreturn,onthrow方法为spring bean(以下称A)的方法,这些方法上还有诸如 @transactional 之类的Spring AOP注解。
A里面 @Autowired 注解的成员均注入失败,使用时均为null对象。

可以提供一个具体的测试用例吗?

package com.test.notifyApi;
public interface UserNotifyService {
    String getName(NotifyDto dto);

接口方法入参NotifyDto:

package com.test.notifyApi;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import lombok.Data;
@Data
public class NotifyDto implements Serializable {
    private static final long serialVersionUID = 1L;
    private String id;
    @NotNull
    private String sex;
  • consumer工程notifyConsumer.jar
    配置类DubboReferenceConfig:
  • package com.test.notifyConsumer.config;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.ImportResource;
    @Configuration
    @ImportResource({"classpath:notify.xml"})
    public class DubboReferenceConfig {
    

    定义Dubbo Reference bean的notify.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans        
        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        
        http://dubbo.apache.org/schema/dubbo        
        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
        <dubbo:reference id="userNotifyService"
            interface="com.test.notifyApi.UserNotifyService"
            check="false" timeout="3000">
            <dubbo:method name="getName" validation="true"
                retries="0" async="true"
                oninvoke="consumerNotifyServiceImpl.onInvoke"
                onreturn="consumerNotifyServiceImpl.onReturn"
                onthrow="consumerNotifyServiceImpl.onThrow" />
        </dubbo:reference>		
    </beans>
    

    事件通知接口ConsumerNotifyService:

    package com.test.notifyConsumer.service;
    import com.test.notifyApi.NotifyDto;
    public interface ConsumerNotifyService {
        void onInvoke(NotifyDto dto);
        void onReturn(String name, NotifyDto dto);    
        void onThrow(Throwable e, NotifyDto dto);
        void getName(NotifyDto dto);
    

    事件通知接口实现类ConsumerNotifyServiceImpl :

    package com.test.notifyConsumer.service.impl;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextRefreshedEvent;
    import org.springframework.stereotype.Component;
    import com.test.notifyApi.NotifyDto;
    import com.test.notifyApi.UserNotifyService;
    import com.test.notifyConsumer.aspect.MethodAnnotation;
    import com.test.notifyConsumer.service.ConsumerNotifyService;
    import lombok.extern.slf4j.Slf4j;
    @Slf4j
    @Component
    public class ConsumerNotifyServiceImpl implements ConsumerNotifyService {
        @Autowired
        UserNotifyService userNotifyService;
        @Autowired
        NotifyWork work;
        @MethodAnnotation("invoke-annotation")
        public void onInvoke(NotifyDto dto) {
            log.info("invoke for {}", dto);
            work.workInvoke(dto);
        @MethodAnnotation("return-annotation")
        public void onReturn(String name, NotifyDto dto) {
            log.info("return {} for {}", name, dto);
            work.workReturn(name, dto);
        @MethodAnnotation("throw-annotation")
        public void onThrow(Throwable e, NotifyDto dto) {
            log.info("throw exception {} for {}", e, dto);
            work.workThrow(e, dto);
        public void getName(NotifyDto dto) {
            userNotifyService.getName(dto);
    

    注解MethodAnnotation:

    package com.test.notifyConsumer.aspect;
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface MethodAnnotation {
        String value() default "";
    

    切面MethodAspect :

    package com.test.notifyConsumer.aspect;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.stereotype.Component;
    import lombok.extern.slf4j.Slf4j;
    @Slf4j
    @Component
    @Aspect
    public class MethodAspect {
        @Around("@annotation(annotation)")
        public Object aroundMethod(ProceedingJoinPoint pjp, MethodAnnotation annotation) throws Throwable {
            log.info("aspect {} for {}", annotation.value(), pjp.getSignature().getName());
            return pjp.proceed();
    
    package com.test.notifyConsumer;
    import javax.validation.ValidationException;
    import org.junit.AfterClass;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import com.test.notifyApi.NotifyDto;
    import com.test.notifyConsumer.service.ConsumerNotifyService;
    import lombok.extern.slf4j.Slf4j;
    @Slf4j
    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringBootTest(classes = App.class)
    public class AppTest
        @Autowired
        ConsumerNotifyService consumerNotifyService;
        @Test
        public void test1() {
            NotifyDto dto = new NotifyDto();
            dto.setId("12345");
            dto.setSex("M");
            consumerNotifyService.getName(dto);        
        // 2.7.5: ConstraintViolationException, 2.7.12: ValidationException(前者的超类)
        @Test(expected = ValidationException.class)
        public void test2() {
            NotifyDto dto = new NotifyDto();
            dto.setId("67890");
            consumerNotifyService.getName(dto);        
        @Test
        public void test3() {
            NotifyDto dto = new NotifyDto();
            dto.setSex("W");
            consumerNotifyService.getName(dto);        
        @AfterClass
        public static void waitForNotify() throws InterruptedException {
            log.info("WAIT 1s");        
            Thread.sleep(1000);
    
  • provider工程
    服务UserNotifyServiceImpl:
  • package com.test.notifyService.service.impl;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.dubbo.config.annotation.Service;
    import org.apache.dubbo.rpc.RpcException;
    import com.test.notifyApi.NotifyDto;
    import com.test.notifyApi.UserNotifyService;
    import lombok.extern.slf4j.Slf4j;
    @Slf4j
    @Service
    public class UserNotifyServiceImpl implements UserNotifyService {
        @Override
        public String getName(NotifyDto dto) {
            log.info("getname param={}", dto);
            if (StringUtils.isEmpty(dto.getId())) {
                throw new RpcException("null id");
            return "name-" + dto.getId() + "(" + dto.getSex() + ")";
    

    dubbo2.7.5版本
    无论是在启动类加@EnableDubbo注解,还是在application.yml配置dubbo.scan.basePackages都存在问题1、2、3;两种方式启动时间都是12秒左右。

    dubbo2.7.12版本
    启动类上加@EnableDubbo注解的方式,运行正常,问题1、2、3都没有了;只是改成这种方式之后启动测试程序耗时从18秒变成了80秒,也是醉了。
    只在application.yml配置dubbo.scan.basePackages的方式还有问题1、2,而问题3没有了,启动耗时不到18秒。

    启动慢的原因找到了,有1台nacos注册中心down了,up之后重新测试,两个dubbo版本每种方式各测试10次左右,情况如下。
    dubbo2.7.5版本:两种方式下启动耗时均在5~7秒之间;
    dubbo2.7.12版本:@EnableDubbo方式下启动耗时在9~12秒之间;application.yml配置dubbo.scan.basePackages方式下启动耗时在6~10秒之间。
    
     
    推荐文章