通过简单实现来理解控制反转(IOC)和依赖注入(DI)

IOC (控制反转Inversion of control)和依赖注入(DI)是Spring的一个重要特性,这里试图通过一个简单的例子来理解这个特性。

IOC是指控制权的转移。举例来说,在一个一般的类中,如果我们需要用到一些其他对象(不是由构造方法传递,或者从父类继承),正常情况下要使用它们,首先应该获取它,否则就无法使用。
这在一段不长的代码中自然没有什么问题,人脑可以轻松处理这些问题.然而随着开发进行创建的东西越来越多,各个部分的依赖会变得越来越复杂,处理这些就会非常的困难.

我们自然不想处理这些复杂的关系,这会让人的脑袋爆炸的.而这种问题由计算机来处理是很轻松的.IOC就是处理这种问题的方法.

我们想要达到的目的是:我们不需要考虑对象是怎么创建的,只要需要用到对象的时候从容器中获取一个就行。
Spring就是这样一个容器,在创建一个ApplicationContext时,我们从一个外部xml配置文件中读取配置文件,创建其指定的对象,并把它放到容器中去。需要使用对象的时候直接从容器中获取,不需要重复创建。

下面我们来实现一个类似Spring的简单容器,读取类路径下的配置文件,创建容器,在使用对象的时候直接从容器中获取。

首先新建两个我们想要获取的简单的类

public class MyOrder {
    
    private OrderDao orderDao;

    public void print(){
        System.out.println("print");
        orderDao.select;
    }
}
public class OrderDao {
    public void select(){
        System.out.println("select");
    }
}

新建一个properties配置文件,在里面配置这两个类的bean名称和类全名。

myOrder=cn.acgq.MyOrder
orderDao=cn.acgq.OrderDao

接下来就是创建容器了,创建了一个名为BeanFactory的容器,代码如下:

public class BeanFactory {
    //使用Map来存放beanName和Instance映射
    private static Map<String, Object> beans = new HashMap<>();
    
    //创建bean名字和类的映射
    static {
        //加载配置文件
       	try {
          properties.load(BeanFactory.class.getResourceAsStream("/config.properties"));
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
          properties.load(this.class.getResourceAsStream("/config.properties"));
          properties.forEach((bean, beanClass) -> {
                try {
                    //根据配置文件创建bean,将其放到Map中去
                    Class clazz = Class.forName((String) beanClass);
                    Object beanInstance = clazz.getConstructor().newInstance();
                    beans.put((String) bean,beanInstance);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }  
            });
        //依赖注入
//         beans.forEach((beanName,beanInstance) -> 	
//                      dependencyInjection(beanName,beanInstance,beans));
   
    }
    
   public static Object getBean(String beanName){
       return beans.get(beanName);
   }
}

通过java的反射,我们实现了根据类名创建实例,这样我们就能从beanFactory中根据名称获取bean了。

MyOrder myOrder= (MyOrder) BeanFactory.getBean("myOrder");

当然到这里还没有结束,这里新创建的实例里成员变量为空,如果执行print方法会报错。我们还需要把beanFactory中生成的OrderDao注入MyOrder。

这里我们使用注解的方式进行,新建一个注释,表示这个成员变量需要自动注入。

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

在MyOrder中的OrderDao上添加注释

@Resources
private OrderDao orderDao;

新建方法 dependencyInjection进行注入

private static void dependencyInjection(String beanName, Object beanInstance,Map<String,Object> beans) {
    	//过滤出需要注入的字段
        List<Field> fieldToBeAutowired = 
        Stream.of(beanInstance.getClass().getDeclaredFields())
                .filter(field -> field.isAnnotationPresent(Resources.class))
                .collect(Collectors.toList());
        //进行注入
        fieldToBeAutowired.forEach(field -> {
            String fieldName=field.getName();
            field.setAccessible(true);//private字段需要进行这一操作
            Object dependencyBeanInstance = beans.get(fieldName);
            try {
                //设置field
                field.set(beanInstance,dependencyBeanInstance);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        });
    }

到这里就完成了一个简单的IOC容器,来测试一下吧

public class TestIOC {
    public static void main(String[] args) {
        MyOrder myOrder= (MyOrder) BeanFactory.getBean("myOrder");
        myOrder.select();
    }
}

输出结果如下

MyOrder select
orderDao

当然了,Spring肯定不是这么简单的实现,它在实现IOC的时候做了大量的事,代码里有非常多的细节处理。这里只是用一个简单的例子理解IOC和DI的实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值