接口创建代理类发送远程RPC请求

420 阅读4分钟

最近遇到一个需求,商城那边提供一套openApi,其实就是一条rpc链路里面有多个service,而我们这边需要调用rpc方法,因为这些方法都使用的一条链路,如果我们把这个接口写一个实现类,那岂不是每一个方法都需要写实现,如果openApi又增多,我们这边变更起来非常麻烦耦合性非常高,所以就想到利用springAOP的特效动态生成一个代理对象,就类似于mybatis里的dao层,提供方又增加了新方法我们直接加一个接口就行了。

public interface YzwOrderIntegration {
    
    YzwApiResult<YzwOrderConfirmResp> confirmOrder(YzwOrderConfirm req, BoxYzwPlatformApp app);
    
    YzwApiResult<YzwOrderConfirmResp> deliveryOrder(YzwDeliveryOrder req, BoxYzwPlatformApp app);
    
    YzwApiResult<YzwOrderConfirmResp> updateOrderTrack(YzwOrderTrack req, BoxYzwPlatformApp app);
    
    YzwApiResult<YzwOrderConfirmResp> upProductStock(List<YzwProductStock> req, BoxYzwPlatformApp app);
    
    YzwApiResult<YzwOrderConfirmResp> changeProductStock(List<YzwProductStock> req, BoxYzwPlatformApp app);
}

上面的接口的入参和返回类型的基本格式,不在赘述先来自己建立一个注解方便标记需要代理的类

/**
 *打在需要代理的类上的注解  
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface YzwApiClient {
}
/**
 *打在方法的注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface YzwApiMethod {

    //默认不重试
    int DEFAULT_RETRY = -1;

     // 服务名 (eg: Order.GetSupplierOrderDetail)对方提供的service方法
    String value();

     // 重试次数
    int retry() default DEFAULT_RETRY;
}

最后变成了这样,第一步算是完成了是不是很简单,注解定义好就到了这些注解发挥作用的时候了

@YzwApiClient
public interface YzwOrderIntegration {
    
    @YzwApiMethod(value = "Order.ConfirmOrder", retry = 3)
    YzwApiResult<YzwOrderConfirmResp> confirmOrder(YzwOrderConfirm req, BoxYzwPlatformApp app);
    
    @YzwApiMethod(value = "Order.DeliveryOrder", retry = 3)
    YzwApiResult<YzwOrderConfirmResp> deliveryOrder(YzwDeliveryOrder req, BoxYzwPlatformApp app);
    
    @YzwApiMethod(value = "Order.UpdateOrderTrack", retry = 3)
    YzwApiResult<YzwOrderConfirmResp> updateOrderTrack(YzwOrderTrack req, BoxYzwPlatformApp app);
    
    @YzwApiMethod(value = "Product.UpProductStock", retry = 3)
    YzwApiResult<YzwOrderConfirmResp> upProductStock(List<YzwProductStock> req, BoxYzwPlatformApp app);
    
    @YzwApiMethod(value = "Product.ChangeProductStock", retry = 3)
    YzwApiResult<YzwOrderConfirmResp> changeProductStock(List<YzwProductStock> req, BoxYzwPlatformApp app);
}

接口定义好了,那么就该给这套API注册一下了,先写一个注册类ImportBeanDefinitionRegistrar,ResourceLoaderAware,EnvironmentAware这是spring提供的三个扩展接口分别是定义bean的实现,资源获取也就是class文件之类的,获取配置的接口。

public class YzwApiRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    private Environment environment;
    private ResourceLoader resourceLoader;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    /**
     * 扫描包并注册
     *
     * @param metadata
     * @param registry
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        //我们自己建了一个扫描类,重写了isCandidateComponent方法,因为我们会指定的包下去扫描所以不用做多余的判定
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        //获取包路径
        Set<String> basePackages = getBasePackages(metadata);
        //添加过滤  
        scanner.addIncludeFilter(new AnnotationTypeFilter(YzwApiClient.class));
        for (String basePackage : basePackages) {
            //这里会把指定包名下的class文件转换为ScannedGenericBeanDefinition
            Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(basePackage);
            for (BeanDefinition candidateComponent : beanDefinitions) {
                //ScannedGenericBeanDefinition实现了AnnotatedBeanDefinition接口
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                    //获得类上的注解数据
                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                    Assert.isTrue(annotationMetadata.isInterface(), "@YzwApiClient 注解只能指定在接口上");
                    registerApiClient(registry, annotationMetadata);
                }
            }
        }
    }

   
    protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
        Map<String, Object> attributes = importingClassMetadata
                .getAnnotationAttributes(EnableYzwApiClients.class.getCanonicalName());
        Set<String> basePackages = new HashSet<>();
        for (String pkg : (String[]) attributes.get("value")) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }
        return basePackages;
    }
}

上面scanner.findCandidateComponents(basePackage);这个方法我们看看源码是怎么做的,经过一系列判断会调用scanCandidateComponents()方法懒得简化了直接看我注释的重点:

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
            //通过我们指定的包路径扫描资源
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
                //是否可读
				if (resource.isReadable()) {
					try {
                        //获取class的元数据
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                        //我们重写的方法
						if (isCandidateComponent(metadataReader)) {
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setResource(resource);
							sbd.setSource(resource);
                            //重写的isCandidateComponent()方法吗
							if (isCandidateComponent(sbd)) {
								if (debugEnabled) {
									logger.debug("Identified candidate component class: " + resource);
								}
								candidates.add(sbd);
							}
							else {
								if (debugEnabled) {
									logger.debug("Ignored because not a concrete top-level class: " + resource);
								}
							}
						}
						else {
							if (traceEnabled) {
								logger.trace("Ignored because not matching any filter: " + resource);
							}
						}
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to read candidate component class: " + resource, ex);
					}
				}
				else {
					if (traceEnabled) {
						logger.trace("Ignored because not readable: " + resource);
					}
				}
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}

好了在回到这个方法上来registerApiClient()

 /**
     * 注册api接口
     *
     * @param registry
     * @param annotationMetadata
     */
    private void registerApiClient(BeanDefinitionRegistry registry,
                                   AnnotationMetadata annotationMetadata) {
        //获取类名
        String className = annotationMetadata.getClassName();
        //获取接口工厂创建代理类获得definitionBuilder 
        BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder
                .genericBeanDefinition(YzwApiClientFactoryBean.class);
        try {
       //给工厂添加构造参数   
        definitionBuilder.addConstructorArgValue(ClassUtils.forName(className, resourceLoader.getClassLoader()));
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException("找不到类:" + className, e);
        }
        //注入方式,按类型注入
        definitionBuilder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        //创建成功
        AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
        //允许优先注入
        beanDefinition.setPrimary(true);
        //获得holder
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className);
        //使用给定的bean工厂注册给定的bean定义
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

    private ClassPathScanningCandidateComponentProvider getScanner() {
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false, environment) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                return !beanDefinition.getMetadata().isAnnotation();
            }
        };
        scanner.setResourceLoader(this.resourceLoader);
        return scanner;
    }

现在就可以来写工厂代理类啦

@EqualsAndHashCode
public class YzwApiClientFactoryBean implements FactoryBean<Object>, InitializingBean,
        ApplicationContextAware {

    private Class<?> type;

    private ApplicationContext applicationContext;

    /**
     * 请求模板
     */
    private volatile YzwApiRequestTemplate yzwApiRequestTemplate;

    public YzwApiClientFactoryBean(Class<?> type) {
        this.type = type;
    }

    @Override
    public void afterPropertiesSet() {
        Assert.notNull(this.type, "type 不能为空");
    }

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

    @Override
    public Object getObject() {
        //代理api接口(封装请求、异常处理、结果转换)
        return Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type},
                (proxy, method, args) -> {
                    YzwApiMethod yzwApiMethod = method.getAnnotation(YzwApiMethod.class);
                    Assert.notNull(yzwApiMethod, "api请求方法未指定");
                    Type resultParameterType = null;
                    //获取接口定义返回参数枚举类型
                    if (method.getGenericReturnType() instanceof ParameterizedType) {
                        ParameterizedType pType = (ParameterizedType) method.getGenericReturnType();
                        Type[] types = pType.getActualTypeArguments();
                        if (types != null && types.length > 0) {
                            resultParameterType = types[0];
                        }
                    }
                    //代理方法返回的结果这里就可以写你想写的业务
                    return null;
                });
    }

    @Override
    public Class<?> getObjectType() {
        return this.type;
    }
    @Override
    public boolean isSingleton() {
        return true;
    }


}

最后新建一个注解,把改注解打到启动类上就OK了,记得把注册类加载进来,让spring初始化的时候回去加载它。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(YzwApiRegistrar.class)
public @interface EnableYzwApiClients {
    /**
     * 接口扫描包路径
     *
     * @return 包路径列表
     */
    String[] value();

}