nats java rpc 实现类似EnableFeignClients模式
以前我简单介绍过基于nats rpc 实现类似feign 模式的访问,但是使用中并不如spring cloud feign 的方便,所以说明下如果实现类似EnableFeignClients的模式,实现bean 自动注册
实现机制
- 原理
基本机制类似EnableFeignClients,通过import 导入一个ImportBeanDefinitionRegistrar 实现,基于bean 的自动注册基于了以前client的机制,具体是自己实现一个FactoryBean
- EnableNatsRpcClients 实现
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(NatsRpcClientsRegistrar.class)
@Documented
public @interface EnableNatsRpcClients {
String[] basePackages() default {};
}
- NatsRpcClientsRegistrar
public class NatsRpcClientsRegistrar implements ImportBeanDefinitionRegistrar {
protected ClassPathScanningCandidateComponentProvider getScanner() {
return new ClassPathScanningCandidateComponentProvider(false) {
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
boolean isCandidate = false;
if (beanDefinition.getMetadata().isIndependent()) {
if (!beanDefinition.getMetadata().isAnnotation()) {
isCandidate = true;
}
}
return isCandidate;
}
};
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> attributes = importingClassMetadata
.getAnnotationAttributes(EnableNatsRpcClients.class.getName());
String[] basePackages = (String[]) attributes.get("basePackages");
if (basePackages.length == 0) {
basePackages = new String[]{importingClassMetadata.getClassName().substring(0,
importingClassMetadata.getClassName().lastIndexOf("."))};
}
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.addIncludeFilter(new AnnotationTypeFilter(RpcClient.class,true));
for (String basePackage : basePackages) {
scanner.findCandidateComponents(basePackage).forEach(beanDef -> {
try {
Class<?> clazz = Class.forName(beanDef.getBeanClassName());
BeanDefinitionBuilder builder =
BeanDefinitionBuilder.genericBeanDefinition(NatsRpcClientFactoryBean.class);
builder.addConstructorArgValue(clazz);
builder.addConstructorArgReference("connection");
builder.addConstructorArgReference("objectMapper");
String beanName = clazz.getSimpleName();
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
});
}
}
}
- NatsRpcClientFactoryBean 实现
直接复用了以前的RpcServiceClient
public class NatsRpcClientFactoryBean<T> implements FactoryBean<T> {
private final Class<T> rpcInterface;
private final Connection connection;
private final ObjectMapper objectMapper;
public NatsRpcClientFactoryBean(Class<T> rpcInterface, Connection connection, ObjectMapper objectMapper) {
this.rpcInterface = rpcInterface;
this.connection = connection;
this.objectMapper = objectMapper;
}
@Nullable
@Override
public T getObject() throws Exception {
T serviceClient = RpcServiceClient.builder().objectMapper(objectMapper)
.connection(connection)
.build().target(rpcInterface);
return serviceClient;
}
@Nullable
@Override
public Class<?> getObjectType() {
return rpcInterface;
}
}
参考使用
就是类似enablefeign 模式
- 参考示例
@SpringBootApplication
@EnableNatsRpcClients(basePackages = {
"com.demo.rpc"
})
- rpc 定义
@RpcClient(
serviceName = "authservice",
servicePrefix = "global",
serviceEndpoint = "tenantauthservicev2"
)
public interface TenantAuthApi {
List<ResponseMessage<String>> auth(Message message);
List<String> getRoles(Message message, Headers headers);
List<String> getRoles(Message message);
List<String> delteRoles(Message message, Headers headers);
List<String> updateRoles(Message message, Headers headers);
Object createTenant(MyTenantModel message, Headers headers);
Object selectTenant(MyTenantModel message, Headers headers);
List<ResponseMessage<String>> auth(Message message, Headers headers);
List<ResponseMessage<String>> auth(String prefix,Message message);
List<ResponseMessage<String>> auth(String prefix,Message message, Headers headers);
}
说明
以上是一个简单的使用,主要是通过类似feign 模式实现业务的快速开发使用,比较有意思的地方是ClassPathScanningCandidateComponentProvider ,我使用了feign 的,isCandidateComponent 方法还是很重要的,否则会有扫描不到的问题
浙公网安备 33010602011771号