面试官问我咋实现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();
        }
    }
}
相关文章
|
人工智能 监控 算法
销售易CRM:功能与优势全解析
销售易CRM是国内领先的客户关系管理(CRM)系统,提供强大的销售管理、全方位客户管理、丰富的营销自动化工具、智能AI赋能及灵活的开放性平台。其功能涵盖线索获取、商机管理、客户画像、营销活动策划、智能预测等,支持企业高效管理客户、优化业务流程、提升销售效率和客户满意度。通过灵活的二次开发和API接口,销售易CRM可无缝集成企业现有系统,助力企业在数字化转型中实现业绩高质量增长。
|
弹性计算 运维 安全
优化管理与服务:操作系统控制平台的订阅功能解析
本文介绍了如何通过操作系统控制平台提升系统效率,优化资源利用。首先,通过阿里云官方平台开通服务并安装SysOM组件,体验操作系统控制平台的功能。接着,详细讲解了订阅管理功能,包括创建订阅、查看和管理ECS实例的私有YUM仓库权限。订阅私有YUM仓库能够集中管理软件包版本、提升安全性,并提供灵活的配置选项。最后总结指出,使用阿里云的订阅和私有YUM仓库功能,可以提高系统可靠性和运维效率,确保业务顺畅运行。
新功能上线:云解析DNS-重点域名监控功能发布
新功能上线:云解析DNS-重点域名监控功能发布
|
前端开发 安全 Java
2025春招,Spring 面试题汇总
本文详细整理了2025年春招必备的Spring面试题,分为基础和高级两大部分,帮助求职者全面掌握Spring相关知识点,结合实际项目经验,提升面试成功率。内容涉及Spring框架、AOP、事务管理、数据库集成、Spring Boot、Spring Security、微服务架构等,助力你在春招中脱颖而出。
3614 0
|
存储 前端开发 JavaScript
调用DeepSeek API增强版纯前端实现方案,支持文件上传和内容解析功能
本方案基于DeepSeek API增强版,提供纯前端实现的文件上传与内容解析功能。通过HTML和JavaScript,用户可选择文件并调用API完成上传及解析操作。方案支持多种文件格式(如PDF、TXT、DOCX),具备简化架构、提高响应速度和增强安全性等优势。示例代码展示了文件上传、内容解析及结果展示的完整流程,适合快速构建高效Web应用。开发者可根据需求扩展功能,满足多样化场景要求。
4545 64
|
设计模式 算法 架构师
京东二面:说下spring中常用的设计模式? (一个 深入骨髓的答案, 面试官跪下了)
京东二面:说下spring中常用的设计模式? (一个 深入骨髓的答案, 面试官跪下了)
京东二面:说下spring中常用的设计模式? (一个 深入骨髓的答案, 面试官跪下了)
|
机器学习/深度学习 人工智能 JSON
Resume Matcher:增加面试机会!开源AI简历优化工具,一键解析简历和职位描述并优化
Resume Matcher 是一款开源AI简历优化工具,通过解析简历和职位描述,提取关键词并计算文本相似性,帮助求职者优化简历内容,提升通过自动化筛选系统(ATS)的概率,增加面试机会。
2013 18
Resume Matcher:增加面试机会!开源AI简历优化工具,一键解析简历和职位描述并优化
|
人工智能 API 开发者
HarmonyOS Next~鸿蒙应用框架开发实战:Ability Kit与Accessibility Kit深度解析
本书深入解析HarmonyOS应用框架开发,聚焦Ability Kit与Accessibility Kit两大核心组件。Ability Kit通过FA/PA双引擎架构实现跨设备协同,支持分布式能力开发;Accessibility Kit提供无障碍服务构建方案,优化用户体验。内容涵盖设计理念、实践案例、调试优化及未来演进方向,助力开发者打造高效、包容的分布式应用,体现HarmonyOS生态价值。
1162 27
|
人工智能 自然语言处理 搜索推荐
ViDoRAG:开源多模态文档检索框架,多智能体推理+图文理解精准解析文档
ViDoRAG 是阿里巴巴通义实验室联合中国科学技术大学和上海交通大学推出的视觉文档检索增强生成框架,基于多智能体协作和动态迭代推理,显著提升复杂视觉文档的检索和生成效率。
1249 8
ViDoRAG:开源多模态文档检索框架,多智能体推理+图文理解精准解析文档

推荐镜像

更多
  • DNS