IOC容器的实现

IOC容器的实现,第1张

IOC容器的实现

手写一个简易版的spring ioc容器

步骤
1.创建注解
2.提取标记对象
3.实现容器
4.依赖注入


创建注解

创建4个注解,用来自动装配

package org.simpleframework.core;

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


@Target(ElementType.TYPE) // 作用在类上
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {


}


提取标记对象

实现思路: 
1.指定扫包范围,获取范围内的所有类
2.遍历所有类,获取被注解标记的类并加载进容器

extractPackageClass需要完成的事情:
1.获取到类的加载器
2.通过类加载器获取到加载的资源信息
3.根据不同的资源类型,采用不同的方式获取资源的集合

package org.simpleframework.util;

import java.io.File;
import java.io.FileFilter;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;


public class ClassUtil {

    
    public static final String FILE_PROTOCOL = "file";
    
    public static final String CLASS_SUFFIX = ".class";

    
    public static Set> extractPackageClass(String packageName) {
        // 1.获取到类的加载器
        ClassLoader classLoader = getClassLoader();
        // 2.通过类加载器获取到加载的资源信息
        URL url = classLoader.getResource(packageName.replace(".", "/"));
        if (url == null) {
            System.out.println("unable to retrieve anything from package " + packageName);
            return null;
        }
        // 3.根据不同的资源类型,采用不同的方式获取资源的集合
        Set> classSet = null;
        // 过滤出文件类型的资源
        if (url.getProtocol().equalsIgnoreCase(FILE_PROTOCOL)) {
            classSet = new HashSet<>();
            File packageDirectory = new File(url.getPath());
            extractClassFile(classSet, packageDirectory, packageName);
        }

        // else { } TODO 此处还可以加入对其他类型资源的处理


        return classSet;
    }

    
    private static void extractClassFile(Set> classSet, File fileSource, String packageName) {
        // 不是一个目录则直接结束递归方法
        if (!fileSource.isDirectory()) {
            return;
        }

        // 如果是一个文件夹,则调用listFiles方法获取文件夹下的文件或文件夹
        File[] files = fileSource.listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                if (file.isDirectory()) {
                    return true;
                } else {
                    // 获取文件的绝对路径
                    String absolutePath = file.getAbsolutePath();
                    // 判断是不是字节码文件
                    if (absolutePath.endsWith(CLASS_SUFFIX)) {
                        // 若是class文件,则放入classSet
                        addToClassSet(absolutePath);
                    }
                }
                return false;
            }

            // 根据class文件的绝对路径,获取并生成class对象,并放入classSet中
            private void addToClassSet(String absolutePath) {
                // 1.从class文件的绝对路径里提取出包含了package的类名
                absolutePath = absolutePath.replace(File.separator, ".");
                // 去掉项目路径
                String className = absolutePath.substring(absolutePath.indexOf(packageName));
                // 去掉.class后缀
                className = className.substring(0, className.lastIndexOf("."));
                // 2.通过反射获取对应的Class对象并放入到classSet中
                try {
                    Class targetClass = Class.forName(className);
                    classSet.add(targetClass);

                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
        });

        if (files != null && files.length != 0) {
            for (File file : files) {
                // 递归调用
                extractClassFile(classSet, file, packageName);
            }
        }

    }

    
    public static ClassLoader getClassLoader() {
        return Thread.currentThread().getContextClassLoader();
    }

    public static void main(String[] args) {
        Set> classSet = extractPackageClass("com.imooc.entity");
        System.out.println();
    }
}

实现容器

容器的组成部分
1.保存Class对象及其实例的载体
2.容器的加载
3.容器的 *** 作方式

实现容器的加载
1.配置的管理与获取
2.获取指定范围内的Class对象
3.根据配置提取Class对象,连同实例一并存入容器

package org.simpleframework.core;

import org.simpleframework.core.annotation.Component;
import org.simpleframework.core.annotation.Controller;
import org.simpleframework.core.annotation.Repository;
import org.simpleframework.core.annotation.Service;
import org.simpleframework.util.ClassUtil;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;


public class BeanContainer {

    
    private final Map, Object> beanMap = new ConcurrentHashMap<>();

    
    public static final List> BEAN_ANNOTATION_LIST = Arrays.asList(
            Component.class, Controller.class, Repository.class, Service.class
    );

    
    private boolean loaded = false;

    private BeanContainer() {

    }

    
    public static BeanContainer getInstance() {
        return BeanContainerHolder.HOLDER.instance;
    }

    private enum BeanContainerHolder {
        
        HOLDER;

        private final BeanContainer instance;

        BeanContainerHolder() {
            instance = new BeanContainer();
        }
    }

    
    public synchronized void loadBeans(String packageName) {
        if (loaded) {
            return;
        }
        Set> classSet = ClassUtil.extractPackageClass(packageName);
        if (classSet == null && classSet.isEmpty()) {
            System.out.println("extract nothing from package " + packageName);
            return;
        }

        for (Class clazz : classSet) {
            for (Class annotation : BEAN_ANNOTATION_LIST) {
                // 如果类上面标注了指定的注解
                if (clazz.isAnnotationPresent(annotation)) {
                    // 将目标类本身作为键,目标类的实例作为值,放入到beanMap中
                    beanMap.put(clazz, ClassUtil.newInstance(clazz));
                }
            }
        }
        loaded = true;
    }

    public static void main(String[] args) {
        BeanContainer beanContainer = BeanContainer.getInstance();
        beanContainer.loadBeans("com.imooc");
        System.out.println();
    }

}

使用自定义的注解标注需要加载进容器的类

 测试自定义的容器加载bean

实现容器的 *** 作方式
1.增加,删除 *** 作
2.根据Class获取对应实例
3.获取所有的Class和实例
4.通过注解来获取被注解标注的Class
5.通过超类获取子类的Class
6.获取容器载体保存Class的数量 


依赖注入 

实现思路
1.定义相关的注解标签
2.实现创建被注解标记的成员变量的实例,并将其注入到成员变量里
3.依赖注入的使用

 定义相关的注解标签

package org.simpleframework.inject.annotation;

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


@Target(ElementType.FIELD) // 作用在成员变量上
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}

实现创建被注解标记的成员变量的实例,并将其注入到成员变量里
执行ioc
1.遍历bean容器中所有的Class对象
2.遍历Class对象的所有成员变量
3.找出被Autowired标记的成员变量
4.获取这些成员变量的类型
5.获取这些成员变量的类型在容器里对应的实例
6.通过反射将对应的成员变量实例注入到成员变量所在类的实例里

 

package org.simpleframework.inject;

import com.imooc.controller.UserController;
import com.imooc.service.UserService;
import org.simpleframework.core.BeanContainer;
import org.simpleframework.inject.annotation.Autowired;
import org.simpleframework.util.ClassUtil;

import java.lang.reflect.Field;
import java.util.Set;


public class DependencyInjector {

    
    private BeanContainer beanContainer;

    public DependencyInjector() {
        beanContainer = BeanContainer.getInstance();
    }

    
    public void doIoc() {
        // 1.遍历bean容器中所有的Class对象
        Set> classSet = beanContainer.getClasses();
        if (classSet == null || classSet.isEmpty()) {
            System.out.println("empty classSet in BeanContainer");
            return;
        }

        for (Class clazz : classSet) {
            // 2.遍历Class对象的所有成员变量
            Field[] fields = clazz.getDeclaredFields();
            if (fields.length == 0) {
                continue;
            }
            for (Field field : fields) {
                // 3.找出被Autowired标记的成员变量
                if (field.isAnnotationPresent(Autowired.class)) {
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    String autowiredValue = autowired.value();
                    // 4.获取这些成员变量的类型
                    Class fieldClass = field.getType();
                    // 5.获取这些成员变量的类型在容器里对应的实例
                    Object fieldValue = getFieldInstance(fieldClass, autowiredValue);
                    if (fieldValue == null) {
                        throw new RuntimeException("unable to inject relevant type, target fieldClass is " + fieldClass.getName() + "autowiredValue is " + autowiredValue);
                    }
                    // 6.通过反射将对应的成员变量实例注入到成员变量所在类的实例里
                    Object targetBean = beanContainer.getBean(clazz);
                    ClassUtil.setField(targetBean, field, fieldValue);
                }
            }
        }


    }

    
    private Object getFieldInstance(Class fieldClass, String autowiredValue) {
        Object fieldValue = beanContainer.getBean(fieldClass);
        if (fieldValue != null) {
            return fieldValue;
        } else {
            Class implementClass = getImplementClass(fieldClass, autowiredValue);
            if (implementClass != null) {
                return beanContainer.getBean(implementClass);
            } else {
                return null;
            }
        }
    }

    
    private Class getImplementClass(Class fieldClass, String autowiredValue) {
        Set> classSet = beanContainer.getClassesBySuper(fieldClass);
        if (classSet == null || classSet.isEmpty()) {
            System.out.println("empty classSet in BeanContainer");
            return null;
        } else {
            // 如果@Autowired's value没有值
            if (autowiredValue == null || autowiredValue.length() == 0) {
                if (classSet.size() == 1) {
                    return classSet.iterator().next();
                } else {
                    // 如果多于两个实现类,且用户未指定其中一个实现类,则抛出异常
                    throw new RuntimeException("multiple implement classes for " + fieldClass.getName() + ", please set @Autowired's value to pick one");
                }
            } else {
                for (Class clazz : classSet) {
                    if (autowiredValue.equals(clazz.getSimpleName())) {
                        return clazz;
                    }
                }
            }
        }
        return null;
    }

    public static void main(String[] args) {
        BeanContainer beanContainer = BeanContainer.getInstance();
        beanContainer.loadBeans("com.imooc");
        UserController userController = (UserController) beanContainer.getBean(UserController.class);
        UserService userService = userController.getUserService();
        // 执行ioc依赖注入前
        new DependencyInjector().doIoc();
        // 执行ioc依赖注入后
        UserService userService1 = userController.getUserService();
        System.out.println();
    }
}

 至此,实现了简易ioc的整个流程,有装载bean的容器,能够注入依赖

源码地址https://gitee.com/jiangli31346337/simple-framework

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5715514.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存