如何实现依赖注入功能

从学习Java框架之后Spring就一直是Java视野中时时刻刻存在的东西,Spring最为强大的特性就是IOC和AOP,今天先说下IOC,从刚开始对IOC懵懵懂懂到了解Spring框架,再到熟练使用Spring,到Spring框架的一些特性解读,也是经历了很长时间,不说Spring,看我们怎么来实现IOC。IOC,通俗的说是依赖注入,可能还有其他的说法,但是在日常开发中使用它就是代替开发者初始化bean,自动的创建实例对象的。

  • 来个例子吧

有两个类,UserController/UserService,我们在UserController中要使用UserService的方法,应该怎嘛办呢?

  • 调用的UserService方法是static的
  • 创建UserService的实例,再调用方法

我们采用第二种方法来实现:

public class UserController {
    
    private UserService userService;
    
    public UserController(){
        userService = new UserService();
    }
    
    public void sayHello(){
      //调用userService的sayHello方法
        userService.sayHello();
    }
}

一、我们如何实现IOC

我们最终想要的结果是这样的:

public class UserController {
    
    @Inject
    private UserService userService;
    
    public void sayHello(){
        userService.sayHello();
    }
}

如何实现?

  • 使用一个Map<Class<?>, Object>结构的Map用来存储所有类和实例的映射关系
  • 遍历Map,分别取出bean类和bean实例
  • 通过反射取出类中的成员变量
  • 遍历成员变量,查看是否带有inject注解(自己新建的注解)
  • 若有该注解,则从Map中根据bean类取出bean实例
  • 再通过反射修改当前成员变量的值

新建inject注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {
}

主体方法

public final class IocHelper {

    static {
       //获取bean映射关系Map
        Map<Class<?>, Object> beanMap = BeanHelper.getBeanMap();
        if (CollectionUtil.isNotEmpty(beanMap)) {
            for (Map.Entry<Class<?>, Object> beanEntry : beanMap.entrySet()) {
                 //获取bean类
                Class<?> beanClass = beanEntry.getKey();
                //获取bean实例
                Object beanInstance = beanEntry.getValue();
                //使用反射获取成员
                Field[] beanFields = beanClass.getDeclaredFields();
                if (ArrayUtil.isNotEmpty(beanFields)) {
                    for (Field beanField : beanFields) {
                         //判断成员中是否又inject注解
                        if (beanField.isAnnotationPresent(Inject.class)) {
                            //在Map中找到Bean Field对应的实例
                            Class<?> beanFieldClass = beanField.getType();
                            Object beanFieldInstance = beanMap.get(beanFieldClass);
                            if (beanFieldInstance != null) {
                                //通过反射初始化bean Field的值
                                ReflectionUtil.setField(beanInstance, beanField, beanFieldInstance);
                            }
                        }
                    }
                }
            }
        }
    }
}

当然,这个类必须是在web环境下才可以运行起来,其中包含了一些其他的工具方法,没有列出来,但是我想我给出一个简单的test示例大家就理解了:

UserService

public class UserService {

    public void sayHello(){
        System.out.println("say hello");
    }
}

UserController

public class UserController {

    @Inject
    private UserService userService;

    public void sayHello(){
        userService.sayHello();
    }
}

测试方法

@Test
    public void testClass() throws Exception {
        Class<?> cls = Class.forName("com.sailfish.UserController", false, Thread.currentThread().getContextClassLoader());
        Object o1 = cls.newInstance();
        Field field = cls.getDeclaredField("userService");
        Class<?> userServiceClass = field.getType();
        //创建userService的实例
        Object o2 = userServiceClass.newInstance();
        field.set(o1, o2);
        ((UserController)o1).sayHello();//say hello
    }
  • 得到UserController的Class
  • 获得UserController的实例
  • 得到UserController的field:userService
  • 根据Field获得UserService类
  • 创建UserService实例
  • 使用反射设置UserController的成员变量userService为UserService实例
  • 调用UserController的sayHello方法

总结:其实有些看起来很难的知识点只是在表面看起来难,了解之后其中的原理还是不难理解,spring的Ioc实现肯定不是这么简单,但是其背后的原理还是类似的。

    原文作者:橙小张
    原文地址: https://www.jianshu.com/p/7f82da317a28
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞