面试官问我咋实现Spring框架IOC和DI好吧打趴下,深度解析手动实现Spring框架的IOC与DI功能

简介: 面试官问我咋实现Spring框架IOC和DI好吧打趴下,深度解析手动实现Spring框架的IOC与DI功能

前言


如果还不会 Spring源码编译 可去看看 Spring源码编译

面试官问我咋实现Spring框架IOC和DI好吧打趴下,深度解析手动实现Spring框架的IOC与DI功能




工程搭建


使用 Maven 创建普通 Web 工程:

修改 pom.xml 添加依赖内容如下图:

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.28</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
    </dependency>
</dependencies>





配置 Tomcat


添加 Tomcat 的 jar 包:

点击上图中的 Add Selected 即可完成添加,添加完成了之后你在创建的时候就会出现 Servlet 选项如下图所示:

创建一个 Servlet 测试一下:

/**
 * 控制器
 *
 * @author yby6
 * @date 2023/09/29
 */
@WebServlet(name = "UserServlet", value = "/UserServlet")
public class UserServlet extends HttpServlet {
    @Override
    public void service(HttpServletRequest req, HttpServletResponse resp) {
        System.out.println("test Servlet");
    }
}

启动 Tomcat 在浏览器中访问:http://127.0.0.1:8080/UserServlet 即可测试我的测试结果如下图所示:

好了到了这里咱们的工程就已经创建完毕了,回归到我们文章的主要内容。




整体思路




解析配置

将相关配置加载进内存当中,存入定义好的数据结构,要管理哪些目标对象,通过编程语言进行解析,配置可以使用 xml,properties  或注解的方式。


定位与注册对象

查看哪些类当中标记了注解,定位到了目标对象之后,将对象注册到容器当中管理起来。


注入对象

在用户需要用到对象时,从容器中精确的获取对象,返回给用户给对应的属性进行注入(赋值)


提供通用的工具类

通过封装一些通用的工具,能够方便框架或用户方便进行操作。




创建注解


分别创建如下几个注解:

Component.java

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/9/30 030 9:07
 * @description
 **/
// 作用在类上
@Target(ElementType.TYPE)
// 注解的生命周期为RUNTIME,因为使用反射创建对象
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}


Controller.java

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/9/30 030 9:07
 * @description
 **/
// 作用在类上
@Target(ElementType.TYPE)
// 注解的生命周期为RUNTIME,因为使用反射创建对象
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}


Repository.java

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/9/30 030 9:07
 * @description
 **/
// 作用在类上
@Target(ElementType.TYPE)
// 注解的生命周期为RUNTIME,因为使用反射创建对象
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
}


Service.java

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/9/30 030 9:07
 * @description
 **/
// 作用在类上
@Target(ElementType.TYPE)
// 注解的生命周期为RUNTIME,因为使用反射创建对象
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
}




获取指定包下所有的类


指定范围,获取范围内的所有类,遍历所有类,获取被注解标记的类并加载进容器里,使用 classLoad 获取资源路径,直接使用 top.it6666 包是无法定位到对应的文件夹所在的路径,必须拿到具体路径,才能获取到该路径下所有 .class 文件。


ClassLoad 的作用:根据一个指定的类的名称,找到或者生成其对应的字节码,加载 Java 应用所需的资源:图片文件、配置文件、文件目录。




获取 URL 中文件与目录




根据文件与目录提取所有的 .class 文件


最终 ClassUtil.java 的内容如下:

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/9/30 030 9:24
 * @description 获取指定包下所有的类
 **/
@Slf4j
@UtilityClass
public class ClassUtil {
    public static final String FILE_PROTOCOL = "file";
    public static final String CLASS_SUFFIX = ".class";
    /**
     * 获取指定包下所有的类
     *
     * @param packageName 包名
     * @return {@link Set}<{@link Class}<{@link ?}>>
     */
    @SuppressWarnings("all")
    public Set<Class<?>> getPackageClass(String packageName) {
        // 1.获取类加载器
        ClassLoader classLoader = ClassUtil.getClassLoad();
        // 2.通过类加载器获取到加载的资源
        // 2.1把top.it6666转成top/it6666
        // 2.2通过类加载器获取加载的资源
        URL url = classLoader.getResource(packageName.replace(".", "/"));
        if (null == url) {
            log.error("{}:该包下没有任何内容", packageName);
        }
        Set<Class<?>> classSet = null;
        // 判断url是否为文件
        if (Objects.equals(FILE_PROTOCOL, url.getProtocol())) {
            // 创建集合
            classSet = new HashSet<>();
            String path = url.getPath();
            try {
                path = URLDecoder.decode(path, "utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            // 获取资源的实际路径
            File packageDir = new File(path);
            // 从路径当中提取Class文件,放到Set集合
            ClassUtil.getDirClass(classSet, packageDir, packageName);
        }
        return classSet;
    }
    ClassLoader getClassLoad() {
        return Thread.currentThread().getContextClassLoader();
    }
    /**
     * 根据目录提出所有的class文件
     *
     * @param classSet    Ioc容器
     * @param packageDir  文件
     * @param packageName 包名
     */
    private static void getDirClass(Set<Class<?>> classSet, File packageDir, String packageName) {
        // 如果不是一个目录直接结束
        if (!packageDir.isDirectory()) {
            return;
        }
        // 如果是目录,对目录里面的内容进行过滤
        File[] files = packageDir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                if (file.isDirectory()) {
                    return true;
                }
                String absolutePath = file.getAbsolutePath();
                // 判断absolutePath是不是以 .class 后缀结尾
                if (absolutePath.endsWith(CLASS_SUFFIX)) {
                    // 到了这里就代表是 .class 结尾的
                    addToClassSet(absolutePath);
                }
                return false;
            }
            private void addToClassSet(String absolutePath) {
                // absolutePath 有可能是:/D:/aa/aaa 或者是:D:\bb\bb 可以使用 File.separator 来统一代替
                // 把文件目录转成包的形式
                absolutePath = absolutePath.replace(File.separator, ".");
                String className = absolutePath.substring(absolutePath.indexOf(packageName));
                className = className.substring(0, className.lastIndexOf("."));
                try {
                    Class<?> targetClass = Class.forName(className);
                    classSet.add(targetClass);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        });
        if (null != files) {
            Arrays.stream(files).forEach(childFile -> ClassUtil.getDirClass(classSet, childFile, packageName));
        }
    }
}




创建 BeanContainer 容器


BeanContainer.java

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/10/08 008 17:24
 * @description
 **/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings("unused")
public class BeanContainer {
    public static BeanContainer getInstance() {
        return ContainerHolder.HOLDER.instance;
    }
    /**
     * 容器枚举
     *
     * @author yby6
     * @date 2023/10/08
     */
    private enum ContainerHolder {
        /**
         * 持有人
         */
        HOLDER;
        /**
         * 实例
         */
        private final BeanContainer instance;
        /**
         * 容器
         */
        ContainerHolder() {
            instance = new BeanContainer();
        }
    }
}
相关文章
|
8月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
766 2
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
559 2
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
391 32
|
安全 Java API
Spring Boot 功能模块全解析:构建现代Java应用的技术图谱
Spring Boot不是一个单一的工具,而是一个由众多功能模块组成的生态系统。这些模块可以根据应用需求灵活组合,构建从简单的REST API到复杂的微服务系统,再到现代的AI驱动应用。
1653 8
|
机器学习/深度学习 人工智能 JSON
Resume Matcher:增加面试机会!开源AI简历优化工具,一键解析简历和职位描述并优化
Resume Matcher 是一款开源AI简历优化工具,通过解析简历和职位描述,提取关键词并计算文本相似性,帮助求职者优化简历内容,提升通过自动化筛选系统(ATS)的概率,增加面试机会。
1773 18
Resume Matcher:增加面试机会!开源AI简历优化工具,一键解析简历和职位描述并优化
|
人工智能 API 开发者
HarmonyOS Next~鸿蒙应用框架开发实战:Ability Kit与Accessibility Kit深度解析
本书深入解析HarmonyOS应用框架开发,聚焦Ability Kit与Accessibility Kit两大核心组件。Ability Kit通过FA/PA双引擎架构实现跨设备协同,支持分布式能力开发;Accessibility Kit提供无障碍服务构建方案,优化用户体验。内容涵盖设计理念、实践案例、调试优化及未来演进方向,助力开发者打造高效、包容的分布式应用,体现HarmonyOS生态价值。
945 27
|
人工智能 自然语言处理 搜索推荐
ViDoRAG:开源多模态文档检索框架,多智能体推理+图文理解精准解析文档
ViDoRAG 是阿里巴巴通义实验室联合中国科学技术大学和上海交通大学推出的视觉文档检索增强生成框架,基于多智能体协作和动态迭代推理,显著提升复杂视觉文档的检索和生成效率。
1120 8
ViDoRAG:开源多模态文档检索框架,多智能体推理+图文理解精准解析文档
|
监控 安全 Java
Java 开发中基于 Spring Boot 3.2 框架集成 MQTT 5.0 协议实现消息推送与订阅功能的技术方案解析
本文介绍基于Spring Boot 3.2集成MQTT 5.0的消息推送与订阅技术方案,涵盖核心技术栈选型(Spring Boot、Eclipse Paho、HiveMQ)、项目搭建与配置、消息发布与订阅服务实现,以及在智能家居控制系统中的应用实例。同时,详细探讨了安全增强(TLS/SSL)、性能优化(异步处理与背压控制)、测试监控及生产环境部署方案,为构建高可用、高性能的消息通信系统提供全面指导。附资源下载链接:[https://pan.quark.cn/s/14fcf913bae6](https://pan.quark.cn/s/14fcf913bae6)。
2580 0

推荐镜像

更多
  • DNS