自定义注解@MRpcInjection,实现服务的自动注入

文章目录

  • 实现服务的自动注入
      • 前言
      • 如何实现?
          • 找到被注解标注的field
          • 向field 中注入代理类
      • 测试一下
      • 后记

实现服务的自动注入

前言

最近在自己写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() 方法:

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。此项目为本人在学习一些知识后自己动手的产物,目前还存在很多问题,正在不断的完善中,有想学习相关内容的小伙伴可以一起看看!!!

你可能感兴趣的:(java,进阶,rpc,自定义注解,自动注入实现)