spring(三):IOC介绍及代码实现

1. IoC容器概述

IoC 是 Inversion of Control 的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。

Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系。我们将由 IoC 容器管理的 Java 对象称为 Spring Bean,它与使用关键字 new 创建的 Java 对象没有任何区别。

IoC 容器是 Spring 框架中最重要的核心组件之一,它贯穿了 Spring 从诞生到成长的整个过程。

2. 控制反转(IoC)

控制反转是一种思想,可以降低程序耦合度,提高程序扩展力。

控制反转, 就是将对象的创建以及对象和对象之间关系的维护(也就是对象中属性的赋值)交给第三方容器负责。

也就是说,我们可以不需要手动创建Bean对象以及手动对Bean对象中的属性赋值,这些操作都可以由IoC容器完成。

控制反转这种思想是根据依赖注入(Dependency Injection,DI)实现的。

3. 依赖注入

指Spring创建对象的过程中,将对象依赖属性通过配置进行注入。

依赖注入常见的实现方式包括两种:

  • 第一种:set注入
  • 第二种:构造注入

IOC 就是一种控制反转的思想, 而 DI 是对IoC的一种具体实现。

4. 代码实现

该代码只是粗略实现,目的是为了更好的展示IoC及依赖注入的实现放视。其中,Bean注解表示将该类交由IoC容器管理(spring中的@component),Di注解表示对该属性进行依赖注入(spring中的@Autowired)。

实现思路:

  • 根据用户输入的包名,首先获取到这个包的所在路径。
  • 之后,对这个路径下的所有文件及文件夹进行扫描,寻找添加了Bean注解的的类。
  • 找到满足条件的类后创建该类的实例对象,并将这个类的实例对象以及其类型放入到map当中。其中,key是类型,value是实例对象。这样。我们就可以根据类型获取到相对应的实例对象。
  • 将所有加了Bean注解的类都放入map之后,对map中的所有对象进行遍历。根据反射获取其所有属性,判断是否有属性添加了注解Di。
  • 如果存在添加了注解Di的属性,我们就要根据其属性的类型在map中得到相对应类型的实例对象,并调用set函数将该对象赋值给该属性,完成依赖注入操作。

实现代码如下:

public class AnnotationApplicationContext implements ApplicationContext{

   //存放bean对象
   private HashMap<Class, Object> beanFactory = new HashMap<>();
   private static String rootPath;

   //当前包及其子包,哪个类有@Bean注解,把这个类通过反射实例化
   public AnnotationApplicationContext(String basePackage) throws Exception {
      // 把.替换成\\
      String packageDir = basePackage.replaceAll("\\.", "\\\\");
      //得到绝对路径
      Enumeration<URL> absolutePath = Thread.currentThread().getContextClassLoader().getResources(packageDir);
      while (absolutePath.hasMoreElements()) {
         URL url = absolutePath.nextElement();
         //斜杠'/'可能被编码,因此需要解码
         String filePath = URLDecoder.decode(url.getFile(),"utf-8");
         //获得包前面的路劲,以便后续操作
         rootPath = filePath.substring(0, filePath.length()-packageDir.length());
         //包扫描
         scanBean(new File(filePath));
         //属性注入
         loadDi();
      }
   }

   private void loadDi() throws Exception {
      Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
      //遍历map集合中的每个对象
      for(Map.Entry<Class, Object> entry : entries) {
         Object obj = entry.getValue();
         //得到每个对象的class
         Class<?> clazz = obj.getClass();
         //获得每个对象的所有属性
         Field[] declaredFields = clazz.getDeclaredFields();
         for(Field field : declaredFields) {
            //如果存在属性含有注解Di
            if(field.getAnnotation(Di.class) != null) {
               //如果时私有属性,设置可访问
               field.setAccessible(true);
               //为属性赋值,按类型获取对应的对象
               field.set(obj, beanFactory.get(field.getType()));
            }
         }
      }
   }

   private void scanBean(File file) throws Exception {
      // 如果当前是文件夹
      if(file.isDirectory()) {
         //获得所有子文件(夹)
         File[] childFiles = file.listFiles();
         //如果当前文件夹空
         if(childFiles == null || childFiles.length == 0) {
            return;
         }
         for(File childFile : childFiles) {
            //如果当前子文件时文件夹,递归
            if(childFile.isDirectory()) {
               scanBean(childFile);
            }else {
               //得到包路径+类名称
               String classPath = childFile.getAbsolutePath().substring(rootPath.length() - 1);
               //如果当前文件类型时class
               if(classPath.endsWith(".class")) {
                  //把路径\替换成.  把.class去掉
                  String allName = classPath.replaceAll("\\\\", ".").replace(".class", "");
                  //获取类的class
                  Class clazz = Class.forName(allName);
                  //如果不是接口而且上面有注解Bean
                  if(!clazz.isInterface() && clazz.getAnnotation(Bean.class) != null) {
                     // 实例化对象
                     Object instance = clazz.getConstructor().newInstance();
                     // 如果当前类实现了接口
                     if(clazz.getInterfaces().length > 0) {
                        //把接口的类型作为该实例的类别
                        beanFactory.put(clazz.getInterfaces()[0], instance);
                     }else {
                        //没有接口,就把该类的类型作为key
                        beanFactory.put(clazz, instance);
                     }
                  }
               }
            }
         }
      }
   }

   @Override
   public Object getBean(Class clazz) {
      return beanFactory.get(clazz);
   }

}

使用代码如下:

   @Test
   public void testIoc() throws Exception {
      ApplicationContext context = new AnnotationApplicationContext("org.kkk.spring6");
      UserService userService = (UserService) context.getBean(UserService.class);
      userService.print();
   }

你可能感兴趣的:(spring,spring,java,后端)