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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 面试官问我咋实现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();
        }
    }
}
相关文章
|
2月前
|
XML Java 开发者
Spring底层架构核心概念解析
理解 Spring 框架的核心概念对于开发和维护 Spring 应用程序至关重要。IOC 和 AOP 是其两个关键特性,通过依赖注入和面向切面编程实现了高效的模块化和松耦合设计。Spring 容器管理着 Beans 的生命周期和配置,而核心模块为各种应用场景提供了丰富的功能支持。通过全面掌握这些核心概念,开发者可以更加高效地利用 Spring 框架开发企业级应用。
89 18
|
3月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
1月前
|
传感器 监控 安全
智慧工地云平台的技术架构解析:微服务+Spring Cloud如何支撑海量数据?
慧工地解决方案依托AI、物联网和BIM技术,实现对施工现场的全方位、立体化管理。通过规范施工、减少安全隐患、节省人力、降低运营成本,提升工地管理的安全性、效率和精益度。该方案适用于大型建筑、基础设施、房地产开发等场景,具备微服务架构、大数据与AI分析、物联网设备联网、多端协同等创新点,推动建筑行业向数字化、智能化转型。未来将融合5G、区块链等技术,助力智慧城市建设。
|
3月前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
3月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
88 2
|
3月前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
4月前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
303 2
|
4月前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
101 2
|
4月前
|
前端开发 Java Maven
深入解析:如何用 Spring Boot 实现分页和排序
深入解析:如何用 Spring Boot 实现分页和排序
212 2
|
4月前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
87 4

热门文章

最新文章

推荐镜像

更多