手写简单实现IOC

这个小demo是利用反射从最基础一步一步模拟实现了IOC的功能,所有的代码基本都给出了注释,方便大家阅读.

目录结构:

这里需要导入一下junit依赖

   <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

 两个工具类:

TestController类:

package com.heaboy.springioc.entity;

import com.heaboy.springioc.stereotype.Autowired;
import com.heaboy.springioc.stereotype.Component;
import sun.misc.Contended;

@Component
public class TestController {
    @Autowired
    private UserService userService;
    public void test(){
        //通过反射拿到userService对象调用addUser方法
        userService.addUser("zhangsan",111);
    }
}

UserService类:

package com.heaboy.springioc.entity;

import com.heaboy.springioc.stereotype.Component;

@Component
public class UserService {
    //接受传参 并且输出
    public void addUser(String name,int age){
        System.out.println(name);
        System.out.println(age);
    }
}

两个注解类:

Autowired:

package com.heaboy.springioc.stereotype;

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 {
}

Component:

package com.heaboy.springioc.stereotype;

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 {
}

最重要的模拟IOC类:

package com.heaboy.springioc.ioc;

import com.heaboy.springioc.stereotype.Autowired;
import com.heaboy.springioc.stereotype.Component;

import java.io.File;
import java.io.FileNotFoundException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

public class SpringIOC {
    //声明存放bean名称的列表
    private List<String> beanNames;
    //声明存放文件路径的列表
    private List<String> filePaths;
    //声明基础路径字符串
    private String basePath;
    //声明基础包名字符串
    private String basePackage;
    //声明存放bean对象的映射
    private Map<String,Object> beans = new HashMap<>();
    /**
     * 扫描所有的文件信息,存放到filePaths
     */
    private void scan() throws FileNotFoundException{
        //创建File 对象表示基础路径
        File file = new File(basePath);
        //初始化存放文件路径的列表
        filePaths = new ArrayList<>();
        //判断文件是否存在
        if(file.exists()){
            //定义一个列表 用于逐层深入的遍历每个文件(广度优先搜索)
            Queue<File> queue = new LinkedList<>();
            //将根文件添加到队列当中
            queue.add(file);
            //判断队列是否为空
            while (!queue.isEmpty()){
                //取出 根文件赋值给poll便于逐层深入
                File poll = queue.poll();
                //如果poll是空说明他下面已经不存在文件和文件路径了 直接跳过执行下一步
                if(poll == null){
                    continue;
                }
                //如果是文件夹
                if(poll.isDirectory()){
                    //创建一个File类型的数组 拿到poll里面的全部文件,文件夹
                    File[] files = poll.listFiles();
                    for (File file1:files){
                        //将poll里面的全部文件夹以及文件加入队列
                        queue.add(file1);
                    }
                    //如果此poll是文件
                }else {
                    //直接将文件的路径添加到filePaths列表中
                    filePaths.add(poll.getPath());
                }
            }
        }else {
            //如果没找到文件
            //抛出文件未找到异常
            throw new FileNotFoundException(basePath+"not found");
        }
    }
    /**
     * 将所有的.java结尾的 全限定名放到 beanNames 列表里面
     */
    public void initBeanNames(){
        //遍历 filePaths里面的每一个文件的路径
        for (String s :filePaths){
            //将文件中的 基础路径部分替换成空串(因为所有文件路径的基础路径都是一样的,
            // 我们需要获取的是以.java结尾的文件的全限定名 前缀需要去掉)
            String replace = s.replace(basePath,"");
            //判断文件的全限定名是不是以.java结尾
            if (replace.endsWith(".java")){
                //截取掉.java
                replace = replace.substring(0,replace.length()-5);
            }
            //将字符串转换成字符数组 目的是为了方便把路径中的\\替换成.
            char[] chars = replace.toCharArray();
            for (int i=0;i<chars.length;i++){
                if(chars[i]=='\\'){
                    chars[i]='.';
                }
            }
            //拼接字符串 拼接出文件在该项目中的源根地址
            beanNames.add(basePackage+"."+new String(chars));
        }
    }
    /**
     * 初始化所有的Bean对象 根据上个方法求出来的所有文件的源根地址
     * 遍历每一个文件(其实就是java文件) 看四否含有Component注解(也就是被标记为
     * 它表明这个类会被 Spring IoC 容器自动检测和注册为 Spring 的一个 bean。
     * 也就是把类交给spring管理)
     */
    public void initBeans(){
        for (String beanName : beanNames){
            try {
                //根据路径 拿到具体的类对象
                Class<?> aClass = Class.forName(beanName);
                //拿到这个类声明的所有注解
                Annotation[] declaredAnnotations = aClass.getDeclaredAnnotations();
                //遍历所有注解 看是否存在@Component注解
                for (Annotation declaredAnnotation : declaredAnnotations){
                    //如果是Component注解
                    if(declaredAnnotation instanceof Component){
                        //创建该类的实例化对象
                        Object o =aClass.newInstance();
                        //将对象放入beans映射中
                        //aClass.getName() 类名  o 实例化对象
                        beans.put(aClass.getName(),o);
                    }
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        //依次遍历 查看是否有Autowired注解 如果有,给对应字段附上相应的实例化对象
        for (Map.Entry<String,Object> entry : beans.entrySet()){
            //利用反射获取到对象对应类的所有声明字段
            Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
            //遍历所有字段 获取每个字段声明的注解看是否包含Autowired注解
            for (Field field : declaredFields){
                Annotation[] annotations = field.getDeclaredAnnotations();
                for (Annotation annotation : annotations){
                    if(annotation instanceof Autowired){
                        //是Autowired注解我们需要为对应的变量 付给他对应的对象
                        //先获取该字段的类型对应的对象名称
                        String name = field.getType().getName();
                        //从beans里获取这个名字的对象
                        Object o = beans.get(name);
                        //设置字段可访问
                        field.setAccessible(true);
                        try {
                            //给字段赋值
                            //也就是说 原本没有赋值的时候entry对象里对应的字段的值是一个空的对象,
                            //然后通过set方法把这个字段的值替换成相应的对象
                            field.set(entry.getValue(),o);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }


    }
    /**
     * 根据bean名称获取对应的实例化对象
     * beanName bean的名称
     * 返回值:一个实例化对象
     */
    public Object getInstance(String beanName){
        return beans.get(beanName);
    }

    /**
     * 初始化基础路径和包名
     */
    private void initPath(){
        //初始化文件的路径 你项目所在电脑中的位置
        basePath="D:\\blog1\\ioc\\src\\main\\java\\com\\heaboy\\springioc\\";
        //初始化基础包名
        basePackage = "com.heaboy.springioc";
    }
    /**
     * SpringIOC类的构造方法,初始化路径和扫描文件
     */
    public SpringIOC(){
        //初始化路径和包名
        initPath();
        try {
            scan();//扫描文件
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        //初始化bean名称列表
        beanNames = new ArrayList<>();
        //初始化bean名称
        initBeanNames();
    }

}

测试类:

package com.heaboy.springioc.ioc;


import com.heaboy.springioc.entity.TestController;
import org.junit.Test;


import java.io.FileNotFoundException;

public class SpringIOCTest {
    @Test
    public void testScan() throws FileNotFoundException {
        //创建SpringIOC类型的实例化对象
        SpringIOC springIOC = new SpringIOC();
        //调用SpringIOC的bean对象初始化方法
        springIOC.initBeans();
        //借用SpringIOC中的利用反射创建实例化对象方法 通过传入一个类名获取到实例化对象
        TestController instance = (TestController)springIOC.getInstance(TestController.class.getName());
        //对象调用test方法;
        instance.test();

    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值