Spring 手写IoC

Spring框架的IOC是基于Java反射机制实现的。

实现Spring的IoC

我们知道,IoC(控制反转)和DI(依赖注入)是Spring里面核心的东西,那么,我们如何自己手写出这样的代码呢?下面我们就一步一步写出Spring框架最核心的部分。


1、定义注解

 Bean注解

package com.cj.annotation.anno;

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

@Target(ElementType.TYPE) // 用于描述类、接口(包括注解类型) 或enum声明
@Retention(RetentionPolicy.RUNTIME) // 在运行时生效
public @interface Bean {
}

依赖注入注解

package com.cj.spring.core.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 Di {
}

2、定义bean容器接口

package com.cj.spring.core;

public interface ApplicationContext {

    Object getBean(Class clazz);
}

3、编写注解bean容器接口实现

我们通过构造方法传入包的base路径,扫描被@Bean注解的java对象,完整代码如下

package com.cj.annotation.bean;

import com.cj.annotation.anno.Bean;
import com.cj.annotation.anno.Di;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class AnnotationApplicationContext implements ApplicationContext {

    private static String rootPath;

    // 存储bean的容器
    private HashMap<Class, Object> beanFactory = new HashMap<>();

    /**
     * 根据包扫描加载bean
     *
     * @param basePackage
     */
    public AnnotationApplicationContext(String basePackage) {

        try {
            // 1 把.替换成\
            String packagePath = basePackage.replaceAll("\\.",
                    "\\\\");

            // 2 获取包绝对路径
            Enumeration<URL> urls
                    = Thread.currentThread().getContextClassLoader()
                    .getResources(packagePath);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                String filePath = URLDecoder.decode(url.getFile(),
                        "utf-8");
                // 获取包前面路径部分,字符串截取
                rootPath = filePath.substring(0, filePath.length() - packagePath.length());
                // 包扫描
                loadBean(new File(filePath));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        // 属性注入
        loadDi();
    }


    /**
     * 包扫描
     *
     * @param file 打包构建的路径
     */
    public void loadBean(File file) throws Exception {

        // 1、判断当前是否为文件夹
        if (file.isDirectory()) {

            // 2、获取文件夹中的所有内容
            File[] childrenFiles = file.listFiles();

            // 2.1 当前文件夹是否为空
            if (childrenFiles == null || childrenFiles.length == 0) {
                return;
            }

            // 3、当文件夹不为空的时候,获取文件夹中的全部内容
            for (File child : childrenFiles) {
                // 3.1 判断当前文件是否还是文件夹,如果是,就进行一个 递归 寻找
                if (child.isDirectory()) {
                    loadBean(child);
                } else {

                    // 3.2 在这里得到的 File 已经是文件,当得到包路径 + 类名称 - 字符串截取
                    String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);

                    // 3.3 在判断当前文件是否 .class
                    if (pathWithClass.contains(".class")) {
                        // 3.4 因为要用的是 .class ,要将路径中的 \ 替换成 . ,
                        // 最后将得到为 com.cj.service
                        String allClass = pathWithClass
                                .replaceAll("\\\\", ".")
                                .replace(".class", "");

                        // 3.5 获取类的 Class
                        Class<?> clazz = Class.forName(allClass);

                        // 3.6 判断是不是接口
                        if (!clazz.isInterface()) {

                            // 获取类上的注解
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            // 3.7 判断类上面是否有注解
                            if (annotation != null) {

                                // 3.8 如果有就实例化
                                Object instance = clazz.getConstructor().newInstance();

                                // 3.9 把实例化后的对象放到 map 集合中
                                if (clazz.getInterfaces().length > 0) {
                                    beanFactory.put(clazz.getInterfaces()[0], instance);
                                } else {
                                    beanFactory.put(clazz, instance);
                                }
                            }

                        }
                    }
                }
            }
        }

    }

    /**
     * 属性的输入
     */
    private void loadDi() {
        // 实例化对象在 beanFactory 的 map 集合里边

        // 1、遍历 beanFactory 的 map 集合
        Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();

        for (Map.Entry<Class, Object> entry : entries) {

            // 获取每个 map 对象,每个对象属性取到
            Object obj = entry.getValue();
            // 获取对象 Class
            Class<?> clazz = obj.getClass();
            // 获取每个对象属性
            Field[] declaredFields = clazz.getDeclaredFields();

            // 遍历得到每个对象属性数组,得到每个属性
            for (Field field : declaredFields) {
                Di annotation = field.getAnnotation(Di.class);
                if (annotation != null) {
                    // 为了防止拿不到 私有类型的属性
                    field.setAccessible(true);

                    try {
                        // 如果有 @Di 注解,就把对象进行注入
                        field.set(obj, beanFactory.get(field.getType()));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }

        }

    }

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

}

编写测试场景

package com.cj.annotation.service;

public interface UserService {
    public void add();
}
package com.cj.annotation.service;

import com.cj.annotation.anno.Bean;
import com.cj.annotation.anno.Di;
import com.cj.annotation.dao.UserDao;

@Bean
public class UserServiceImpl implements UserService {

    @Di
    public UserDao userDao;

    public void add() {
        userDao.add();
        System.out.println("Service->add()... ");
    }

}
package com.cj.annotation;

import com.cj.annotation.bean.AnnotationApplicationContext;
import com.cj.annotation.bean.ApplicationContext;
import com.cj.annotation.service.UserService;
import org.junit.jupiter.api.Test;

public class UserControllerTest {

    @Test
    public void test1() {
        ApplicationContext context = new AnnotationApplicationContext("com.cj");
        UserService userService = (UserService) context.getBean(UserService.class);
        System.out.println(userService);
        userService.add();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

你小子在看什么……

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

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

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

打赏作者

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

抵扣说明:

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

余额充值