手写Spring IOC框架

一、前言

2020.03.28 :本来打算把源码分析一遍,这周时间不够了,下周分析好再更新一遍。


2020.04.05 :人类的本质果然是鸽子,没错,我鸽了。。。

二、简介

首先明白Spring几个关键类:

  • BeanFactory :
    BeanFactory定义了 IOC 容器的最基本形式,并提供了 IOC 容器应遵守的的最基本的接口,也就是Spring IOC 所遵守的最底层和最基本的编程规范。在 Spring 代码中, BeanFactory 只是个接口,并不是 IOC容器的具体实现,但是 Spring 容器给出了很多种实现,如 DefaultListableBeanFactory 、 XmlBeanFactory 、ApplicationContext 等,都是附加了某种功能的实现。

  • ApplicationContext
    ApplicationContext继承了BeanFactory。是Spring在 BeanFactory基础上的一个扩展类,提供了更多的功能,比如 国际化、资源获取、AOP等。并且ApplicationContext加载Bean是启动加载,而BeanFactory是懒加载的方式。

  • BeanDefination:
    这个接口,可以理解为xml bean元素的数据载体。通过对比xml bean标签的属性列表和BeanDefinition的属性列表一看便知。我的理解,是解析XML的过程,就是 xml 元素内容 转换为BeanDefinition对象的过程。而且这个接口,支持层级,对应对象的继承。有一个类BeanDefinitionHolder,BeanDefinitionHolder,根据名称或者别名持有beanDefinition,它承载了name和BeanDefinition的映射信息。BeanWarpper:提供对标准javabean的分析和操作方法:单个或者批量获取和设置属性值,获取属性描述符,查询属性的可读性和可写性等。支持属性的嵌套设置,深度没有限制。

三、手写IOC框架

手写首先需要了解IOC整个原理。等下周把源码分析了会更详细的写一写,莫要捉急。。。

简单来说 :

  1. 服务启动后扫描开始扫描指定的路径及其子路径下的文件。
  2. 过滤出其中的字节码文件(class文件)。
  3. 通过反射来获取 Class,判断类上是否被 @SelfComponent 注解修饰
  4. 若被 @SelfComponent 修饰,则说明该类的生命周期控制反转,交由容器来控制其创建销毁。代码中的表现是 创建实例,并保存到beanMap 中。
  5. 当我们把所有的所有 被 @SelfComponent 修饰 的对象保存到 beanMap中后,开始对这些对象中的属性进行一个判断: 若属性被 @SelfAutowired 修饰 , 则表明需要容器给他注入实例,这时候从beanMap中拿出对应的实例,进行一个赋值处理。

下面贴一些关键代码

1. 自定义注解

自定义 SelfAutowired 、SelfComponent 两个注解,见名知意。

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({
    
    ElementType.METHOD, ElementType.FIELD})
public @interface SelfAutowired {
    
    
}
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({
    
    ElementType.TYPE})
public @interface SelfComponent {
    
    
}

2. SpringIocApplication

SpringIocApplication 作为主要实现类,主要在 SpringIocApplication 中实现了 IOC 功能

package com.kingfish.ioc;

import com.google.common.collect.Maps;
import com.kingfish.ioc.annotation.SelfAutowired;
import com.kingfish.ioc.annotation.SelfComponent;
import com.kingfish.ioc.utils.ClassUtils;
import com.kingfish.ioc.utils.lambda.Lambda;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

/**
 * @Author : wlhao
 * @Email : [email protected]
 * @Data: 2020/3/29 14:08
 * @Des: SpringIoc 的启动类
 */

@Slf4j
public class SpringIocApplication {
    
    

    private static volatile SpringIocApplication springIocApplication;

    private Map<String, Object> beanMap = Maps.newConcurrentMap();

    private SpringIocApplication() {
    
    
    }

    public static SpringIocApplication getInstance() {
    
    
        if (springIocApplication == null) {
    
    
            synchronized (SpringIocApplication.class) {
    
    
                if (springIocApplication == null) {
    
    
                    springIocApplication = new SpringIocApplication();
                }
            }
        }

        return springIocApplication;
    }

    /**
     * 启动方法
     *
     * @param aClass
     * @return
     */
    public SpringIocApplication run(Class<?> aClass) {
    
    
        try {
    
    
            String packageName = aClass.getPackage().getName();
            List<Class<?>> allClass = ClassUtils.getAllClassByPakcage(packageName);
            initBean(allClass);
            initBeanFields();
        } catch (IOException | IllegalAccessException e) {
    
    
            e.printStackTrace();
        }
        return this;
    }

    /**
     * 初始化Bean,将需要的bean保存到容器beanMap中
     *
     * @param classList
     */
    private void initBean(List<Class<?>> classList) {
    
    
        classList.stream().filter(aClass -> existComponent(aClass))
                .forEach(Lambda.forEach(aClass -> {
    
    
                    Class<?>[] interfaces = aClass.getInterfaces();
                    String beanName = ArrayUtils.isEmpty(interfaces) ? toLowerCaseFirstWord(aClass.getSimpleName()) : toLowerCaseFirstWord(interfaces[0].getSimpleName());
                    if (beanMap.containsKey(beanName)) {
    
    
                        throw new RuntimeException("bean already existed");
                    }
                    beanMap.put(beanName, aClass.newInstance());
                }));
    }

    /**
     * 初始化容器中的bean 的属性字段
     */
    private void initBeanFields() throws IllegalAccessException {
    
    
        for (Object bean : beanMap.values()) {
    
    
            Field[] declaredFields = bean.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
    
    
                if (existAutowired(field)) {
    
    
                    String fieldName = toLowerCaseFirstWord(field.getName());

                    if (!beanMap.containsKey(fieldName)) {
    
    
                        throw new RuntimeException("class not found");
                    }

                    Object beanObject = beanMap.getOrDefault(fieldName, null);
                    // 允许访问私有属性
                    field.setAccessible(true);
                    // 给bean 的field属性赋值为beanObject
                    field.set(bean, beanObject);
                }
            }
        }
    }

    /**
     * 是否存在SelfComponent 注解
     *
     * @param aclass
     * @return
     */
    private boolean existComponent(Class<?> aclass) {
    
    
        return aclass.getAnnotation(SelfComponent.class) != null && !aclass.isAnnotation();
    }

    /**
     * 是否存在SelfAutowired 注解
     *
     * @param field
     * @return
     */
    private boolean existAutowired(Field field) {
    
    
        return field.getAnnotation(SelfAutowired.class) != null;
    }


    /**
     * 获取bean
     *
     * @param beanName
     * @param tClass
     * @param <T>
     * @return
     */
    public <T> T getBean(String beanName, Class<T> tClass) {
    
    
        if (!beanMap.containsKey(beanName)) {
    
    
            throw new RuntimeException("not found");
        }
        return (T) beanMap.getOrDefault(beanName, null);
    }

    /**
     * 首字母转小写
     *
     * @param s
     * @return
     */
    private String toLowerCaseFirstWord(String s) {
    
    
        return Character.isLowerCase(s.charAt(0)) ? s
                : (new StringBuilder().append(Character.toLowerCase(s.charAt(0)))).append(s.substring(1)).toString();
    }
}

3. ClassUtils

一个辅助的工具类。
package com.kingfish.ioc.utils;

import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;

/**
 * @Author : wlhao
 * @Email : [email protected]
 * @Data: 2020/3/29 14:07
 * @Des: 类操作的辅助类
 */
public class ClassUtils {
    
    

    /**
     * 根据包路径获取路径下的所有类
     *
     * @param packageName
     * @return
     */
    public static List<Class<?>> getAllClassByPakcage(String packageName) throws IOException {
    
    
        List<Class<?>> classList = Lists.newArrayList();
        String packagePath = packageName.replace(".", "/");

        Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources(packagePath);
        while (resources.hasMoreElements()) {
    
    
            URL url = resources.nextElement();
            String protocol = url.getProtocol();

            if ("file".equals(protocol)) {
    
    
                System.out.println("file类型的扫描");
                String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                findAllClassByPath(packageName, filePath, classList);
            } else if ("jar".equals(protocol)) {
    
    
                System.out.println("jar类型的扫描");
            }
        }

        return classList;
    }

    /**
     * 获取给定路径下的所有class
     *
     * @param packageName 包名
     * @param filePath    路径
     * @param classList   返回列表
     */
    public static void findAllClassByPath(String packageName, String filePath, List<Class<?>> classList) {
    
    
        try {
    
    
            File rootFile = new File(filePath);
            if (!rootFile.exists()) {
    
    
                throw new RuntimeException(filePath);
            }


            if (rootFile.isDirectory()) {
    
    
                Arrays.stream(rootFile.listFiles(file -> (StringUtils.endsWith(file.getName(), "class") || file.isDirectory())))
                        .forEach(file -> findAllClassByPath(packageName + "." + file.getName(), file.getAbsolutePath(), classList));
            } else{
    
    
                String className = StringUtils.substringBeforeLast(packageName, ".");
                classList.add(Class.forName(className));

            }
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        }
    }
}

4. 测试

总的来说,只有两个关键类 SpringIocApplication、ClassUtils。依次创建IocController、IocService、IocDao 来进行一个测试就行了

public class SpringApplicationServer {
    
    

    public static void main(String[] args) {
    
    
        SpringIocApplication springIocApplication = SpringIocApplication.getInstance();
        springIocApplication.run(SpringApplicationServer.class);
        IocController iocController = springIocApplication.getBean("iocController", IocController.class);
        iocController.iocController();
    }
}
/**
 * @Author : wlhao
 * @Email : [email protected]
 * @Data: 2020/3/29 14:56
 * @Des:
 */
@SelfComponent
public class IocController {
    
    
    @SelfAutowired
    private IocService iocService;

    public void iocController() {
    
    
        System.out.println("IocDao.iocController");
        iocService.iocService();
    }
}

结果如下
在这里插入图片描述

5. 项目地址

https://github.com/HKingFish/ioc-demo.git


未完待遇…


以上:内容部分参考
https://blog.csdn.net/woshilijiuyi/article/details/82219585
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

猜你喜欢

转载自blog.csdn.net/qq_36882793/article/details/100559453