乐趣区

聊聊DubboOpenFeignAutoConfiguration

本文主要研究一下 DubboOpenFeignAutoConfiguration

DubboOpenFeignAutoConfiguration

spring-cloud-alibaba-0.9.0.RELEASE/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboOpenFeignAutoConfiguration.java

@ConditionalOnClass(name = {"feign.Feign", TARGETER_CLASS_NAME})
@AutoConfigureAfter(name = {"org.springframework.cloud.openfeign.FeignAutoConfiguration"})
@Configuration
public class DubboOpenFeignAutoConfiguration {

    public static final String TARGETER_CLASS_NAME = "org.springframework.cloud.openfeign.Targeter";

    @Bean
    public TargeterBeanPostProcessor targeterBeanPostProcessor(Environment environment,
                                                               DubboServiceMetadataRepository dubboServiceMetadataRepository,
                                                               DubboGenericServiceFactory dubboGenericServiceFactory,
                                                               DubboGenericServiceExecutionContextFactory contextFactory) {
        return new TargeterBeanPostProcessor(environment, dubboServiceMetadataRepository,
                dubboGenericServiceFactory, contextFactory);
    }

}
  • DubboOpenFeignAutoConfiguration 注册了 TargeterBeanPostProcessor

TargeterBeanPostProcessor

spring-cloud-alibaba-0.9.0.RELEASE/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/TargeterBeanPostProcessor.java

public class TargeterBeanPostProcessor implements BeanPostProcessor, BeanClassLoaderAware {

    private final Environment environment;

    private final DubboServiceMetadataRepository dubboServiceMetadataRepository;

    private final DubboGenericServiceFactory dubboGenericServiceFactory;

    private final DubboGenericServiceExecutionContextFactory contextFactory;

    private ClassLoader classLoader;

    public TargeterBeanPostProcessor(Environment environment,
                                     DubboServiceMetadataRepository dubboServiceMetadataRepository,
                                     DubboGenericServiceFactory dubboGenericServiceFactory,
                                     DubboGenericServiceExecutionContextFactory contextFactory) {
        this.environment = environment;
        this.dubboServiceMetadataRepository = dubboServiceMetadataRepository;
        this.dubboGenericServiceFactory = dubboGenericServiceFactory;
        this.contextFactory = contextFactory;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}

    @Override
    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {if (isPresent(TARGETER_CLASS_NAME, classLoader)) {Class<?> beanClass = getUserClass(bean.getClass());
            Class<?> targetClass = resolveClassName(TARGETER_CLASS_NAME, classLoader);
            if (targetClass.isAssignableFrom(beanClass)) {return newProxyInstance(classLoader, new Class[]{targetClass},
                        new TargeterInvocationHandler(bean, environment, classLoader, dubboServiceMetadataRepository,
                                dubboGenericServiceFactory, contextFactory));
            }
        }
        return bean;
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {this.classLoader = classLoader;}
}
  • TargeterBeanPostProcessor 实现了 BeanPostProcessor、BeanClassLoaderAware 接口;其 postProcessAfterInitialization 方法首先判断 classLoader 是否有 org.springframework.cloud.openfeign.Targeter,如果有则判断 beanClass 的父类是否是 targetClass,如果是则使用 Proxy.newProxyInstance 进行代理,代理的 InvocationHandler 为 TargeterInvocationHandler

TargeterInvocationHandler

spring-cloud-alibaba-0.9.0.RELEASE/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/TargeterInvocationHandler.java

class TargeterInvocationHandler implements InvocationHandler {private final Logger logger = LoggerFactory.getLogger(getClass());

    private final Object bean;

    private final Environment environment;

    private final ClassLoader classLoader;

    private final DubboServiceMetadataRepository repository;

    private final DubboGenericServiceFactory dubboGenericServiceFactory;

    private final DubboGenericServiceExecutionContextFactory contextFactory;

    TargeterInvocationHandler(Object bean, Environment environment,
                              ClassLoader classLoader,
                              DubboServiceMetadataRepository repository,
                              DubboGenericServiceFactory dubboGenericServiceFactory,
                              DubboGenericServiceExecutionContextFactory contextFactory) {
        this.bean = bean;
        this.environment = environment;
        this.classLoader = classLoader;
        this.repository = repository;
        this.dubboGenericServiceFactory = dubboGenericServiceFactory;
        this.contextFactory = contextFactory;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /**
         * args[0]: FeignClientFactoryBean factory
         * args[1]: Feign.Builder feign
         * args[2]: FeignContext context
         * args[3]: Target.HardCodedTarget<T> target
         */
        FeignContext feignContext = cast(args[2]);
        Target.HardCodedTarget<?> target = cast(args[3]);

        // Execute Targeter#target method first
        method.setAccessible(true);
        // Get the default proxy object
        Object defaultProxy = method.invoke(bean, args);
        // Create Dubbo Proxy if required
        return createDubboProxyIfRequired(feignContext, target, defaultProxy);
    }

    private Object createDubboProxyIfRequired(FeignContext feignContext, Target target, Object defaultProxy) {DubboInvocationHandler dubboInvocationHandler = createDubboInvocationHandler(feignContext, target, defaultProxy);

        if (dubboInvocationHandler == null) {return defaultProxy;}

        return newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, dubboInvocationHandler);
    }

    private DubboInvocationHandler createDubboInvocationHandler(FeignContext feignContext, Target target,
                                                                Object defaultFeignClientProxy) {// Service name equals @FeignClient.name()
        String serviceName = target.name();
        Class<?> targetType = target.type();

        // Get Contract Bean from FeignContext
        Contract contract = feignContext.getInstance(serviceName, Contract.class);

        DubboTransportedMethodMetadataResolver resolver =
                new DubboTransportedMethodMetadataResolver(environment, contract);

        Map<DubboTransportedMethodMetadata, RestMethodMetadata> feignRestMethodMetadataMap = resolver.resolve(targetType);

        if (feignRestMethodMetadataMap.isEmpty()) { // @DubboTransported method was not found from the Client interface
            if (logger.isDebugEnabled()) {logger.debug("@{} method was not found in the Feign target type[{}]",
                        DubboTransported.class.getSimpleName(), targetType.getName());
            }
            return null;
        }

        // Update Metadata
        repository.initialize(serviceName);

        Map<Method, FeignMethodMetadata> feignMethodMetadataMap = getFeignMethodMetadataMap(serviceName, feignRestMethodMetadataMap);

        InvocationHandler defaultFeignClientInvocationHandler = Proxy.getInvocationHandler(defaultFeignClientProxy);

        DubboInvocationHandler dubboInvocationHandler = new DubboInvocationHandler(feignMethodMetadataMap,
                defaultFeignClientInvocationHandler, classLoader, contextFactory);

        return dubboInvocationHandler;
    }

    private Map<Method, FeignMethodMetadata> getFeignMethodMetadataMap(String serviceName,
                                                                       Map<DubboTransportedMethodMetadata, RestMethodMetadata>
                                                                               feignRestMethodMetadataMap) {Map<Method, FeignMethodMetadata> feignMethodMetadataMap = new HashMap<>();

        for (Map.Entry<DubboTransportedMethodMetadata, RestMethodMetadata> entry : feignRestMethodMetadataMap.entrySet()) {RestMethodMetadata feignRestMethodMetadata = entry.getValue();
            RequestMetadata feignRequestMetadata = feignRestMethodMetadata.getRequest();
            DubboRestServiceMetadata metadata = repository.get(serviceName, feignRequestMetadata);
            if (metadata != null) {DubboTransportedMethodMetadata dubboTransportedMethodMetadata = entry.getKey();
                Map<String, Object> dubboTranslatedAttributes = dubboTransportedMethodMetadata.getAttributes();
                Method method = dubboTransportedMethodMetadata.getMethod();
                GenericService dubboGenericService = dubboGenericServiceFactory.create(metadata, dubboTranslatedAttributes);
                RestMethodMetadata dubboRestMethodMetadata = metadata.getRestMethodMetadata();
                MethodMetadata methodMetadata = dubboTransportedMethodMetadata.getMethodMetadata();
                FeignMethodMetadata feignMethodMetadata = new FeignMethodMetadata(dubboGenericService,
                        dubboRestMethodMetadata, feignRestMethodMetadata);
                feignMethodMetadataMap.put(method, feignMethodMetadata);
            }
        }

        return feignMethodMetadataMap;
    }

    private static <T> T cast(Object object) {return (T) object;
    }
}
  • TargeterInvocationHandler 实现了 InvocationHandler 接口,其 invoke 方法执行 createDubboProxyIfRequired;该方法主要是创建 dubboInvocationHandler,然后进行 proxy

DubboInvocationHandler

spring-cloud-alibaba-0.9.0.RELEASE/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboInvocationHandler.java

public class DubboInvocationHandler implements InvocationHandler {

    private final Map<Method, FeignMethodMetadata> feignMethodMetadataMap;

    private final InvocationHandler defaultInvocationHandler;

    private final DubboGenericServiceExecutionContextFactory contextFactory;

    private final ClassLoader classLoader;

    public DubboInvocationHandler(Map<Method, FeignMethodMetadata> feignMethodMetadataMap,
                                  InvocationHandler defaultInvocationHandler,
                                  ClassLoader classLoader,
                                  DubboGenericServiceExecutionContextFactory contextFactory) {
        this.feignMethodMetadataMap = feignMethodMetadataMap;
        this.defaultInvocationHandler = defaultInvocationHandler;
        this.classLoader = classLoader;
        this.contextFactory = contextFactory;
    }

    @Override
    public Object invoke(Object proxy, Method feignMethod, Object[] args) throws Throwable {FeignMethodMetadata feignMethodMetadata = feignMethodMetadataMap.get(feignMethod);

        if (feignMethodMetadata == null) {return defaultInvocationHandler.invoke(proxy, feignMethod, args);
        }

        GenericService dubboGenericService = feignMethodMetadata.getDubboGenericService();
        RestMethodMetadata dubboRestMethodMetadata = feignMethodMetadata.getDubboRestMethodMetadata();
        RestMethodMetadata feignRestMethodMetadata = feignMethodMetadata.getFeignMethodMetadata();

        DubboGenericServiceExecutionContext context = contextFactory.create(dubboRestMethodMetadata, feignRestMethodMetadata, args);

        String methodName = context.getMethodName();
        String[] parameterTypes = context.getParameterTypes();
        Object[] parameters = context.getParameters();

        Object result = dubboGenericService.$invoke(methodName, parameterTypes, parameters);

        Class<?> returnType = getReturnType(dubboRestMethodMetadata);

        return realize(result, returnType);
    }

    private Class<?> getReturnType(RestMethodMetadata dubboRestMethodMetadata) {String returnType = dubboRestMethodMetadata.getReturnType();
        return ClassUtils.resolveClassName(returnType, classLoader);
    }
}
  • DubboInvocationHandler 主要是使用 dubboGenericService.&dollar;invoke 方法来执行调用

小结

  • DubboOpenFeignAutoConfiguration 注册了 TargeterBeanPostProcessor
  • TargeterBeanPostProcessor 实现了 BeanPostProcessor、BeanClassLoaderAware 接口;其 postProcessAfterInitialization 方法首先判断 classLoader 是否有 org.springframework.cloud.openfeign.Targeter,如果有则判断 beanClass 的父类是否是 targetClass,如果是则使用 Proxy.newProxyInstance 进行代理,代理的 InvocationHandler 为 TargeterInvocationHandler
  • TargeterInvocationHandler 实现了 InvocationHandler 接口,其 invoke 方法执行 createDubboProxyIfRequired;该方法主要是创建 dubboInvocationHandler,然后进行 proxy;DubboInvocationHandler 主要是使用 dubboGenericService.&dollar;invoke 方法来执行调用

doc

  • DubboOpenFeignAutoConfiguration
退出移动版