Java手写实现IOC

什么是依赖注入?

平常的Java开发中,程序员在某个类中需要依赖其它类的方法。我们通常是new一个依赖类再调用类实例的方法set进去,这种开发存在的问题是new的类实例不好统一管理。所以Spring提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过Spring容器帮我们new指定实例并且将实例注入到需要该对象的类中。

依赖注入的另一种说法是"控制反转"。通俗的理解是:平常我们new一个实例,这个实例的控制权是我们程序员而控制反转是指new实例工作不由我们程序员来做而是交给Spring容器来做。
注意在这篇文章中我们用注解的方法去模拟,采用懒加载方式。
其中涉及到一个扫描包中其他Java类的扫描工具类
模拟的思路如下:
Java手写实现IOC_第1张图片

  • @Component注解类:用来注解需要哪些类的定义信息,被扫描生成BeanDefination,可以拥有别名。
  • @Autowired注解类:用来注解依赖类成员,表示该成员需要注入,可以根据别名注入,可以生成单例或非单例的实例;
  • @Qualifier注解类:当某个要被注入的成员是接口或者父类时,确定要注入哪个子类,ImplementationClass记录父类名称。
  • BeanDefinition:记录一个类的信息,包含三个成员:klass是类的类型,object是记录已经注入的实例,isinject表示是否已经注入,它的作用有两个,一是在getbean时不会再次依赖注入,二是巧妙解决循环依赖。
  • BeanFactory:顾名思义就是生产bean的地方了,我这里实现的比较粗糙,将扫描和依赖注入均放在这里,它有两个成员beanPool:用来存放BeanDefinition,alias用来存放别名,beanfactory会先扫描包启动容器,然后在调用getBean时进行依赖注入(这里的依赖注入实际就是用递归表达出来的)。

具体代码

  • 三个注解类:
package annotation;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(TYPE)
public @interface Component {
   String name() default "";
}
package annotation;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(FIELD)
public @interface Autowired {
   String name() default "";
   boolean isSinglton() default true;
}
package annotation;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 如果要向接口注入,需要确定注入接口实现类的哪一个
 * @author quan
 * @create 2020-06-25 12:52
 */

@Retention(RUNTIME)
@Target(FIELD)
public @interface Qualifier {
    String ImplementationClass() default "";
}
  • BeanDefinition: 类的定义,以及生成的单例也会包含在这里
package core;

/**
 * 类的定义
 */
public class BeanDefinition {
   private Class<?> klass;
   private Object object;
   private boolean inject;

   public BeanDefinition() {
   }

   protected Class<?> getKlass() {
      return klass;
   }
   
   protected void setKlass(Class<?> klass) {
      this.klass = klass;
   }
   
   protected Object getObject() {
      return object;
   }
   protected Object getPropertyObject() throws IllegalAccessException, InstantiationException {
      return klass.newInstance();
   }
   
   protected void setObject(Object object) {
      this.object = object;
   }

   protected boolean isInject() {
      return inject;
   }

   protected void setInject(boolean inject) {
      this.inject = inject;
   }

   @Override
   public String toString() {
      return "[klass=" + klass.getSimpleName() + ", object=" + object + "]";
   }
}
  • BeanFactory:重要类
package core;

import annotation.Autowired;
import annotation.Component;
import annotation.Qualifier;
import exception.HasNoBeanException;
import exception.InJectFailException;
import util.PackageScanner;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 
 * 拥有三大功能
 * 1.scanBeanByPackage:启动容器
 * 2.injectproperties:依赖注入
 * 3.getbean:获得实例
 */
public class BeanFactory {
   private final Map<Class<?>, BeanDefinition> beanPool;
   private final Map<String, Class<?>> alias;

   static {

   }
   
   public BeanFactory() {
      beanPool = new HashMap<Class<?>, BeanDefinition>();
      alias = new HashMap<String, Class<?>>();
   }
   
   public void scanBeanByPackage(String packageName) {
      new PackageScanner() {

         @Override
         public void dealClass(Class<?> klass) {
            if (klass.isPrimitive()
               || klass.isArray()
               || klass.isEnum()
               || klass.isAnnotation()
               || klass.isInterface()
               || !klass.isAnnotationPresent(Component.class)) {
               return ;
            }
            Object object = null;
            try {
               Component component = klass.getAnnotation(Component.class);
               String alia = component.name();
               if (alias.size() == 0) {
                  alia = klass.getSimpleName();
               }
               alias.put(alia, klass);
               object = klass.newInstance();
               BeanDefinition bd = new BeanDefinition();
               bd.setKlass(klass);
               bd.setObject(object);

               beanPool.put(klass, bd);
            } catch (InstantiationException e) {
               e.printStackTrace();
            } catch (IllegalAccessException e) {
               e.printStackTrace();
            }
         }
      }.packageScanner(packageName);

   }

   /**
    * //依赖注入重点方法:在注入成员时递归注入成员的成员
    * @param bd
    * @throws InJectFailException
    */
   private void injectProperties(BeanDefinition bd) throws InJectFailException {
      System.out.println("注入" + bd.getKlass().getName() + "的成员");
      Class<?> klass = bd.getKlass();
      Object object = bd.getObject();
      System.out.println(klass);
      Field[] fileds = klass.getDeclaredFields();
      for (Field field : fileds) {
         if (!field.isAnnotationPresent(Autowired.class)) {
            continue;
         }
         Class<?> prototyklass = field.getType();
         Object value = null;
         BeanDefinition beanDefinition = getBeanDefinition(prototyklass);
         //如果取出的类的定义是null,说明实要去找真正实现类
         if (beanDefinition == null) {
            //如果指定了其要取那个类就按照那个去取
            if (field.isAnnotationPresent(Qualifier.class)) {
               //如果找不到,去找它的父类是不是某个接口
               BeanDefinition[] beans = searchBeanBySuperType(prototyklass);
               if (beans.length == 1) {
                  value = beans[0].getObject();
               }
               //存在@Qualifier注解的成员才需要去找它的接口实现类,若没有直接抛出异常
               //若存在的话一个实现类那么就将这个类注入,若存在多个实现类,根据@Qualifier注解中value的值来确定是哪个实现类
               if (beans.length <= 0) {
                  throw new InJectFailException("类" + bd.getKlass() + "的成员" + field.getName() + "注入失败!");
               } else {
                  Qualifier qualifier = field.getAnnotation(Qualifier.class);
                  String beanName = qualifier.ImplementationClass();
                  beanDefinition = getBeanDefinition(beanName);
                  if (beanDefinition == null) {
                     throw new InJectFailException("类" + bd.getKlass() + "的成员" + field.getName() + "注入失败!");
                  }
                  //继续调用实现类进行注入
                  value = getBean(beanDefinition.getKlass());
               }
            }
            //如果不是接口类,则去递归getBean
         } else {
            value = getBean(field.getType());
         }

         Autowired autowired = field.getAnnotation(Autowired.class);
         boolean isSinglton = autowired.isSinglton();
         //如果是单例
         value = dealSinglton(isSinglton, value, prototyklass, field);
         //最后给成员赋值
         try {
            field.setAccessible(true);
            field.set(object, value);
         } catch (IllegalAccessException e) {
            throw new InJectFailException("成员" + field.getName() + "注入失败");
         }
      }
   }

   /**
    * 处理单例情况
    * @param isSinglton
    * @param value
    * @param prototyklass
    * @param field
    * @return
    * @throws InJectFailException
    */
   private Object dealSinglton(boolean isSinglton, Object value, Class<?> prototyklass, Field field) throws InJectFailException {
      if (!isSinglton) {
         try {
            value = beanPool.get(prototyklass).getPropertyObject();
            if (value == null) {
               throw new HasNoBeanException("类["
                     + prototyklass.getName() + "]的成员[" + field.getName() + "]没有对应的Bean");
            }
         } catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) {
            throw new InJectFailException("成员" + field.getName() + "注入失败");
         }
      }
      return value;
   }

   //寻找实现它的父类或者继承类
   private BeanDefinition[] searchBeanBySuperType(Class<?> klass) {
      List<BeanDefinition> beanList = new ArrayList<>();

      for (BeanDefinition bean : beanPool.values()) {
         Class<?> beanClass = bean.getKlass();
         if (klass.isAssignableFrom(beanClass)) {
            beanList.add(bean);
         }
      }
      BeanDefinition[] beans = new BeanDefinition[beanList.size()];
      beanList.toArray(beans);

      return beans;
   }

   /**
    * 在这里巧妙的解决了循环依赖(在文章的后面细祥 )
    * @param klass
    * @param 
    * @return
    * @throws InJectFailException
    */
   public <T> T getBean(Class<?> klass) throws InJectFailException {
      BeanDefinition bd = getBeanDefinition(klass);
      if (bd == null) {
         throw new HasNoBeanException("类["
               + klass.getName() +  "]没有对应的Bean");
      }

      Object object = bd.getObject();

      if (!bd.isInject()) {
         //解决了循环依赖的关键
         bd.setInject(true);
         //注入成员变量
         injectProperties(bd);
      }
      return (T) object;
   }
   
   public <T> T getBean(String className) {
      try {
         return getBean(alias.get(className));
      } catch (InJectFailException e) {
         return null;
      }
   }
   
   BeanDefinition getBeanDefinition(String className) {
      return beanPool.get(alias.get(className));
   }
   
   BeanDefinition getBeanDefinition(Class<?> className) {
      return beanPool.get(className);
   }
}

以上就是IOC/DI的思想模拟实现,其实本质上还是反射机制
接下来详解如何巧妙的解决循环依赖:
举个栗子:现在有A依赖B,B依赖C,C依赖A,那在我们依赖创建到C时便又去找A,A还是显示没有好,那么久形成循环依赖
而这里巧妙的解决方法就是在生成A时就将它设置为已经注入,如下图所示就成功解决了循环依赖:

Java手写实现IOC_第2张图片
其实还应该有一个注解是@bean注解,用来解决假如该类无法加注解怎么办,比如该类是一个jar包中的类,那么我们的解决方案就是用这个bean注解,这个解决方法可以看博主写的这一篇:Bean方法注入),

这里我们来做测试:

代码测试

  • 测试A->B->C->A
package test;

import annotation.Autowired;
import annotation.Component;

/**
 * @author quan
 * @create 2020-06-25 11:53
 */
@Component
public class ClassOne {

    @Autowired
    private ClassTwo classTwo;
}

package test;

import annotation.Autowired;
import annotation.Component;

/**
 * @author quan
 * @create 2020-06-25 11:53
 */
@Component
public class ClassTwo {

    @Autowired
    public ClassThree ClassThree;
}

package test;

import annotation.Autowired;
import annotation.Component;
import annotation.Qualifier;

/**
 * @author quan
 * @create 2020-06-25 12:45
 */
@Component
public class ClassThree {
 
@Autowired
private ClassOne classOne;
}

public static void main(String[] args) {
    BeanFactory beanFactory = new BeanFactory();
    beanFactory.scanBeanByPackage("test");
    try {
        ClassThree three = beanFactory.getBean(ClassThree.class);

        System.out.println(three);
    } catch (InJectFailException e) {
        e.printStackTrace();
    }
}

结果如下:
Java手写实现IOC_第3张图片

  • 测试@Qualifier
    A1 extends A, A2 extendsA, Classthree->A(实际注入A1), A1->ClassOne
package test;

/**
 * @author quan
 * @create 2020-06-25 16:24
 */
public class A {
}

package test;

import annotation.Autowired;
import annotation.Component;

/**
 * @author quan
 * @create 2020-06-25 16:24
 */
@Component
public class A1 extends A {

    @Autowired(isSinglton = false)
    public ClassOne classOne;
}

package test;

import annotation.Component;

/**
 * @author quan
 * @create 2020-06-25 16:24
 */
@Component
public class A2 extends A {
}

package test;

import annotation.Autowired;
import annotation.Component;

/**
 * @author quan
 * @create 2020-06-25 11:53
 */
@Component
public class ClassOne {

    @Autowired
    private ClassTwo classTwo;
}

package test;

import annotation.Autowired;
import annotation.Component;
import annotation.Qualifier;

/**
 * @author quan
 * @create 2020-06-25 12:45
 */
@Component
public class ClassThree {
    @Autowired
    @Qualifier(ImplementationClass = "A1")
    public  A a;

}


public static void main(String[] args) {
    BeanFactory beanFactory = new BeanFactory();
    beanFactory.scanBeanByPackage("test");
    try {
        ClassThree three = beanFactory.getBean(ClassThree.class);

        System.out.println("注入完成");
        System.out.println("得到: "+three);
    } catch (InJectFailException e) {
        e.printStackTrace();
    }
}

结果如下:

Java手写实现IOC_第4张图片

你可能感兴趣的:(Java,Spring)