版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_24871519/article/details/85066838
实现服务的自动注入
前言
最近在自己写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 对象。
如何实现?
主要分为下面两步:
找到被注解标注的field
想法:在每个Bean 初始化完成之后,遍历其包含的field, 如果是被 @MRpcInject 注解的,则是我们所找的field。
BeanPostProcessor提供了两个方法:postProcessBeforeInitialization和postProcessAfterInitialization,主要针对bean初始化提供扩展。
- postProcessBeforeInitialization() 会在每一个bean实例化之后、初始化(如afterPropertiesSet方法)之前被调用。
- 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 中注入代理类
这里主要使用field 提供的set() 方法:
扫描二维码关注公众号,回复:
4942037 查看本文章
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。此项目为本人在学习一些知识后自己动手的产物,目前还存在很多问题,正在不断的完善中,有想学习相关内容的小伙伴可以一起看看!!!