从0到1实现IOC

一、什么是 IOC

我们先来看看spring官网对IOC的定义:

IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse, hence the name Inversion of Control (IoC), of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes, or a mechanism such as the Service Locator pattern.

就不逐句翻译了,主要意思就是,IOC也被称为依赖注入,它是一个容器创建bean时将依赖对象注入的过程,而这些依赖对象在该bean中仅仅通过构造参数、工厂方法的参数或属性来指定。

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低代码之间的耦合度,提高代码的可重用性和可维护性。通过控制反转,对象在被创建的时候,由一个容器将其所依赖的对象的引用传递给它,即我们常说的,依赖被注入到对象中。要自己设计实现一个IOC,首先要理解IOC的作用, 最基础且必要功能有两个:a、存储对象, b、有注解注入的功能;

二、为什么需要 IOC

在传统java项目中对象是通过new 获得的,并需要自己维护对象的生命周期,在一个大型项目中,这个将是繁杂冗余浪费机器资源的,如图(1) ,IOC就是来解决该问题的,由IOC容器来维护类的生命周期和类之间的引用,如图(2),对象的关系图也即变成图(3) 的效果,看着是否清爽多了,对人是这样,对机器也是这样

                                                                      (图1)

 

                                                   (图2)

                                                               (图3)

三、怎么实现IOC

Java语言允许通过程序化的方式间接对Class进行操作,Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数、属性和方法等。Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能,这就为使用程序化方式操作Class对象开辟了途径。下面我们着手实现一个IOC容器,先参照下最经典的Spring容器。

 

 

上图是Spring如何工作的高级视图,业务类与配置元数据相结合,这样在创建和初始化ApplicationContext(spring 容器)之后,就有了一个完全配置和可执行的系统或应用程序,Spring IOC的实现官网等都有比较详细的介绍了,这里就不做赘述。

在这里,参照spring容器视图,为了更好的理解和实现IOC我们从基础0代码出发,一步一步详细实现一个简单的通过注解的方式实现IOC容器功能, 一个IOC容器中最核心的当属容器接口,比如我们定义为:Container。那么这个容器里应该有什么呢?即需要具备什么功能呢?要能存储对象,其次包括括获取、注册、删除对象的方法,所以可以通过一个map来实现容器的核心。

1、容器接口

public interface Container {

/**
* 根据Class获取Bean
* @param clazz
* @return
*/
public <T> T getBean(Class<T> clazz);

/**
* 根据名称获取Bean
* @param name
* @return
*/
public <T> T getBeanByName(String name);

/**
* 注册一个Bean到容器中
* @param object
*/
public Object registerBean(Object bean);

/**
* 注册一个Class到容器中
* @param clazz
*/
public Object registerBean(Class<?> clazz);

/**
* 注册一个带名称的Bean到容器中
* @param name
* @param bean
*/
public Object registerBean(String name, Object bean);

/**
* 删除一个bean
* @param clazz
*/
public void remove(Class<?> clazz);

/**
* 根据名称删除一个bean
* @param name
*/
public void removeByName(String name);

/**
* @return 返回所有bean对象名称
*/
public Set<String> getBeanNames();

/**

* 初始化装配
*/
public void initWired();

}



2、容器实现
 

这里将所有Bean的名称存储在 beanKeys 这个map中,将所有的对象存储在 beans 中,用 beanKeys 维护名称和对象的关系。

在装配的时候步骤如下

public class ContainerImpl implements Container {


   /**
    * 保存所有bean对象,格式为com.test.TestA TestA@516
    */
   private Map<String, Object> beans;

   /**
    * 存储注入属性名和对象的全名称,格式testA  com.test.TestA
    */
   private Map<String, String> beanKeys;


   public ContainerImpl() {
      this.beans = new ConcurrentHashMap<String, Object>();
      this.beanKeys = new ConcurrentHashMap<String, String>();
   }

   /**
    * 根据类获取指定的对象
    * @param clazz
    * @param <T>
    * @return
    */
   @Override
   public <T> T getBean(Class<T> clazz) {
      String name = clazz.getName();
      Object object = beans.get(name);
      if(null != object){
         return (T) object;
      }
      return null;
   }

   /**
    * 获取指定的对象
    * @param name 对象名称
    * @param <T>
    * @return
    */
   @Override
   public <T> T getBeanByName(String name) {
      String className = beanKeys.get(name);
      Object obj = beans.get(className);
      if(null != obj){
         return (T) obj;
      }
      return null;
   }

   /**
    * 通过对象的方式进行注册
    * @param bean
    * @return
    */
   @Override
   public Object registerBean(Object bean) {
      String name = bean.getClass().getName();
      String lastName = name.substring(name.lastIndexOf(".")+1);
      lastName = StringUtils.uncapitalize(lastName);
      beanKeys.put(lastName, name);
      beans.put(name, bean);
      return bean;
   }

   /**
    * 通过类方式进行注册
    * @param clazz
    * @return
    */
   @Override
   public Object registerBean(Class<?> clazz) {
      String name = clazz.getName();
      String lastName = name.substring(name.lastIndexOf(".")+1);
      lastName = StringUtils.uncapitalize(lastName);
      beanKeys.put(lastName, name);
      Object bean = null;
      try {
         bean = ReflectUtil.newInstance(clazz);
         beans.put(name, bean);
      }catch (Exception e) {
         return null;
      }
      return bean;
   }

   /**
    * 通过指定对象名称方式进行注册
    * @param name
    * @param bean
    * @return
    */
   @Override
   public Object registerBean(String name, Object bean) {
      String className = bean.getClass().getName();
      beanKeys.put(name, className);
      beans.put(className, bean);
      return bean;
   }

   /**
    * 获取对象名称集合
    * @return
    */
   @Override
   public Set<String> getBeanNames() {
      return beanKeys.keySet();
   }

   /**
    * 根据类卸载一个对象
    * @param clazz
    */
   @Override
   public void remove(Class<?> clazz) {
      String className = clazz.getName();
      if(null != className && !className.equals("")){
         beanKeys.remove(className);
         beans.remove(className);
      }
   }

   /**
    * 指定卸载某个对象
    * @param name
    */
   @Override
   public void removeByName(String name) {
      String className = beanKeys.get(name);
      if(null != className && !className.equals("")){
         beanKeys.remove(name);
         beans.remove(className);
      }
   }

   /**
    * 将注册的bean通过循环的方式注入的容器中,后续通过容器进行使用各个bean对象
    */
   @Override
   public void initWired() {
      Iterator<Entry<String, Object>> it = beans.entrySet().iterator();
      while (it.hasNext()) {
         Map.Entry<String, Object> entry = (Map.Entry<String, Object>) it.next();
         Object object = entry.getValue();
         injection(object);
      }
   }

   /**
    * 本例子是展示了,通过注解的方式进行注入
    * @param object
    */
   public void injection(Object object) {

      try {
         // 所有字段
         Field[] fields = object.getClass().getDeclaredFields();
         for (Field field : fields) {
            // 需要注入的字段,找到注解的对象(@autowired注解)
//          Autowired autoWired = field.getAnnotation(Autowired.class);
            // 需要注入的字段,找到注解的对象(@Resource注解)
            Resource autoWired = field.getAnnotation(Resource.class);
            if (null != autoWired) {

               // 要注入的字段
               Object autoWiredField = null;

               String name = field.getName();

               String className = beanKeys.get(name);
               if (StringUtils.isNotBlank(className)) {
                  autoWiredField = beans.get(className);
               }

               if (autoWiredField == null) {
                  throw new RuntimeException("Unable to load " + field.getType().getCanonicalName());
               }

               boolean accessible = field.isAccessible();
               field.setAccessible(true);
               field.set(object, autoWiredField);
               field.setAccessible(accessible);
            }
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}


3、测试类

public class IocTest {

private static Container container = new ExampleContainer();

public static void baseTest(){

// 将TestB注册到容器中(通过类名方式)
container.registerBean(TestB.class);

// 将TestA注册到容器中(通过对象方式)
TestA a = new TestA();
container.registerBean(a);

// 进行初始化(依赖注入)
container.initWired();

// 获取bean
TestB test = container.getBean(TestB.class);

// 执行对应bean里的方法
test.doTest();
}

public static void main(String[] args) {
baseTest();
}
}

4、测试BEAN

public class TestA {
   
   public void work(){
      System.out.println("TestA executes,implment IOC...");
   }
}
public class TestB {

   /**
    * TestB中注入TestA
    */
   @Autowired
   private TestA testA;

   public void doTest (){
      System.out.println("testB executes...");
      testA.work();
   }

}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现IOC的设计模式有多种方式,其中比较常见的方式是使用依赖注入(Dependency Injection)来实现。依赖注入是指通过将对象的依赖关系从程序内部提到外部来管理。通过依赖注入,我们可以将对象的创建和依赖关系的管理交给IOC容器来处理,从而实现对象的解耦和灵活性。 在实现依赖注入时,可以使用不同的方式,如构造函数注入、属性注入和方法注入等。构造函数注入是最常见的方式,通过在对象的构造函数中接收依赖对象的参数来实现注入。属性注入是通过在对象的属性上标记注入的依赖对象来实现注入。方法注入是通过在对象的方法中接收依赖对象的参数来实现注入。 除了依赖注入,还可以使用服务定位器(Service Locator)来实现IOC。服务定位器是一个中心化的容器,用于管理对象的创建和依赖关系的解析。通过服务定位器,我们可以通过名称或类型来获取所需的对象,而不需要显式地指定依赖关系。 另外,还可以使用IOC容器框架来实现IOC。常见的IOC容器框架有Autofac、Unity、Spring等。这些框架提供了一套完整的IOC容器,可以方便地管理对象的创建和依赖关系的解析。 总结起来,实现IOC的设计模式可以通过依赖注入、服务定位器或使用IOC容器框架来实现。具体选择哪种方式取决于项目的需求和个人的偏好。 #### 引用[.reference_title] - *1* *3* [IOC设计模式](https://blog.csdn.net/C_gyl/article/details/109488822)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [设计模式之——IOC](https://blog.csdn.net/u012539826/article/details/103071473)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值