代码编织梦想

1. 实现Spring的IOC

我们知道,IOC(控制反转)和DI(依赖注入)是Spring里面核心的东西,那么,我们如何自己手写IOC

  1. 搭建子模块
    搭建模块:guigu-spring,搭建方式如其他spring子模块
  2. 准备测试需要的bean
    添加依赖
<dependencies>
    <!--junit5测试-->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.3.1</version>
    </dependency>
</dependencies>

创建UserDao接口

public interface UserDao {

    public void print();
}

创建UserDaoIm实现


public class UserDaoImpl implements UserDao {

    @Override
    public void print() {
        System.out.println("Dao层执行结束");
    }
}

创建UserService接口

public interface UserService {

    public void out();
}

创建UserServiceImp实现类

@Bean
public class UserServiceImpl implements UserService {

//    private UserDao userDao;

    @Override
    public void out() {
        //userDao.print();
        System.out.println("Service层执行结束");
    }
}
  1. 定义注解
    我们通过注解的形式加载bean与实现依赖注入
    bean注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//注解的作用范围
/*@Target(ElementType.TYPE)——接口、类、枚举、注解
@Target(ElementType.FIELD)——字段、枚举的常量
@Target(ElementType.METHOD)——方法
@Target(ElementType.PARAMETER)——方法参数
@Target(ElementType.CONSTRUCTOR) ——构造函数
@Target(ElementType.LOCAL_VARIABLE)——局部变量
@Target(ElementType.ANNOTATION_TYPE)——注解
@Target(ElementType.PACKAGE)——包,用于记录java文件的package信息
*/
@Target(ElementType.TYPE)
/*@Retention修饰注解,用来表示注解的生命周期
//RetentionPolicy.SOURCE---只是做一些检查性的操作,,比如 @Override 和 @SuppressWarnings
RetentionPolicy.CLASS0---要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife)
要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife)
RetentionPolicy.RUNTIME---需要在运行时去动态获取注解信息
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}

依赖注入注解

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 {
}
  1. 定义bean接口

public interface ApplicationContext {

    Object getBean(Class clazz);
}
  1. 编写扫描bean逻辑
public class AnnotationApplicationContext  implements ApplicationContext{

    //创建map集合,放bean对象
    private Map<Class,Object> beanFactory = new HashMap<>();
    private static String rootPath;

    //返回对象
    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

    //创建有参数构造,传递包路径,设置包扫描规则
    //当前包及其子包,哪个类有@Bean注解,把这个类通过反射实例化
    public AnnotationApplicationContext(String basePackage) {
        // com.atguigu
        try {
            //1 把.替换成\
            String packagePath = basePackage.replaceAll("\\.",
                                                  "\\\\");

            //2 获取包绝对路径
            Enumeration<URL> urls
                    = Thread.currentThread().getContextClassLoader()
                                            .getResources(packagePath);
            while(urls.hasMoreElements()) {
                URL url = urls.nextElement();
                String filePath = URLDecoder.decode(url.getFile(),
                                                  "utf-8");
                //获取包前面路径部分,字符串截取
                rootPath = filePath.substring(0, filePath.length() - packagePath.length());
                //包扫描
                loadBean(new File(filePath));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        //属性注入
        loadDi();
    }

    //包扫描过程,实例化
    private void loadBean(File file) throws Exception {
        //1 判断当前是否文件夹
        if(file.isDirectory()) {
            //2 获取文件夹里面所有内容
            File[] childrenFiles = file.listFiles();
            
            //3 判断文件夹里面为空,直接返回
            if(childrenFiles == null || childrenFiles.length == 0) {
                return;
            }

            //4 如果文件夹里面不为空,遍历文件夹所有内容
            for(File child : childrenFiles) {
                //4.1 遍历得到每个File对象,继续判断,如果还是文件夹,递归
                if(child.isDirectory()) {
                    //递归
                    loadBean(child);
                } else {
                    //4.2 遍历得到File对象不是文件夹,是文件,
                    //4.3 得到包路径+类名称部分-字符串截取
                    String pathWithClass =
                            child.getAbsolutePath().substring(rootPath.length() - 1);

                    //4.4 判断当前文件类型是否.class
                    if(pathWithClass.contains(".class")) {

                        //4.5 如果是.class类型,把路径\替换成.  把.class去掉
                        // com.atguigu.service.UserServiceImpl
                        String allName = pathWithClass.replaceAll("\\\\", ".")
                                .replace(".class", "");

                        //4.6 判断类上面是否有注解 @Bean,如果有实例化过程
                        //4.6.1 获取类的Class
                        Class<?> clazz = Class.forName(allName);
                        //4.6.2 判断不是接口
                        if(!clazz.isInterface()) {
                            //4.6.3 判断类上面是否有注解 @Bean
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if(annotation != null) {
                                //4.6.4 实例化
                                Object instance = clazz.getConstructor().newInstance();
                                //4.7 把对象实例化之后,放到map集合beanFactory
                                //4.7.1 判断当前类如果有接口,让接口class作为map的key
                                if(clazz.getInterfaces().length>0) {
                                    beanFactory.put(clazz.getInterfaces()[0],instance);
                                } else {
                                    beanFactory.put(clazz,instance);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    //属性注入
    private void loadDi() {
        //实例化对象在beanFactory的map集合里面
        //1 遍历beanFactory的map集合
        Set<Map.Entry<Class, Object>> entries = beanFactory.entrySet();
        for (Map.Entry<Class, Object> entry:entries) {
            //2 获取map集合每个对象(value),每个对象属性获取到
            Object obj = entry.getValue();

            //获取对象Class
            Class<?> clazz = obj.getClass();

            //获取每个对象属性获取到
            Field[] declaredFields = clazz.getDeclaredFields();

            //3 遍历得到每个对象属性数组,得到每个属性
            for(Field field:declaredFields) {
                //4 判断属性上面是否有@Di注解
                Di annotation = field.getAnnotation(Di.class);
                if(annotation != null) {
                    //如果私有属性,设置可以设置值
                    field.setAccessible(true);

                    //5 如果有@Di注解,把对象进行设置(注入)
                    try {
                        field.set(obj,beanFactory.get(field.getType()));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_53120322/article/details/129646345

手写ioc容器-爱代码爱编程

Ioc:Inversion of Control的缩写,控制反转、依赖注入。 1.控制的是:java bean的创建与销毁,也就是 生命周期 2.反转的是:将对象的控制权交给了Ioc容器。   下面我将通过一个demo,简单实现ioc。场景:我有2辆车,我要开车回家。车的功能有启动、左转、右转、停车。 1.首先有一个人类接口:他有回家这个方法定义

手写简易ioc容器-爱代码爱编程

前言 本文是为了学习Spring IOC容器的执行过程而写,不能完全代表Spring IOC容器,只是简单实现了容器的依赖注入和控制反转功能,无法用于生产,只能说对理解Spring容器能够起到一定的作用。 开始 创建项目 创建Gradle项目,并修改build.gradle plugins { id 'java' i

spring复习回顾之手写ioc容器_bubbletg的博客-爱代码爱编程

Spring复习回顾之手写IOC容器 手写IOC之前,我们先来看看下面问题: 1. 请问什么是IoC和DI?并且简要说明一下DI是如何实现的? ​ IOC是控制反转,是Inversion of Contro 的缩写。D

从零手写 IOC容器(通俗易懂)-爱代码爱编程

目录 概述 1.Component注解定义 2.Reject注解定义 3.User对象定义 4.UserService实现 5.UserController实现 6.IocContext ioc bean容器 7.IocUtils ioc的依赖注入 8.模拟调用UserController 结果 概述 IOC (Inversion

python ioc_Spring源码分析(三)手写简单的IOC容器和解决循环依赖问题-爱代码爱编程

前言 上一节我们分析了Spring的实例化和IOC过程。在熟悉了Spring的处理流程后,我们自己能不能写一个IOC的容器和实现依赖注入呢?要注意哪些问题呢?本章节我们重点关注两个问题。 手写一个简单的IOC容器并实现依赖注入 分析Spring是怎样解决循环依赖的问题 一、加载配置文件 先来看我们自定义的配置文件,ioc.xml。我们定义了两

ioc java_从零手写 IOC容器-爱代码爱编程

概述 IOC (Inversion of Control) 控制反转。熟悉Spring的应该都知道。那么具体是怎么实现的呢?下面我们通过一个例子说明。 1. Component注解定义 package cn.com.qunar.annotation; import java.lang.annotation.ElementType; impor

Spring ioC笔记-爱代码爱编程

Spring 框架核心源码 1、使用 Spring 框架 2、反射机制 IoC 控制反转 Inverse of Control 创建对象的权限,Java 程序中需要用到的对象不再由程序员自己创建,而是交给 IoC 容器来创建。 IoC 核心思想 1、pom.xml <dependencies> <!-- 引入 Serv

手写IOC容器总结-爱代码爱编程

一个超级简略的IOC容器,代码见: spring-code/src/main/java/com/IOC at main · CodePpoi/spring-code · GitHub 参考了手写一个最简单的IOC容器,从而了解spring的核心原理-技术圈 一开始是自己写ClassLoader,结果发现自己写的ClassLoader不能加载到放在类上面

C#写简单的IOC容器-爱代码爱编程

文章目录 前言一、IOC和DIP1.IOC2.DIP二、写一个超简单的IOC容器实例1.代码准备2.IOC容器3.调用结果总结 前言 IOC个人之前一直搞不明白,不够理解,写这篇文章也是为了加强学习。 一、IOC和DIP 1.IOC IOC中文叫做控制反转:一般开发,上端都依赖下端的对象,类似于在类中new一个其他类对象。这样上端对下端

手写IOC容器(xml,注解)-爱代码爱编程

手写IOC容器(xml,注解) 软件设计模式期末作业的设计内容: 在网上参考了资料,将两份资料,再加上自己的理解融合成一份!! xml的参考资料’君君要上天’手写的IOC容器 注解的参考资料’楠哥教你学Java’手写的IOC容器 1.概念 IOC又称控制反转,把原先我们代码里面需要实现的对象的创建、依赖的代码,反转给容器来帮忙实现。 2.

来,从零手写一个ioc容器_菜鸟厚非的博客-爱代码爱编程

一、简介 IOC(控制翻转)是程序设计的一种思想,其本质就是上端对象不能直接依赖于下端对象,要是依赖的话就要通过抽象来依赖。比如,上端对象如 BLL 层中,需要调用下端对象的 DAL 层时不能直接调用 DAl 的具体实现,而是通过抽象的方式来进行调用。 有这么一个场景,项目本来是用 Sqlserver 来进行数据访问的,那么就会有一个 Sqlserve

spring学习及手写简易ioc容器_carolfinchs的博客-爱代码爱编程

目录 Spring 框架核心源码IoC 核心思想Spring IoC 的使用IoC 基于注解的执行原理 代码实现定义四个自定义注解自定义一个MyAnnotationConfigApplicationContext

java手写ioc_str_null的博客-爱代码爱编程

文章目录 视频教程连接实现的效果实现这些IOC的思路1. 获取到我们要管理的Class对象2.实例化要管理的对象 把初始化之后的对象给放到Map容器里面3.依赖注入的实现 具体实现步骤注解MyBeanDefini

自己搭建ioc容器(c#)(二)实现ioc容器基本功能_海盗1234的博客-爱代码爱编程

实现Ioc容器基本的类和功能 实现简单ioc容器的基本方式:通过一个Dictionary字典集合存储不同类型Type之间的对应关系,然后使用System.Activator.CreateInstance的系统方法,根据Ty

手写ioc容器_手写一个ioc容器-爱代码爱编程

目录 1、Spring IoC 2、反射 3、工厂模式 4、手写IoC容器 4.1 解析xml,获得class路径 4.2 创建工厂 4.3 启动类 4.4 实体类 1、Spring IoC         为什么要使用IoC,原因是之前的对象都是通过手动new的方式进行创建,代码会比较冗余,并且没有统一的管理。而使用了Spri

mybatis开发环境搭建_during mybatis-爱代码爱编程

1.创建工程 2.引入相关的依赖 pom.xml <dependencies> <!--1.引入mybatis包--> <dependen

jdk11下j2cache序列化器反射异常及-爱代码爱编程

问题现象 最近线上部署应用时,发现如下异常: Failed to instantiate [net.oschina.j2cache.CacheChannel]: Factory method 'cacheChannel'