最近遇到一个需求,商城那边提供一套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();
}