Spring FactoryBean + JDK 动态代理抽象模块

发布于:2024-05-06 ⋅ 阅读:(29) ⋅ 点赞:(0)

这篇博客是对业务中的一次重构做的总结,这个项目我放在个人博客:https://github.com/cyk110/depcloud

如果有更好的思路,欢迎留言讨论

业务场景

在业务中有两种场景:

  • 云地合设场景:直接走注册中心服务发现,即直接走 Feign 请求地端
  • 云地分离场景:需要走 API Gateway(即 API 网关) 代理到地端

由于这段判断走合设或分离的代码写在某个服务中,实际业务有时候不得不多走一次 rest 请求,为了节省这一次 rest 请求的开销,我认为可以抽象出代理模块,增强 Feign 请求地端的能力

实现思路

Feign 的原理是帮我们生成一个请求的 bean,只要能代理这个 bean,我们就能增强它的能力,自然而然就会想到动态代理。那生成的动态代理 bean 如何给业务呢?只需要一个代理工厂,这个东西在 Spring 中就是 FactoryBean

proxy 模块

首先实现 proxy 模块,任何需要云地合设/分离能力的服务,直接引入该模块

APIGatewayProxyFactoryBean

APIGatewayProxyFactoryBean 作为生产这个动态代理 bean 的工厂,直接实现 Spring 的 FactoryBean 接口

@RequiredArgsConstructor
public class APIGatewayProxyFactoryBean<T> implements FactoryBean<T> {

    private final Object target;
    private final Class<?> proxyInterface;

    @Override
    @SuppressWarnings("unchecked")
    public T getObject() throws Exception {
        APIGatewayProxy apiGatewayProxy = new APIGatewayProxy(target);
        return (T) Proxy.newProxyInstance(proxyInterface.getClassLoader(), new Class[] {proxyInterface},
            apiGatewayProxy);
    }

    @Override
    public Class<?> getObjectType() {
        return Proxy.class;
    }
}
APIGatewayProxy

APIGatewayProxy 实现 InvocationHandler 接口,作为动态代理的实现类,在这里就可以写真正的增强后的逻辑

@RequiredArgsConstructor
public class APIGatewayProxy implements InvocationHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(APIGatewayProxy.class);

    private final Object target;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        LOGGER.info("before invoke");
        Object result = method.invoke(target, args);
        LOGGER.info("after invoke");
        return result;
    }
}

业务模块

业务模块通过注入 APIGatewayProxyFactoryBean,获得增强后的动态代理 bean

    @Bean(name = "groundRemoteServiceProxy")
    APIGatewayProxyFactoryBean<GroundRemoteService> apiGatewayProxyFactoryBean(GroundRemoteService groundRemoteService) {
        return new APIGatewayProxyFactoryBean<>(groundRemoteService, GroundRemoteService.class);
    }

总结

这套思路,个人感觉还是比较直观的,没有绕什么弯子。但是这里遗留了一个问题,如果我想通过 Spring Boot 自动装配直接把动态代理类注入到业务模块,应该怎么做?

我的一个想法是通过 SPI 机制,但是这样会引入模块间循环依赖的问题