一步一步实现SpringIoc容器

IoCInverse of Controll控制反转:指的是对象的创建方式进行了反转,传统的开发方式是程序员自己 new 对象,IoC就是将这一过程进行了反转,程序员不需要自己 new 对象,而是交给 IoC 容器来创建对象,程序员只需要使用这些对象即可。 控制反转的好处是解耦合。将创建对象的控制权进行了反转,之前是直接 new 的,现在是接收工厂方法返回的对象,无法控制对象的创建,将对象创建的权力进行了反转,所以叫控制反转

IOC创建对象的两种方式:

一、基于XML

1、创建一个 XML 文件,在文件中将程序中需要用的对象进行配置。

2、加载 IoC 容器,Spring 框架会自动读取 XML 文件,根据 XML 的配置,自动创建各种对象,放入到缓冲池中。

3、开发者只需要根据需求从缓冲池中取出相应对象使用即可。

二、基于注解

基于注解的方式和基于 XML 的方式原理是相通的,只不过换了一种形式来处理。

1、在需要注入的 IoC 的类处添加注解,标注一下,告诉Spring 框架,这个类是需要注入的 bean。默认是根据容器中的类型去加载。

方式一:

方式二:

2、加载 IoC 容器,Spring 框架会自动读取到所有添加了注解的类,通过反射机制创建 bean,注入到 IoC 容器中,指定要扫描的包。

3、开发者从 IoC 容器中取出需要的 bean。

基于注解的方式,类默认的 id 是类名的首字母小写,User --> user,如果要修改 id,只需要给@Component 注解添加 value 属性即可,属性值就是新的 id 值。


Spring IoC的核心源码原理:

一、基于 XML

1、IoC 容器加载的时候,会自动读取spring.xml。

2、通过 XML 解析,获取到目标数据。

3、通过反射机制,创建目标 bean。

4、将这些 bean 注入到 IoC 缓存中,以 key-value 的形式进行存储。

      5、开发者只需要通过 key/Class 从缓存中取出 bean 使用即可。

二、基于注解

1、IoC 容器加载的时候,要遍历指定包名下的所有类。

2、判断这些类是否添加了@Component,把所有添加了@Component 注解的类进行数据收集(beanId、className),存入集合中。

3、将集合传递给下一个单元,遍历这个集合,获取目标数据。

4、通过反射机制,创建目标 bean。

5、将这些 bean 注入到 IoC 缓存中,以 key-value 的形式进行存储。

6、开发者只需要通过 key/Class 从缓存中取出 bean 使用即可。

首先在指定给目标包中添加注解,然后启动ioc容器,找到添加的注解类,获取类上面的id和class信息然后封装成一个对象,每一个对象用BeanDefinition表示,把每一个BeanDefinition放入BeanDefinitions集合,解析这个集合,遍历每一个元素,拿到每一个id根据class创建对象,最后把对象放入cache中,把id作为key存放进去。


手写一个SpringIoc容器:

步骤:

/**
 * 1、自定义一个AnnotationConfigApplicationContext,构造器中传入目标包。
 * 2、获取这个包下的所有类。
 * 3、遍历这些类,找出添加了 @Component 注解的类,获取对应的 beanName,beanClass,封装成一个BeanDefinition 对象,存入 Set 集合,这些就是 IoC 自动装载的原材料。
 * 4、遍历 Set 集合,通过反射机制创建对象,同时检测属性是否有 @Value 注解,有的话就赋值,没有就不赋值。
 * 5、将上面创建的 bean 以 k-v 的形式存入 cache Map
 * 6、提供 getBean 方法,通过 beanName 从缓存区取出对应的 bean
 */

使用Maven进行创建:

pom.xml文件,引入lombok和jdk版本号

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

创建所需的标注类:

Component.java
// 标注在类上定义的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    String value() default "";
}
Autowired.java
// 标注在属性上的注解,按照类型自动注入
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {}
Qualifier.java
// 标注在属性上的注解,按照名称注入
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Qualifier {
    String value();
}
Value.java
// 标注在属性上的注解,用于给属性赋值
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Value {
    String value();
}

创建BeanDefinition包装类

// 用于包装注解类的id和class
@Data
@AllArgsConstructor
public class BeanDefinition {
    private String beanName;
    private Class beanClass;
}

核心类:ioc容器入口

AnnotationConfigApplicationContext.java
public class AnnotationConfigApplicationContext {

    // 用来存储创建好的bean
    private Map<String,Object> cache = new HashMap<>();

    public AnnotationConfigApplicationContext(String basePackage){
        //遍历包,找到目标类
        Set<BeanDefinition> beanDefinitions = findBeanDefinition(basePackage);
        //根据原料创建bean
        createObject(beanDefinitions);
        //自动装载
        autowiredObject(beanDefinitions);
    }

    /**
     * 遍历包,找到目标类
     * @param basePackage 要扫描的包路径
     * @return 返回包装好的类
     */
    private Set<BeanDefinition> findBeanDefinition(String basePackage) {
        // 获取包下的所有Class
        Set<Class<?>> classes = MyUtil.getClasses(basePackage);
        // 遍历集合
        Iterator<Class<?>> iterator = classes.iterator();
        // 用来存放包装好的BeanDefinition
        Set<BeanDefinition> set = new HashSet<>();
        while (iterator.hasNext()){
            Class<?> clazz = iterator.next();
            // 判断该类是否添加了@Component注解
            Component component = clazz.getAnnotation(Component.class);
            if(component!=null){
                // 获取注解上的值
                String beanName = component.value();
                if ("".equals(beanName)){
                    // 注解值为空,自动给这个bean取一个名字,默认类名首字母小写
                    String packageName = clazz.getPackage().getName();
                    String className = clazz.getName();
                    beanName = className.replace(packageName+".","");
                    beanName = beanName.substring(0,1).toLowerCase()+beanName.substring(1);
                }
                // 添加到集合
                set.add(new BeanDefinition(beanName,clazz));
            }
        }
        return set;
    }

    /**
     * 创建bean对象
     * @param beanDefinitions 包装好的beanDefinitions
     */
    private void createObject(Set<BeanDefinition> beanDefinitions) {
        // 遍历集合
        Iterator<BeanDefinition> iterator = beanDefinitions.iterator();
        while (iterator.hasNext()) {
            BeanDefinition beanDefinition = iterator.next();
            try {
                // 获取beanClass信息
                Class beanClass = beanDefinition.getBeanClass();
                // 创建当前bean的实例
                Object instance = beanClass.getConstructor(null).newInstance(null);
                // 给属性赋值
                Field[] fields = beanClass.getDeclaredFields();
                for (Field field : fields) {
                    // 判断当前属性是否添加了@Value注解
                    Value fieldAnnotation = field.getAnnotation(Value.class);
                    if(fieldAnnotation!=null){
                        // 有@Value注解,给当前属性赋值
                        // 获取注解上的值
                        String value = fieldAnnotation.value();
                        // 获得属性名字,并找到给当前属性赋值的方法setXxx()
                        String fieldName = field.getName();
                        String methodName = "set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1);
                        Method method = beanClass.getMethod(methodName, field.getType());
                        // 得到属性的类型
                        String name = field.getType().getName();
                        Object val = null;
                        switch (name){
                            case "java.lang.Integer":
                                val = Integer.parseInt(value);
                                break;
                            case "java.lang.String":
                                val = value;
                                break;
                            case "java.lang.Double":
                                val = Double.parseDouble(value);
                                break;
                        }
                        // 通过反射给当前属性赋值
                        method.invoke(instance,val);
                    }
                }
                // 并将创建好的bean放在缓存中
                cache.put(beanDefinition.getBeanName(),instance);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 自动装载
     * @param beanDefinitions 包装好的beanDefinitions
     */
    private void autowiredObject(Set<BeanDefinition> beanDefinitions) {
        // 遍历集合
        Iterator<BeanDefinition> iterator = beanDefinitions.iterator();
        while (iterator.hasNext()) {
            BeanDefinition beanDefinition = iterator.next();
            // 获得当前的类信息
            Class beanClass = beanDefinition.getBeanClass();
            // 获得当前类的所有属性,并遍历它
            Field[] fields = beanClass.getDeclaredFields();
            for (Field field : fields) {
                // 判断该属性上是否添加了@Autowired注解
                Autowired autowired = field.getAnnotation(Autowired.class);
                if(autowired!=null){
                    // 判断该属性上是否添加了@Qualifier注解
                    Qualifier qualifier = field.getAnnotation(Qualifier.class);
                    if(qualifier!=null){
                        // 根据名字进行装载
                        try {
                            // 获取@Qualifier注解上的value值
                            String value = qualifier.value();
                            // 得到缓存中的bean对象
                            Object obj = cache.get(value);
                            Object bean = cache.get(beanDefinition.getBeanName());
                            // 找到要注入的方法setXxx()
                            String name = field.getName();
                            String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
                            Method method = beanClass.getMethod(methodName, field.getType());
                            // 通过反射注入对象
                            method.invoke(bean,obj);
                        } catch (NoSuchMethodException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    } else {
                        // 根据类型进行装载
                        try {
                            // 找到当前对象的类名字,首字母小写
                            Class<?> clazz = field.getType();
                            String packageName = clazz.getPackage().getName();
                            String className = clazz.getName();
                            className = className.replace(packageName+".","");
                            className = className.substring(0,1).toLowerCase()+className.substring(1);
                            // 去缓存中获取bean对象
                            Object obj = cache.get(className);
                            Object bean = cache.get(beanDefinition.getBeanName());
                            // 找到要注入的方法setXxx()
                            String name = field.getName();
                            String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
                            Method method = beanClass.getMethod(methodName, field.getType());
                            // 通过反射注入对象
                            method.invoke(bean,obj);
                        } catch (NoSuchMethodException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    /**
     * 通过name获取bean
     * @param beanName
     * @return
     */
    public Object getBean(String beanName){
        return cache.get(beanName);
    }
}

工具类:

MyUtil.java
public class MyUtil {

    public static Set<Class<?>> getClasses(String pack) {
        // 第一个class类的集合
        Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
        // 是否循环迭代
        boolean recursive = true;
        // 获取包的名字 并进行替换
        String packageName = pack;
        String packageDirName = packageName.replace('.', '/');
        // 定义一个枚举的集合 并进行循环来处理这个目录下的things
        Enumeration<URL> dirs;
        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            // 循环迭代下去
            while (dirs.hasMoreElements()) {
                // 获取下一个元素
                URL url = dirs.nextElement();
                // 得到协议的名称
                String protocol = url.getProtocol();
                // 如果是以文件的形式保存在服务器上
                if ("file".equals(protocol)) {
                    // 获取包的物理路径
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    // 以文件的方式扫描整个包下的文件 并添加到集合中
                    findClassesInPackageByFile(packageName, filePath, recursive, classes);
                } else if ("jar".equals(protocol)) {
                    // 如果是jar包文件
                    // 定义一个JarFile
                    System.out.println("jar类型的扫描");
                    JarFile jar;
                    try {
                        // 获取jar
                        jar = ((JarURLConnection) url.openConnection()).getJarFile();
                        // 从此jar包 得到一个枚举类
                        Enumeration<JarEntry> entries = jar.entries();
                        findClassesInPackageByJar(packageName, entries, packageDirName, recursive, classes);
                    } catch (IOException e) {
                        // log.error("在扫描用户定义视图时从jar包获取文件出错");
                        e.printStackTrace();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classes;
    }

    private static void findClassesInPackageByJar(String packageName, Enumeration<JarEntry> entries, String packageDirName, final boolean recursive, Set<Class<?>> classes) {
        // 同样的进行循环迭代
        while (entries.hasMoreElements()) {
            // 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
            JarEntry entry = entries.nextElement();
            String name = entry.getName();
            // 如果是以/开头的
            if (name.charAt(0) == '/') {
                // 获取后面的字符串
                name = name.substring(1);
            }
            // 如果前半部分和定义的包名相同
            if (name.startsWith(packageDirName)) {
                int idx = name.lastIndexOf('/');
                // 如果以"/"结尾 是一个包
                if (idx != -1) {
                    // 获取包名 把"/"替换成"."
                    packageName = name.substring(0, idx).replace('/', '.');
                }
                // 如果可以迭代下去 并且是一个包
                if ((idx != -1) || recursive) {
                    // 如果是一个.class文件 而且不是目录
                    if (name.endsWith(".class") && !entry.isDirectory()) {
                        // 去掉后面的".class" 获取真正的类名
                        String className = name.substring(packageName.length() + 1, name.length() - 6);
                        try {
                            // 添加到classes
                            classes.add(Class.forName(packageName + '.' + className));
                        } catch (ClassNotFoundException e) {
                            // .error("添加用户自定义视图类错误 找不到此类的.class文件");
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    private static void findClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set<Class<?>> classes) {
        // 获取此包的目录 建立一个File
        File dir = new File(packagePath);
        // 如果不存在或者 也不是目录就直接返回
        if (!dir.exists() || !dir.isDirectory()) {
            // log.warn("用户定义包名 " + packageName + " 下没有任何文件");
            return;
        }
        // 如果存在 就获取包下的所有文件 包括目录
        File[] dirfiles = dir.listFiles(new FileFilter() {
            // 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
            @Override
            public boolean accept(File file) {
                return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
            }
        });
        // 循环所有文件
        for (File file : dirfiles) {
            // 如果是目录 则继续扫描
            if (file.isDirectory()) {
                findClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
            } else {
                // 如果是java类文件 去掉后面的.class 只留下类名
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    // 添加到集合中去
                    // classes.add(Class.forName(packageName + '.' +
                    // className));
                    classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
                } catch (ClassNotFoundException e) {
                    // log.error("添加用户自定义视图类错误 找不到此类的.class文件");
                    e.printStackTrace();
                }
            }
        }
    }
}

测试用的实体类:Student.java和Class.java类

@Data
@Component("stu")
public class Student {
    @Value("2021100101")
    private Integer sid;
    @Value("张三")
    private String name;
    @Value("男")
    private String sex;
    @Autowired
    @Qualifier("cls")
    private Class clazz;
}
@Data
@Component("cls")
public class Class {
    @Value("1001")
    private Integer cid;
    @Value("精英班")
    private String className;
}

测试:

public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext("com.raylei.entity");
        System.out.println(ioc.getBean("stu"));
    }
}

结果:

GitHub:https://github.com/Mr-LeiLei/MySpringIoc

CSDN:https://download.csdn.net/download/qq_36942720/16640502

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Please Sit Down

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值