自己对控制反转(IOC)思想的理解与实现

IOC思想

控制反转(Inversion of Control,缩写为IOC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

IOC用途
控制反转:以前我们创建对象都是使用new关键字新建对象。
比如:

A a = new A();

但是在控制反转的思想中,我们不需要直接来new对象,我们只需要定义引用变量,稍后让程序为我们自动注入即可使用,而不需要我们手动去为他添加对象。使得现在我们的对象创建从主动变成了被动。

在Spring中我们需要配置非常多的XML文件来添加Bean,SpringBoot为我们省去了这些操作,取而代之的是一些自定义注解例如@Controller,@Service,@Autowired等等,标记在类,变量,方法上都会在初始化容器的时候为我们实例化对象到容器。

例如在初始化的时候,扫描到@Autowired注解,容器就会自动为我们添加A的实例对象到a;

@Autowired
A a;

目的也就是为了提高代码的灵活性和可维护性。

IOC实现
之前一直对IOC的原理比较感兴趣,好奇他是怎么去注入对象,怎么知道哪里应该注入什么东西。下面是我用注解实现的一个小型的IOC容器,只能说是有这种功能,其他方面的都还没有考虑,算是一种自己对IOC容器的一种理解吧。
主要用到的就是java的反射技术和注解。这里的接口实现类没有实现可以添加Value的方式,但是基本原理和那个Service的注入一样。

IOC优缺点

优点:IOC带给我们最大的好处就是我们需要更换某些对象的时候只需要改动很少一部分代码,也就是解耦。把各个类之间的关联降到最低,而不用改一个地方就需要去改动全部代码。

缺点:spring里面用的是xml的方式来实现注入,这就需要配置很多配置文件,非常麻烦,而且使得创建对象的过程更复杂,反射技术也会使效率变低等等。但是SpringBoot里面使用的注解开发方式,已经解决了繁杂的配置问题,只需要几个注解就能完成注入工作。但是后面带来的问题就是报错不能较好的定位。

package springIOC;

import java.io.File;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@YSHService
public class StartApplication {

    @YSHAutowire
    static Controller controller;

    static List<String> classNames = new ArrayList<>();

    static ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>(16);

    static ConcurrentHashMap<String, String> interToImpls = new ConcurrentHashMap<>(16);

    static String classPath = StartApplication.class.getResource("/").getPath();

    public static void main(String[] args) throws Exception {
        // 扫描同一个包下的所有的class文件
        getAllClass(classPath + StartApplication.class.getPackage().getName() + "/");

        // 添加对应的对象到ioc容器
        newInstanceToIOC();

        // 依赖注入
        dependencyInjection();

        // 测试
        controller.outPut();
    }

    public static void getAllClass(String scanPackage) {
        File files = new File(scanPackage);
        for (File file : files.listFiles()) {
            if (file.isDirectory()) {
                getAllClass(scanPackage + file.getName() + "/");
            } else {
                if (!file.getName().endsWith(".class")) {
                    continue;
                }
                String className = scanPackage.replaceAll(classPath, "").replaceAll("/", ".") + file.getName().replaceAll(".class", "");
                classNames.add(className);
            }
        }
    }

    public static void newInstanceToIOC() throws Exception {
        for (String string : classNames) {
            try {
                Class<?> clazz = Class.forName(string);
                Class<?>[] interToImpl = clazz.getInterfaces();

                if (interToImpl != null) {
                    for (Class<?> iti : interToImpl) {
                        if (!(clazz.getAnnotation(YSHService.class) == null)) {
                            YSHService yshService = clazz.getAnnotation(YSHService.class);
                            if (yshService.value().equals("")) {
                                if (interToImpls.containsKey(clazz.getName())) {
                                    throw new Exception("There is more than one interfaceImpl with same name");
                                }
                                interToImpls.put(clazz.getName(), iti.getName());
                            } else {
                                if (interToImpls.containsKey(yshService.value())) {
                                    throw new Exception("There is more than one interfaceImpl with same name");
                                }
                                interToImpls.put(yshService.value(), iti.getName());
                            }
                        }
                    }
                }
                if (clazz.isAnnotationPresent(YSHController.class)) {
                    Object object = clazz.newInstance();
                    String objName = clazz.getName();
                    ioc.put(objName, object);
                }
                if (clazz.isAnnotationPresent(YSHService.class)) {
                    Object object = clazz.newInstance();
                    String objName = clazz.getName();
                    ioc.put(objName, object);
                }
            } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
                e.printStackTrace();
            }
        }
    }

    public static void dependencyInjection() throws Exception {
        List<String> impls = new ArrayList<>();
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            // 拿到各层的属性并注入对象
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(YSHAutowire.class)) {
                    YSHAutowire yshAutowire = field.getAnnotation(YSHAutowire.class);

                    String beanName = yshAutowire.value();
                    if (beanName.equals("")) {
                        beanName = field.getType().getName();
                    }

                    // 强吻
                    field.setAccessible(true);
                    
                    // 容器中查找对象
                    if (Class.forName(beanName).isInterface()) {
                        for (Map.Entry<String, String> map : interToImpls.entrySet()) {
                            if (map.getValue() == beanName) {
                                impls.add(map.getKey());
                            }
                        }
                    }
                    if (impls.size() > 1) {
                        throw new Exception("Can't inject a unique object");
                    }
                    if (impls.size() == 1) {
                        beanName = impls.get(0);
                        impls.clear();
                    }
					// 注入对象
                    field.set(entry.getValue(), ioc.get(beanName));
                }
            }
        }
    }
}