目录

1.引言

1.1介绍依赖注入(DI)和控制反转(IoC)的概念

1.2 为什么需要自己实现一个简单的DI框架

1.3 本项目的实际应用场景

2.核心设计思路

2.1 整体架构设计

2.2关键注解的设计([@AutoWried]

2.3 自动扫描和注入流程

3.核心代码解析

3.1 [BeanCollection]类的实现细节

3.2 反射机制的应用

3.3类路径扫描逻辑

4.运行效果展示

4.1测试代码构成

4.2输出结果


1.引言


1.1介绍依赖注入(DI)和控制反转(IoC)的概念

控制反转(IoC)
    传统方式:对象自己创建和管理依赖关系
    IoC方式:将对象的创建和依赖关系的管理交给外部容器
    核心思想:对象不主动寻找依赖,而是被动接收依赖注入
依赖注入(DI)
    定义:一种实现IoC的具体方式
    工作机制:容器在运行期动态地将依赖关系注入到组件中
    三种注入方式:构造器注入 ,setter方法注入 , 字段注入(如本项目中的@AutoWried)


1.2 为什么需要自己实现一个简单的DI框架

学习目的:

  1. 深入理解Spring原理:通过实践掌握Spring框架核心机制
  2. 提升反射编程能力:熟练运用Java反射机制处理注解和类操作
  3. 理解设计模式:体会工厂模式、单例模式等在框架中的应用

实践价值:

  1. 轻量级解决方案:避免引入大型框架的复杂性
  2. 定制化需求:根据特定业务场景定制注入逻辑
  3. 性能优化:针对具体场景优化注入过程


1.3 本项目的实际应用场景

多数据源切换

  1. 可以通过添加不同的@Mapper标注实现类来支持多种数据库
  2. 如MySQLDao和GaussDAO可以共存,通过配置选择具体使用哪个

插件化架构

  1. 第三方可以通过实现标准接口并添加@Mapper注解来扩展功能
  2. 主程序无需修改代码即可集成新的实现

微服务解耦

  1. 服务间通过接口依赖,具体实现可以在运行时动态注入
  2. 便于测试和维护,降低模块间耦合度

2.核心设计思路


2.1 整体架构设计

    三层架构模式:采用典型的分层设计,包括控制层(UserController)接口层(Dao)和实现层           (MySQLDao、GaussDAO)、

控制层(UserController):

public class UserController {
    @AutoWried
    private Dao dao;
    public void addUser(){
        this.dao.add();
    }
    public void queryUser(){
        this.dao.query();
    }
}

接口层(Dao):

public interface Dao {
    public void add();
    public void query();
}

MySQLDao:

@Mapper
public class MySQLDao implements Dao{
    public void add(){
        System.out.println("MySQLDao.add()");
    }
    public void query(){
        System.out.println("MySQLDao.query()");
    }
}

GaussDAO:

@Mapper
public class GaussDAO implements Dao{
    public void add(){
        System.out.println("使用高斯数据库");
    }
    public void query(){
        System.out.println("使用高斯数据库");
    }
}


    容器管理:通过BeanCollection类作为简易IoC容器,负责对象的创建和依赖管理
    注解驱动:使用自定义注解实现声明式的依赖注入,降低代码耦合度


2.2关键注解的设计([@AutoWried]

@AutoWried 注解
    作用范围:ElementType.FIELD,只能用于字段声明
    生命周期:RetentionPolicy.RUNTIME,运行时可通过反射获取
    功能:标记需要自动注入的依赖字段,如UserController中的dao字段

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 AutoWried {

}


@Mapper 注解
    作用范围:ElementType.TYPE,可用于类声明
    生命周期:RetentionPolicy.RUNTIME,运行时有效
    功能:标识具体的实现类,表明该类可以被注入到接口类型的字段中

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Mapper {


}


2.3 自动扫描和注入流程

BeanCollection:

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

public class BeanCollection {
    public static Object createObject(Class c)throws ClassNotFoundException{
        Object result = null;
        List<String> javaLists = new ArrayList<String>();//用于存放java文件
        try {
            result = c.newInstance();//利用反射创建对象
            Field[] fields = c.getDeclaredFields();//获取所有字段
            //遍历字段
            for (Field field : fields) {
                if (field.isAnnotationPresent(AutoWried.class)) {
                    Class<?> autoClass = field.getType();//获取字段类型
                    if(autoClass.isInterface()){
                        //获取整个包路径,方便后续遍历
                        String pkPath = autoClass.getPackage().getName();//获取包名
                        String path = "E:\\ideaproject\\java\\Springboot\\src\\main\\java\\";
                        pkPath = path + pkPath.replace(".", "\\");
                        getChildFileLists(new File(pkPath), javaLists);
                         //遍历java文件
                        for (String javaList : javaLists) {
                           if(checkChildClass(javaList, Dao.class)){
                               Class clazz = Class.forName(javaList);
                               boolean flag1 = clazz.isAnnotationPresent(Mapper.class);
                               if (flag1) {
                                   field.setAccessible(true);
                                   field.set(result, clazz.newInstance());//设置属性值
                               }
                           }
                        }
                    }
                }
            }
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return result;
    }
    public static boolean checkChildClass(String className, Class<?> parentClass){
        Class zc = null;
        try {
            zc = Class.forName(className);

            if (Modifier.isAbstract(zc.getModifiers())) {
                return false;
            }
            if (Modifier.isInterface(zc.getModifiers())) {
                return false;
            }
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // 判断是不是这个接口子类对象
        return parentClass.isAssignableFrom(zc);
    }
    public static void getChildFileLists(File file, List<String> lists){
        File[] fs = file.listFiles();//获取当前目录下的所有文件
        for (File f : fs) {
            String filePath = f.getPath();
            if (filePath.endsWith(".java")) {
                String cname1 = filePath.substring(filePath.indexOf("java") + 5, filePath.length());
                cname1 = cname1.substring(0, cname1.lastIndexOf(".java"));
                cname1 = cname1.replaceAll("\\\\", ".");
                if (cname1.startsWith("Springboot.src.main.java.")) {
                    cname1 = cname1.substring("Springboot.src.main.java.".length());
                }
                lists.add(cname1);
            }
        }
    }
}
  1. 对象创建阶段:BeanCollection.createObject()方法通过反射创建目标类实例
  2. 依赖扫描阶段:遍历目标类的所有字段,查找带有@AutoWried注解的字段
  3. 类型匹配阶段:获取字段声明的类型(如Dao接口),确定需要注入的依赖类型
  4. 类路径扫描:根据接口所在包路径,扫描该路径下的所有Java类文件
  5. 实现类筛选:通过checkChildClass()方法验证类是否实现了目标接口且带有@Mapper注解
  6. 依赖注入:创建符合条件的实现类实例,通过反射设置到目标字段中

3.核心代码解析


3.1 [BeanCollection]类的实现细节

  • 对象创建入口:createObject(Class c)方法作为核心入口,负责实例化目标类并完成依赖注入
  • 字段扫描机制:通过getDeclaredFields()获取类的所有声明字段,逐一检查是否存在@AutoWried注解
  • 依赖解析策略:针对接口类型字段,扫描实现类并验证@Mapper注解,确保注入正确的实现

3.2 反射机制的应用

  • 实例化对象:使用Class.newInstance()创建类实例,触发无参构造函数
  • 字段访问控制:通过field.setAccessible(true)突破私有字段访问限制
  • 动态赋值操作:利用field.set(result, clazz.newInstance())将实现类实例注入到目标字段
  • 注解检测:通过isAnnotationPresent()方法判断字段或类是否携带特定注解

3.3类路径扫描逻辑

  • 包路径定位:从接口类型获取包名,转换为本地文件系统路径进行扫描
  • 文件遍历处理:使用listFiles()递归获取目录下所有.java文件
  • 类名提取规则:从文件路径中截取相对路径部分,转换为标准的类全限定名格式
  • 继承关系验证:通过isAssignableFrom()方法确认类与接口间的实现关系,排除抽象类和接口本身

4.运行效果展示


4.1测试代码构成

public class Test {
    public static void main(String[] args){
        UserController  uc = null;
        try {
            uc = (UserController) BeanCollection.createObject(UserController.class);
            uc.addUser();
            uc.queryUser();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}


4.2输出结果

MySQLDao.add()
MySQLDao.query()

更多推荐