前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >为自己模拟的IOC容器添加上DI注入

为自己模拟的IOC容器添加上DI注入

作者头像
半月无霜
发布2023-03-07 20:02:27
1870
发布2023-03-07 20:02:27
举报
文章被收录于专栏:半月无霜半月无霜

为自己模拟的IOC容器添加上DI注入

一、介绍

上一篇中,模拟Spring实现了一个简易的IOC容器,完成了初始化bean的操作,统一交给了一个Map集合进行管理。

模拟Spring实现一个简易的IOC容器

现在,将为这个IOC容器添加DI注入功能

二、实现

在编写之前,我们先加一个工具类,用来获取接口所实现的子类Class对象,也是通过子类

代码语言:javascript
复制
<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.10.2</version>
</dependency>

代码如下,对比上一次,稍稍做了一点封装,使得步骤更加清晰

代码语言:javascript
复制
package com.banmoon.test.mockioc.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 {

}
代码语言:javascript
复制
package com.banmoon.test.mockioc.core;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.banmoon.test.mockioc.annotation.Bean;
import com.banmoon.test.mockioc.annotation.Di;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;

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

@Slf4j
@SuppressWarnings("all")
public class MyApplicationContext {

    /**
     * 真正的容器存储集合
     */
    private static final Map<String, Object> singleObjects = new HashMap<>();

    /**
     * 当前运行环境的路径
     */
    private static String currentAbsPath = null;

    /**
     * 扫描的包路径
     */
    private String packagePath;

    /**
     * 扫描的包路径反射工具类
     */
    private Reflections packageReflections;

    public MyApplicationContext(String packagePath) throws Exception {
        this.packagePath = packagePath;
        this.packageReflections = new Reflections(packagePath);
        // 1、将包路径中的.变成\
        String basePackage = StrUtil.replace(packagePath, ".", "\\");
        // 2、获取包的绝对路径,我们要获取class包的绝对路径,也就是target里面的那些
        URL url = Thread.currentThread().getContextClassLoader().getResource(basePackage);
        // 3、得到url后还需要进行转码
        if (Objects.nonNull(url)) {
            String filePath = URLDecoder.decode(url.getFile(), "utf-8");
            // 4、为了方便,此处记录target包的绝对路径
            currentAbsPath = filePath.substring(0, filePath.length() - basePackage.length());
            // 5、扫描包里面所有的类
            scanBean(new File(filePath));
        }
    }

    private void scanBean(File file) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 1、判断当前是否文件夹
        if (file.isDirectory()) {
            // 2、获取文件夹中所有的内容,如果为空直接返回
            File[] children = file.listFiles();
            // 3、遍历内容
            if (ArrayUtil.isEmpty(children)) {
                return;
            }
            for (File child : children) {
                // 4.1、如果是文件夹,则进行递归
                if (child.isDirectory()) {
                    scanBean(child);
                } else {
                    // 5.1、如果是文件,则进行判断是否为class文件
                    String pathWithClass = child.getAbsolutePath().substring(currentAbsPath.length() - 1);
                    if (pathWithClass.endsWith(".class")) {
                        // 5.2、反射得到当前文件的Class对象
                        String classPath = StrUtil.replace(pathWithClass, "\\", ".")
                                .replace(".class", "");
                        Class<?> clazz = Class.forName(classPath);
                        initializeBean(clazz, false);
                    }
                }
            }
        }
    }

    public <T> T getBean(String beanName, Class<T> clazz) {
        Object o = singleObjects.get(beanName);
        if (!clazz.isInstance(o)) {
            throw new UnsupportedOperationException("获取的类型错误");
        }
        return (T) o;
    }

    /**
     * 获取bean的名字
     *
     * @param annotation bean注解
     * @param clazz      class对象
     * @return bean的名字
     */
    private String generateBeanName(Bean annotation, Class<?> clazz) {
        String value = annotation.value();
        if (StrUtil.isBlank(value)) {
            Class<?>[] interfaces = clazz.getInterfaces();
            if (ArrayUtil.isNotEmpty(interfaces)) {
                value = StrUtil.lowerFirst(interfaces[0].getSimpleName());
            } else {
                value = StrUtil.lowerFirst(clazz.getSimpleName());
            }
        }
        return value;
    }

    /**
     * 处理DI注解,实现注入属性
     *
     * @param currentObj
     */
    private void diField(Object currentObj, Class<?> clazz) {
        // 1、获取所有属性
        Field[] fields = clazz.getDeclaredFields();
        // 2、遍历属性
        for (Field field : fields) {
            // 3、找到有DI注解的属性
            Di annotation = field.getAnnotation(Di.class);
            if (Objects.nonNull(annotation)) {
                // 4、找到对应属性在容器中的实例对象
                Class<?> fieldClazz = field.getType();
                // 5、在容器中查找bean,没有则进行初始化bean
                Object o = initializeBean(fieldClazz, true);
                // 6、通过反射设置到属性中
                try {
                    field.set(currentObj, o);
                } catch (IllegalAccessException e) {
                    log.error("DI注入异常", e);
                }
            }
        }
    }

    /**
     * 初始化bean
     *
     * @param clazz beanClass对象
     * @param find  是否在容器中进行寻找
     * @return 返回bean
     */
    public Object initializeBean(Class<?> clazz, boolean find) {
        // 1、判断是否是接口,且判断是不是要查找子类
        if (clazz.isInterface() && find) {
            Set<Class<?>> set = packageReflections.getSubTypesOf((Class<Object>) clazz);
            clazz = CollUtil.get(set, 0);
        } else if (clazz.isInterface() && !find) {
            return null;
        }
        // 2、查找上面是否有@bean注解
        Bean beanAnnotation = clazz.getAnnotation(Bean.class);
        if (Objects.isNull(beanAnnotation)) {
            return null;
        }
        try {
            // 3、找到bean名字,获取在容器中的实例对象
            String beanName = generateBeanName(beanAnnotation, clazz);
            // 4、判断是不是需要查找bean
            Object o = null;
            if (find) {
                o = singleObjects.get(beanName);
            }
            // 5、如果在容器中没有找到,则进行初始化
            if (Objects.isNull(o)) {
                Constructor<?> constructor = clazz.getConstructor();
                Object obj = constructor.newInstance();
                // 6、将实例化后的对象,放入map容器中
                singleObjects.put(beanName, obj);
                // 7、处理属性,DI注入属性
                diField(obj, clazz);
            }
            return o;
        } catch (Exception exception) {
            log.error("初始化bean异常", exception);
            return null;
        }
    }

}

三、测试

同样,service及其实现类,dao及其实现类

代码语言:javascript
复制
package com.banmoon.test.mockioc.service;

public interface TestService {

    void hello();
}
代码语言:javascript
复制
package com.banmoon.test.mockioc.service.impl;

import com.banmoon.test.mockioc.annotation.Bean;
import com.banmoon.test.mockioc.annotation.Di;
import com.banmoon.test.mockioc.dao.TestDao;
import com.banmoon.test.mockioc.service.TestService;

@Bean
public class TestServiceImpl implements TestService {

    @Di
    public TestDao testDao;

    @Override
    public void hello() {
        System.out.println("service hello...");
        testDao.hello();
    }
}
代码语言:javascript
复制
package com.banmoon.test.mockioc.dao;

public interface TestDao {

    void hello();
}
代码语言:javascript
复制
package com.banmoon.test.mockioc.dao.impl;

import com.banmoon.test.mockioc.annotation.Bean;
import com.banmoon.test.mockioc.dao.TestDao;

@Bean
public class TestDaoImpl implements TestDao {

    @Override
    public void hello() {
        System.out.println("dao hello...");
    }
}

service实现类上面有dao类型的属性,并加上了@Di注解,试试能不能成功注入

代码语言:javascript
复制
package com.banmoon.test.mockioc;

import com.banmoon.test.mockioc.core.MyApplicationContext;
import com.banmoon.test.mockioc.service.TestService;

public class Test {

    public static void main(String[] args) throws Exception {
        MyApplicationContext context = new MyApplicationContext("com.banmoon.test.mockioc");
        TestService testService = context.getBean("testService", TestService.class);
        testService.hello();
    }

}

运行查看结果,成功

image-20230303141513459
image-20230303141513459

四、最后

其实,这也是最为简单的注入,我就问问

都说Spring使用了三级缓存,那么这三级缓存是怎么使用的,它有什么作用呢?

我是半月,你我一同共勉!!!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-03-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为自己模拟的IOC容器添加上DI注入
    • 一、介绍
      • 二、实现
        • 三、测试
          • 四、最后
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档