最近在自己写rpc 框架,目前已完成服务注册发布、注册中心、服务发现功能,Github 地址为:https://github.com/caigoumiao/mrpc
在做服务的消费端时,想要实现服务代理的自动注入,不用每次调用服务都暴露代理的生成细节,就像这样:(一个注解搞定)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface MRpcInjection
{
}
@Component
public class TopService
{
@MRpcInjection
private TestService testService;
public void callTestService()
{
User u = testService.getUser(21);
System.out.println(u);
}
}
通过一个注解 @MRpcInject 实现字段testService 自动注入Proxy 对象。
主要分为下面两步:
想法:在每个Bean 初始化完成之后,遍历其包含的field, 如果是被 @MRpcInject 注解的,则是我们所找的field。
BeanPostProcessor提供了两个方法:postProcessBeforeInitialization和postProcessAfterInitialization,主要针对bean初始化提供扩展。
所以我们自定义Spring 的BeanPostProcessor,然后在postProcessAfterInitialization() 中判断其包含字段是否有@MRpcInject 注解即可。
具体实现:
public class ClientPostProcessor implements BeanPostProcessor
{
private Logger log = LoggerFactory.getLogger(this.getClass());
private ServiceImporter serviceImporter;
@Override
public Object postProcessAfterInitialization(Object bean , String beanName) throws BeansException
{
log.info("Bean[" + beanName + "] initialized ...");
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields)
{
// 当字段被 @MRpcInjection 注解时,为此字段自动注入服务代理类
if (field.getAnnotation(MRpcInjection.class) != null)
{
// 找到指定注解的字段,准备注入代理类
}
}
return bean;
}
}
这里主要使用field 提供的set() 方法:
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
getFieldAccessor(obj).set(obj, value);
}
第一个参数为字段所在对象,第二个参数为注入的值。
具体实现如下:
// 首先得先设置该字段可访问
field.setAccessible(true);
// 根据服务类型生成服务的动态代理
Class> serviceClass = field.getType();
Object serviceProxy = serviceImporter.importService(serviceClass);
// 将该字段值设置为生成的代理类
field.set(bean , serviceProxy);
在确认服务已发布的情况下,客户端服务调用:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ClientConfig.class);
context.getBean(TopService.class).callTestService();
输出结果:(服务提供者的实现结果)
User(name=miao, age=21)
结果表明TestService 已正确注入!!!
查看完整代码,见https://github.com/caigoumiao/mrpc。此项目为本人在学习一些知识后自己动手的产物,目前还存在很多问题,正在不断的完善中,有想学习相关内容的小伙伴可以一起看看!!!