自学内容网 自学内容网

【学习笔记】手写一个简单的 Spring IOC

目录

一、什么是 Spring IOC?

二、IOC 的作用

1. IOC 怎么知道要创建哪些对象呢?

2. 创建出来的对象放在哪儿?

3. 创建出来的对象如果有属性,如何给属性赋值?

三、实现步骤

1. 创建自定义注解

2. 创建 IOC 容器

1. 创建 Bean 定义类

2. 创建 IOC 容器

3. 扫描包

4. 使用自定义注解

5. 在 Servlet 中初始化 IOC 对象

6. 测试

3. 实例化 Bean 

测试

4. 设置属性

1. 创建一个Controller

2. 属性赋值

3. 测试

四、使用 Bean

1. 创建获取 Bean 的方法

2. 使用 Bean 

3. 测试

五、优化 IOC 容器


一、什么是 Spring IOC?

Spring 的核心之一是 IOC,全称 Inversion Of Control ,控制反转,用来统一管理 Bean

二、IOC 的作用

IOC 的作用就是帮程序员创建 Bean 对象

1. IOC 怎么知道要创建哪些对象呢?

xml 配置文件中指定要创建哪些对象,或者使用注解的方式

如果使用注解的方式,需要自定义注解,自定义注解说白了就是告诉 JVM 这个 Bean 不同于其他普通的 Bean ,它有其他的用途,标记一下。

@Component、@Controller、@Service 可以用于创建 Bean 的注解

2. 创建出来的对象放在哪儿?

先记录需要被创建的 Bean 对象的信息,存到 HashMap 中,然后根据 HashMap 中存储的Bean 的信息创建对象,存到IOC容器中

3. 创建出来的对象如果有属性,如何给属性赋值?

使用注解 @Autowired

三、实现步骤

1. 创建自定义注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
    String value() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
    String value() default "";
}

2. 创建 IOC 容器

扫描包,判断类上是否有注解,如果有,存储需要被创建的对象的id,也就是全类名。

定义一个类用于存储这些信息,这个类就是 BeanDefinition  叫做 Bean 定义类,Bean 定义类创建对象后称为 Bean 定义对象,这个对象存储了 Bean 的信息

然后把 Bean 定义对象存放到 HashMap 中,这个 Map 叫做 Bean 定义Map

1. 创建 Bean 定义类

package com.shao.IOC;

public class BeanDefinition {

    private String id;
    private String className;


    public BeanDefinition() {
    }

    public BeanDefinition(String id, String className) {
        this.id = id;
        this.className = className;
    }

    /**
     * 获取
     *
     * @return id
     */
    public String getId() {
        return id;
    }

    /**
     * 设置
     *
     * @param id
     */
    public void setId(String id) {
        this.id = id;
    }

    /**
     * 获取
     *
     * @return className
     */
    public String getClassName() {
        return className;
    }

    /**
     * 设置
     *
     * @param className
     */
    public void setClassName(String className) {
        this.className = className;
    }

    public String toString() {
        return "BeanDefinition{id = " + id + ", className = " + className + "}";
    }
}

2. 创建 IOC 容器

3. 扫描包

扫描包需要先知道路径是什么,从哪开始

这里扫描的是编译后的 class 文件,并不是类文件,因为程序运行后使用的是编译后的文件

那如何从这里开始呢?

使用类加载器获取路径

package com.shao.IOC;

import com.shao.Annotation.Controller;
import com.shao.Annotation.Service;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;

/**
 * ApplicationContext 类. 当作 IOC 容器
 *
 * @author shao.
 */
public class ApplicationContext {
    /**
     * Bean 定义Map   存储Bean 定义对象
     */
    private HashMap<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    /**
     * Bean 实例Map 存储Bean 实例对象
     */
    private HashMap<String, Object> BeanMap = new HashMap<>();


    public ApplicationContext(String basePackage) throws UnsupportedEncodingException {
        scanPackage(basePackage);
    }

    /**
     * 1. 扫描包,扫描需要被创建的类,创建 Bean 定义对象,并放入 Bean 定义Map中
     */
    public void scanPackage(String basePackage) throws UnsupportedEncodingException {
//        String basePackage = "com.shao";

        // 获取类加载器
        ClassLoader classLoader = this.getClass().getClassLoader();
        // 获取包路径
        String path = classLoader.getResource(basePackage.replace(".", "/")).getPath();
        // 路径解码,如果路径有空格或者中文,会出现 16 进制的字符
        String packagePath = URLDecoder.decode(path, "UTF-8");

        // 创建文件对象
        File fileDir = new File(packagePath);

        // 获取包下的文件列表
        File[] files = fileDir.listFiles();

        for (File file : files) {
            if (file.isFile()) {
                // 判断是否是class文件
                if (file.getName().endsWith(".class")) {

                    // 包路径 + 文件名 构成全类名
                    String className = basePackage + "." + file.getName().replace(".class", "");

                    try {
                        // 动态加载了名为 className 的类,获取该类的 Class 对象
                        Class<?> aClass = Class.forName(className);

                        // 判断该类是否有 @Service 或 @Controller 注解
                        if (aClass.isAnnotationPresent(Service.class) ||
                                aClass.isAnnotationPresent(Controller.class)) {

                            System.out.println("需要创建:" + className);

                            // 将该类的类名首字母小写作为 id
                            String id = aClass.getSimpleName().substring(0, 1).toLowerCase() + aClass.getSimpleName().substring(1);
                            System.out.println("id:" + id);

                            // 创建 Bean 定义对象,并放入 Bean 定义Map中
                            beanDefinitionMap.put(id, new BeanDefinition(id, className));
                        }

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

                }

            } else if (file.isDirectory()) {
                // 递归扫描子文件夹
                String subPackagePath = basePackage + "." + file.getName();
                scanPackage(subPackagePath);
            }
        }

    }
}

4. 使用自定义注解

5. 在 Servlet 中初始化 IOC 对象

这里为了方便测试,所以先在 Servlet 中初始化 IOC 

6. 测试

3. 实例化 Bean 

遍历 Bean 定义Map ,取出 Bean 定义对象,根据对象的信息使用反射技术创建 Bean 对象,这个时候这个 Bean 也叫裸Bean,然后存入 Bean Map 中。这一步在 Bean 的生命周期中属于 Bean 的实例化

package com.shao.IOC;

import com.shao.Annotation.Controller;
import com.shao.Annotation.Service;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

/**
 * ApplicationContext 类. 当作 IOC 容器
 *
 * @author shao.
 */
public class ApplicationContext {
    /**
     * Bean 定义Map   存储Bean 定义对象
     */
    private HashMap<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    /**
     * Bean 实例Map 存储Bean 实例对象
     */
    private HashMap<String, Object> BeanMap = new HashMap<>();

    public ApplicationContext(String basePackage) throws UnsupportedEncodingException {
        scanPackage(basePackage);
        createBean();
    }

    /**
     * 1. 扫描包,扫描需要被创建的类,创建 Bean 定义对象,并放入 Bean 定义Map中
     */
    public void scanPackage(String basePackage) throws UnsupportedEncodingException {

        // 获取类加载器
        ClassLoader classLoader = this.getClass().getClassLoader();
        // 获取包路径
        String path = classLoader.getResource(basePackage.replace(".", "/")).getPath();
        // 路径解码,如果路径有空格或者中文,会出现 16 进制的字符
        String packagePath = URLDecoder.decode(path, "UTF-8");

        // 创建文件对象
        File fileDir = new File(packagePath);

        // 获取包下的文件列表
        File[] files = fileDir.listFiles();

        for (File file : files) {
            if (file.isFile()) {
                // 判断是否是class文件
                if (file.getName().endsWith(".class")) {

                    // 包路径 + 文件名 构成全类名
                    String className = basePackage + "." + file.getName().replace(".class", "");

                    try {
                        // 动态加载了名为 className 的类,获取该类的 Class 对象
                        Class<?> aClass = Class.forName(className);

                        // 判断该类是否有 @Service 或 @Controller 注解
                        if (aClass.isAnnotationPresent(Service.class) ||
                                aClass.isAnnotationPresent(Controller.class)) {

                            System.out.println("需要创建:" + className);

                            // 将该类的类名首字母小写作为 id
                            String id = aClass.getSimpleName().substring(0, 1).toLowerCase() + aClass.getSimpleName().substring(1);
                            System.out.println("id:" + id);

                            // 创建 Bean 定义对象,并放入 Bean 定义Map中
                            beanDefinitionMap.put(id, new BeanDefinition(id, className));
                        }

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

                }

            } else if (file.isDirectory()) {
                // 递归扫描子文件夹
                String subPackagePath = basePackage + "." + file.getName();
                scanPackage(subPackagePath);
            }
        }

    }

    /**
     * 2. 创建 Bean 实例,并放入 Bean Map 中
     */
    public void createBean() {

        // 获取 Bean 定义 Map 的 所有 id
        Set<String> ids = beanDefinitionMap.keySet();
        Iterator<String> it = ids.iterator();
        while (it.hasNext()) {
            String id = it.next();

            // 获取 Bean 定义对象
            BeanDefinition beanDefinition = beanDefinitionMap.get(id);

            // 获取 Bean 定义对象中的 Bean 的全类名
            String className = beanDefinition.getClassName();

            try {
                // 动态加载类,并创建 Bean 实例,这时候的 Bean 是裸Bean
                Object bean = Class.forName(className).newInstance();

                // 将 Bean 实例放到 Bean Map 中
                BeanMap.put(id, bean);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("实例化 Bean 完成");
        System.out.println(BeanMap);
    }
}

测试

4. 设置属性

给Bean 对象做初始化,也就是依赖注入。Bean 的生命周期中属于 Bean 的初始化

这里是使用类作为属性进行依赖注入,不是接口,后续需要优化

1. 创建一个Controller

使用 @Autowired 注解,这里为了方便测试,重写一下 toString 方法

2. 属性赋值
package com.shao.IOC;

import com.shao.Annotation.Autowired;
import com.shao.Annotation.Controller;
import com.shao.Annotation.Service;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

/**
 * ApplicationContext 类. 当作 IOC 容器
 *
 * @author shao.
 */
public class ApplicationContext {
    /**
     * Bean 定义Map   存储Bean 定义对象
     */
    private HashMap<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    /**
     * Bean 实例Map 存储Bean 实例对象
     */
    private HashMap<String, Object> BeanMap = new HashMap<>();

    public ApplicationContext(String basePackage) throws UnsupportedEncodingException {
        scanPackage(basePackage);
        createBean();
        injectBean();
    }

    /**
     * 1. 扫描包,扫描需要被创建的类,创建 Bean 定义对象,并放入 Bean 定义Map中
     */
    public void scanPackage(String basePackage) throws UnsupportedEncodingException {

        // 获取类加载器
        ClassLoader classLoader = this.getClass().getClassLoader();
        // 获取包路径
        String path = classLoader.getResource(basePackage.replace(".", "/")).getPath();
        // 路径解码,如果路径有空格或者中文,会出现 16 进制的字符
        String packagePath = URLDecoder.decode(path, "UTF-8");

        // 创建文件对象
        File fileDir = new File(packagePath);

        // 获取包下的文件列表
        File[] files = fileDir.listFiles();

        for (File file : files) {
            if (file.isFile()) {
                // 判断是否是class文件
                if (file.getName().endsWith(".class")) {

                    // 包路径 + 文件名 构成全类名
                    String className = basePackage + "." + file.getName().replace(".class", "");

                    try {
                        // 动态加载了名为 className 的类,获取该类的 Class 对象
                        Class<?> aClass = Class.forName(className);

                        // 判断该类是否有 @Service 或 @Controller 注解
                        if (aClass.isAnnotationPresent(Service.class) ||
                                aClass.isAnnotationPresent(Controller.class)) {

                            System.out.println("需要创建:" + className);

                            // 将该类的类名首字母小写作为 id
                            String id = aClass.getSimpleName().substring(0, 1).toLowerCase() + aClass.getSimpleName().substring(1);
                            System.out.println("id:" + id);

                            // 创建 Bean 定义对象,并放入 Bean 定义Map中
                            beanDefinitionMap.put(id, new BeanDefinition(id, className));
                        }

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

                }

            } else if (file.isDirectory()) {
                // 递归扫描子文件夹
                String subPackagePath = basePackage + "." + file.getName();
                scanPackage(subPackagePath);
            }
        }

    }

    /**
     * 2. 创建 Bean 实例,并放入 Bean Map 中
     */
    public void createBean() {

        // 获取 Bean 定义 Map 的 所有 id
        Set<String> ids = beanDefinitionMap.keySet();
        Iterator<String> it = ids.iterator();
        while (it.hasNext()) {
            String id = it.next();

            // 获取 Bean 定义对象
            BeanDefinition beanDefinition = beanDefinitionMap.get(id);

            // 获取 Bean 定义对象中的 Bean 的全类名
            String className = beanDefinition.getClassName();

            try {
                // 动态加载类,并创建 Bean 实例,这时候的 Bean 是裸Bean
                Object bean = Class.forName(className).newInstance();

                // 将 Bean 实例放到 Bean Map 中
                BeanMap.put(id, bean);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("实例化 Bean 完成");
        System.out.println(BeanMap);
    }

    /**
     * 3. 给 Bean 设置属性,Autowired 赋值
     */
    public void injectBean() {

        // 获取 Bean Map 的 所有 id
        Iterator<String> it = BeanMap.keySet().iterator();
        while (it.hasNext()) {

            String id = it.next();

            // 获取 Bean 实例
            Object bean = BeanMap.get(id);

            // 获取 Bean 的 Class 对象
            Class<?> aClass = bean.getClass();

            // 获取这个类的所有属性,不包括父类的属性
            Field[] declaredFields = aClass.getDeclaredFields();

            for (Field field : declaredFields) {

                // 判断该属性是否有 @Autowired 注解
                if (field.isAnnotationPresent(Autowired.class)) {
                    // 设置属性可访问
                    field.setAccessible(true);

                    try {
                        // 给属性赋值
                        field.set(bean, BeanMap.get(field.getName()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
        System.out.println("属性赋值完成");
        System.out.println(BeanMap);
    }
}

3. 测试

四、使用 Bean

1. 创建获取 Bean 的方法

要使用 Bean ,需要从IOC 容器中获取 Bean,所以还需要在 IOC 容器中提供获取 Bean 的接口

package com.shao.IOC;

import com.shao.Annotation.Autowired;
import com.shao.Annotation.Controller;
import com.shao.Annotation.Service;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

/**
 * ApplicationContext 类. 当作 IOC 容器
 *
 * @author shao.
 */
public class ApplicationContext {
    /**
     * Bean 定义Map   存储Bean 定义对象
     */
    private HashMap<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    /**
     * Bean 实例Map 存储Bean 实例对象
     */
    private HashMap<String, Object> BeanMap = new HashMap<>();

    public ApplicationContext(String basePackage) throws UnsupportedEncodingException {
        scanPackage(basePackage);
        createBean();
        injectBean();
    }

    /**
     * 1. 扫描包,扫描需要被创建的类,创建 Bean 定义对象,并放入 Bean 定义Map中
     */
    public void scanPackage(String basePackage) throws UnsupportedEncodingException {

        // 获取类加载器
        ClassLoader classLoader = this.getClass().getClassLoader();
        // 获取包路径
        String path = classLoader.getResource(basePackage.replace(".", "/")).getPath();
        // 路径解码,如果路径有空格或者中文,会出现 16 进制的字符
        String packagePath = URLDecoder.decode(path, "UTF-8");

        // 创建文件对象
        File fileDir = new File(packagePath);

        // 获取包下的文件列表
        File[] files = fileDir.listFiles();

        for (File file : files) {
            if (file.isFile()) {
                // 判断是否是class文件
                if (file.getName().endsWith(".class")) {

                    // 包路径 + 文件名 构成全类名
                    String className = basePackage + "." + file.getName().replace(".class", "");

                    try {
                        // 动态加载了名为 className 的类,获取该类的 Class 对象
                        Class<?> aClass = Class.forName(className);

                        // 判断该类是否有 @Service 或 @Controller 注解
                        if (aClass.isAnnotationPresent(Service.class) ||
                                aClass.isAnnotationPresent(Controller.class)) {

                            System.out.println("需要创建:" + className);

                            // 将该类的类名首字母小写作为 id
                            String id = aClass.getSimpleName().substring(0, 1).toLowerCase() + aClass.getSimpleName().substring(1);
                            System.out.println("id:" + id);

                            // 创建 Bean 定义对象,并放入 Bean 定义Map中
                            beanDefinitionMap.put(id, new BeanDefinition(id, className));
                        }

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

                }

            } else if (file.isDirectory()) {
                // 递归扫描子文件夹
                String subPackagePath = basePackage + "." + file.getName();
                scanPackage(subPackagePath);
            }
        }

    }

    /**
     * 2. 创建 Bean 实例,并放入 Bean Map 中
     */
    public void createBean() {

        // 获取 Bean 定义 Map 的 所有 id
        Set<String> ids = beanDefinitionMap.keySet();
        Iterator<String> it = ids.iterator();
        while (it.hasNext()) {
            String id = it.next();

            // 获取 Bean 定义对象
            BeanDefinition beanDefinition = beanDefinitionMap.get(id);

            // 获取 Bean 定义对象中的 Bean 的全类名
            String className = beanDefinition.getClassName();

            try {
                // 动态加载类,并创建 Bean 实例,这时候的 Bean 是裸Bean
                Object bean = Class.forName(className).newInstance();

                // 将 Bean 实例放到 Bean Map 中
                BeanMap.put(id, bean);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        System.out.println("实例化 Bean 完成");
        System.out.println(BeanMap);
    }

    /**
     * 3. 给 Bean 设置属性,Autowired 赋值
     */
    public void injectBean() {

        // 获取 Bean Map 的 所有 id
        Iterator<String> it = BeanMap.keySet().iterator();
        while (it.hasNext()) {

            String id = it.next();

            // 获取 Bean 实例
            Object bean = BeanMap.get(id);

            // 获取 Bean 的 Class 对象
            Class<?> aClass = bean.getClass();

            // 获取这个类的所有属性,不包括父类的属性
            Field[] declaredFields = aClass.getDeclaredFields();

            for (Field field : declaredFields) {

                // 判断该属性是否有 @Autowired 注解
                if (field.isAnnotationPresent(Autowired.class)) {
                    // 设置属性可访问
                    field.setAccessible(true);

                    try {
                        // 给属性赋值
                        field.set(bean, BeanMap.get(field.getName()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }

        }
        System.out.println("属性赋值完成");
        System.out.println(BeanMap);
    }

    /**
     * 对外提供获取 Bean 的接口
     */
    public Object GetBean(String id) {
        return BeanMap.get(id);
    }

    public <T> T GetBean(Class<T> clazz) {
        // 获取类名,首字母小写
        String id = clazz.getSimpleName().substring(0, 1).toLowerCase() + clazz.getSimpleName().substring(1);
        // 获取 Bean 实例
        Object value = BeanMap.get(id);

        // 判断 value 是否为 clazz 类型
        if (clazz.isInstance(value)) {
            // 安全的转换类型
            return clazz.cast(value);
        } else {
            return null;
        }
    }
}

2. 使用 Bean 

这里为了方便测试,在Servlet 中获取 Bean,然后调用 Bean 实例的方法

3. 测试

五、优化 IOC 容器

现在需要注入依赖的属性是类,不是接口,需要改成给接口赋值其 实现类的对象


原文地址:https://blog.csdn.net/LearnTech_123/article/details/142682954

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!