自制简易Spring框架

1. 注解类

Spring有三个常用注解分别是@Component、@ComponentScan、@Autowired

1.1 @Component

@Component注解的作用是给要注入SpringIOC容器的类做标记

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    String name() default "";
}

1.2 @ComponentScan

@ComponentScan用来扫描指定路径下的类是否包含@Component注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    String basePackages() default "";
}

1.3 @Autowired

@Autowired用来自动给某个属性注入被Component标记的类对象

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}

2. ApplicationContext

ApplicationContext因此也称之为Spring上下文。Spring容器负责管理Bean与Bean之间的依赖关系。

这个类需要做两件事情

  1. 扫描配置信息中指定包下的类
  2. 实例化扫描到的类

在这里插入图片描述

2.1 扫描类信息

首先我们先做扫描类信息的功能,扫描类信息需要做两件事

  1. 解析配置类获取到我指定的扫描包路径
  2. 使用获取到的扫描包路径进行文件遍历操作如果是目录则递归进行2否则进入3
  3. 被Component注解标识的类进行注册
// 1.扫描配置信息中指定包下的类
private void scan(Class<?> configClass) {
    // 解析配置类,获取到扫描包路径
    String basePackages = this.getBasePackages(configClass);
    // 使用扫描包路径进行文件遍历操作
    this.doScan(basePackages);
}

2.1.1 解析配置类,获取到扫描包路径

这个就是简单获取配置类中@ComponentScan注解的basePackages值

使用反射机制解析注解的步骤:

  1. 获取被注解注释的类对象
    Class<Annotation> annotationClass = Annotation.class;
  2. 获取这个类对象中的注解对象
    MyAnnotation myAnnotation = annotationClass.getAnnotation(MyAnnotation.class);
  3. 调用注解对象的方法获取属性值
    myAnnotation.num()

第一步不用做因为注解类是作为参数直接传入的,所以我们只需要做后两步

// 解析配置类,获取到扫描包路径
private String getBasePackages(Class<?> configClass) {
    // 获取这个类对象中的注解对象
    ComponentScan componentScan = configClass.getAnnotation(ComponentScan.class);
    // 调用注解对象的方法获取属性值
    String basePackages = componentScan.basePackages();
    return basePackages;
}

2.1.2 使用扫描包路径进行文件遍历操作

// 使用扫描包路径进行文件遍历操作
private void doScan(String basePackages) {
    // 将.转化为/
    String[] split = basePackages.split("\\.");
    String filePath = System.getProperty("user.dir") + "/src/" +  split[0];
    for (int i = 1; i < split.length; i++) {
        filePath += "/" + split[i];
    }


    // 获取资源信息
    File dir = new File(filePath);
    for (File file : dir.listFiles()) {
        if (file.isDirectory()) {
            // 递归扫描
            doScan(basePackages + "." + file.getName());
        }
        else {
            // com.my.spring.example + . + Boy.class -> com.my.spring.example.Boy
            String className = basePackages + "." + file.getName().replace(".java", "");
            // 将class存放到classMap中
            this.registerClass(className);
        }
    }
}

2.1.3 注册含有Component注解的类

我们需要注册也就是存储含有Component的类,这里为了保证线程安全采用了ConcurrentHashMap

步骤:

  1. 使用类加载器通过类名加载类信息
  2. 获取这个类的Component注解
  3. 如果存在Component注解则将其放入类信息池中
// 如果该类含有Component注解那么就将其加入到classMap中
private void registerClass(String className){
    // 使用类加载器通过类名加载类信息
    ClassLoader classLoader = ClassLoader.getSystemClassLoader();
    try {
        Class<?> clazz = classLoader.loadClass(className);
        // 获取这个类的Component注解
        Component component = clazz.getAnnotation(Component.class);
        // 如果存在Component注解则将其放入类信息池中
        if (component != null) {
            classPool.put(className,clazz);
        }
    } catch (ClassNotFoundException e) {
        System.out.println("类加载失败");
        e.printStackTrace();
    }
}

2.2 实例化扫描到的类

首先我们要用一个ConcurrentHashMap来存储bean对象

// 定义一个线程安全的类对象池
private Map<String, Object> beanPool = new ConcurrentHashMap<>();

流程图:

在这里插入图片描述

2.2.1 实例化扫描到的类

// 2. 实例化扫描到的类
private void instantiateBean() {
    // 将classPool中的所有值都实例化,并获取
    for (Class<?> clazz : classPool.values()) {
        getBean(clazz);
    }
}

2.2.2 获取bean对象

private Object getBean(Class<?> clazz) {
    // 先从缓存中获取
    Object bean = beanPool.get(clazz.getName());
    if (bean != null) return bean;
    // 如果没有则创建
    return createBean(clazz);
}

2.2.3 创建bean对象

// 创建bean对象
private Object createBean(Class<?> clazz) {
    // 1. 实例化bean
    Object bean = this.newInstance(clazz);
    // 2. 填充字段,将含有Autowired注解的字段注入bean对象
    try {
        this.populateBean(bean, clazz);
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    // 3. 实例好并且注入完的bean放入beanPool中
    beanPool.put(clazz.getName(),bean);
    return bean;
}

2.2.4 实例化bean

private Object newInstance(Class<?> clazz) {
    Object bean = null;
    try {
        bean = clazz.getDeclaredConstructor().newInstance();
    } catch (InstantiationException e) {
        System.out.println("bean实例化失败");
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        System.out.println("bean实例化失败");
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        System.out.println("bean实例化失败");
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        System.out.println("bean实例化失败");
        e.printStackTrace();
    }
    return bean;
}

2.2.5 填充字段,将含有Autowired注解的字段注入bean对象

// 填充字段,将含有Autowired注解的字段注入bean对象
private void populateBean(Object bean, Class<?> clazz) throws IllegalAccessException {
    // 解析class信息,判断类中是否有需要进行依赖注入的字段
    Field[] fields = clazz.getDeclaredFields();
    // 遍历每一个属性
    for (Field field : fields) {
        // 获取属性的Autowired注解
        Autowired autowired = field.getAnnotation(Autowired.class);
        // 如果有Autowired这个注解
        if (autowired != null) {
            // 解析这个属性对应的类
            Object value = resolveBean(field.getType());
            // 爆破,因为属性可能是private
            field.setAccessible(true);
            // 将value值保存到bean对象的属性中
            field.set(bean,value);
        }
    }
}

2.2.6 解析bean用来实现声明接口时注入其子类对象

// 解析bean用来实现声明接口时注入其子类对象
private Object resolveBean(Class<?> clazz){
    // 判断clazz是不是一个接口
    if (clazz.isInterface()) {
        // 遍历classPool
        for (Map.Entry<String,Class<?>> entry : classPool.entrySet()) {
            // 如果clazz对应的类是entry对应的类的父类或接口
            if (clazz.isAssignableFrom(entry.getValue())) {
                return getBean(entry.getValue());
            }
        }
        throw new RuntimeException("找不到可以注入的bean");
    }
    else {
        return getBean(clazz);
    }
}

2.3 组合

public class ApplicationContext {

    // 定义一个线程安全的类信息池
    private Map<String, Class<?>> classPool = new ConcurrentHashMap<>();
    // 定义一个线程安全的类对象池
    private Map<String, Object> beanPool = new ConcurrentHashMap<>();

    public ApplicationContext(Class<?> configClass) {
        // 1.扫描配置信息中指定包下的类
        this.scan(configClass);
        // 2.实例化扫描到的类
        this.instantiateBean();
    }



    // 1.扫描配置信息中指定包下的类
    private void scan(Class<?> configClass) {
        // 解析配置类,获取到扫描包路径
        String basePackages = this.getBasePackages(configClass);
        // 使用扫描包路径进行文件遍历操作
        this.doScan(basePackages);
    }

    // 解析配置类,获取到扫描包路径
    private String getBasePackages(Class<?> configClass) {
        // 获取这个类对象中的注解对象
        ComponentScan componentScan = configClass.getAnnotation(ComponentScan.class);
        // 调用注解对象的方法获取属性值
        String basePackages = componentScan.basePackages();
        return basePackages;
    }

    // 使用扫描包路径进行文件遍历操作
    private void doScan(String basePackages) {
        // 将.转化为/
        String[] split = basePackages.split("\\.");
        String filePath = System.getProperty("user.dir") + "/src/" +  split[0];
        for (int i = 1; i < split.length; i++) {
            filePath += "/" + split[i];
        }


        // 获取资源信息
        File dir = new File(filePath);
        for (File file : dir.listFiles()) {
            if (file.isDirectory()) {
                // 递归扫描
                doScan(basePackages + "." + file.getName());
            }
            else {
                // com.my.spring.example + . + Boy.class -> com.my.spring.example.Boy
                String className = basePackages + "." + file.getName().replace(".java", "");
                // 将class存放到classMap中
                this.registerClass(className);
            }
        }
    }

    // 如果该类含有Component注解那么就将其加入到classMap中
    private void registerClass(String className){
        // 使用类加载器通过类名加载类信息
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        try {
            Class<?> clazz = classLoader.loadClass(className);
            // 获取这个类的Component注解
            Component component = clazz.getAnnotation(Component.class);
            // 如果存在Component注解则将其放入类信息池中
            if (component != null) {
                classPool.put(className,clazz);
            }
        } catch (ClassNotFoundException e) {
            System.out.println("类加载失败");
            e.printStackTrace();
        }
    }

    // 2. 实例化扫描到的类
    private void instantiateBean() {
        // 将classPool中的所有值都实例化,并获取
        for (Class<?> clazz : classPool.values()) {
            getBean(clazz);
        }
    }

    // 获取bean对象
    private Object getBean(Class<?> clazz) {
        // 先从缓存中获取
        Object bean = beanPool.get(clazz.getName());
        if (bean != null) return bean;
        // 如果没有则创建
        return createBean(clazz);
    }

    // 创建bean对象
    private Object createBean(Class<?> clazz) {
        // 1. 实例化bean
        Object bean = this.newInstance(clazz);
        // 2. 填充字段,将含有Autowired注解的字段注入bean对象
        try {
            this.populateBean(bean, clazz);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        // 3. 实例好并且注入完的bean放入beanPool中
        beanPool.put(clazz.getName(),bean);
        return bean;
    }

    // 实例化bean
    private Object newInstance(Class<?> clazz) {
        Object bean = null;
        try {
            bean = clazz.getDeclaredConstructor().newInstance();
        } catch (InstantiationException e) {
            System.out.println("bean实例化失败");
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            System.out.println("bean实例化失败");
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            System.out.println("bean实例化失败");
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            System.out.println("bean实例化失败");
            e.printStackTrace();
        }
        return bean;
    }

    // 填充字段,将含有Autowired注解的字段注入bean对象
    private void populateBean(Object bean, Class<?> clazz) throws IllegalAccessException {
        // 解析class信息,判断类中是否有需要进行依赖注入的字段
        Field[] fields = clazz.getDeclaredFields();
        // 遍历每一个属性
        for (Field field : fields) {
            // 获取属性的Autowired注解
            Autowired autowired = field.getAnnotation(Autowired.class);
            // 如果有Autowired这个注解
            if (autowired != null) {
                // 解析这个属性对应的类
                Object value = resolveBean(field.getType());
                // 爆破,因为属性可能是private
                field.setAccessible(true);
                // 将value值保存到bean对象的属性中
                field.set(bean,value);
            }
        }
    }

    // 解析bean用来实现声明接口时注入其子类对象
    private Object resolveBean(Class<?> clazz){
        // 判断clazz是不是一个接口
        if (clazz.isInterface()) {
            // 遍历classPool
            for (Map.Entry<String,Class<?>> entry : classPool.entrySet()) {
                // 如果clazz对应的类是entry对应的类的父类或接口
                if (clazz.isAssignableFrom(entry.getValue())) {
                    return getBean(entry.getValue());
                }
            }
            throw new RuntimeException("找不到可以注入的bean");
        }
        else {
            return getBean(clazz);
        }
    }
}

3. 包结构以及测试结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值