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

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 面试官问我咋实现Spring框架IOC和DI好吧打趴下,深度解析手动实现Spring框架的IOC与DI功能2

定义相关属性扫描所有 Bean



修改 BeanContainer.java

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/10/08 008 17:24
 * @description
 **/
@Slf4j
@SuppressWarnings("unused")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
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();
        }
    }
    /**
     * bean注释
     */
    private final List<Class<? extends Annotation>> beanAnnotation =
            Arrays.asList(Component.class, Controller.class, Service.class, Repository.class);
    /**
     * bean映射
     */
    private final Map<Class<?>, Object> beanMap = new ConcurrentHashMap<>();
    /**
     * 容器实例的个数
     *
     * @return int
     */
    public int size() {
        return beanMap.size();
    }
    /**
     * 加载状态
     */
    private boolean loaded = false;
    /**
     * 容器是否被加载过
     *
     * @return boolean
     */
    public boolean isLoaded() {
        return loaded;
    }
    /**
     * 加载bean
     *
     * @param packageName 包名
     */
    public synchronized void loadBeans(String packageName) {
        // 判断容器是否被加载过
        if (isLoaded()) {
            log.warn("容器已经被加载!");
            return;
        }
        // 1.获取该包下所有的字节码
        Set<Class<?>> packageClass = ClassUtil.getPackageClass(packageName);
        if (null == packageClass || packageClass.isEmpty()) {
            log.warn("get null from packageName:{}", packageName);
            return;
        }
        // 2.遍历所有的字节码对象
        packageClass.forEach(clazz -> beanAnnotation.forEach(annotation -> {
            // 2.1.判断当前字节码上是否有指定的注解
            if (clazz.isAnnotationPresent(annotation)) {
                // 创建对象存放到集合当中
                beanMap.put(clazz, BeanContainer.newInstance(clazz, false));
            }
        }));
        loaded = true;
    }
    private static <T> T newInstance(Class<?> clazz, boolean accessible) {
        Constructor<?> declaredConstructor;
        try {
            declaredConstructor = clazz.getDeclaredConstructor();
            declaredConstructor.setAccessible(accessible);
            return (T) declaredConstructor.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("创建实例失败!");
        }
    }
}


改造一下我们之前的测试类代码移动目录位置,移动到 test 包当中如下,顺便改一下我们自定义的 IOC 与 DI 功能代码存放包的位置:


/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/9/30 030 14:36
 * @description
 **/
public class MyTest {
    private static BeanContainer beanContainer;
    @BeforeAll
    public static void init() {
        beanContainer = BeanContainer.getInstance();
    }
    @Test
    @SuppressWarnings("all")
    void testClassUtil() {
        ClassUtil.getPackageClass("top.it6666").forEach(System.out::println);
    }
    @Test
    void testBeanContainer() {
        Assertions.assertFalse(beanContainer.isLoaded());
        beanContainer.loadBeans("top.it6666");
        Assertions.assertEquals(1, beanContainer.size());
        Assertions.assertTrue(beanContainer.isLoaded());
    }
}

创建一个 MyController.java 然后在该类上添加 @Controller 注解导包是我们自己定义的那个注解:


然后运行测试类即可查看结果:




容器相关操作


增加删除操作,可以通过 class 获取对应的实例。获取所有的 key 和所有的 value。通过注解来获取被注解标注的 class。通过传入接口,来获取接口实现类的 class 对象。


/**
 * 添加bean
 *
 * @param clazz clazz
 * @param obj   obj
 * @return {@link Object}
 */
public Object addBean(Class<?> clazz, Object obj) {
    return beanMap.put(clazz, obj);
}
/**
 * 删除bean
 *
 * @param clazz clazz
 * @return {@link Object}
 */
public Object removeBean(Class<?> clazz) {
    return beanMap.remove(clazz);
}
/**
 * 获取Bean
 *
 * @param clazz clazz
 * @return {@link Object}
 */
public Object getBean(Class<?> clazz) {
    return beanMap.get(clazz);
}
/**
 * 获取容器所有的classes
 *
 * @return {@link Set}<{@link Class}<{@link ?}>>
 */
public Set<Class<?>> getClasses() {
    return beanMap.keySet();
}
/**
 * 获取容器所有的Bean实例
 *
 * @return {@link Set}<{@link Object}>
 */
public Set<Object> getBeans() {
    return new HashSet<>(beanMap.values());
}


添加根据指定注解获取该注解标记的 Bean 的 Class 集合:

/**
 * 根据指定注解获取该注解标记的 Bean 的 Class 集合
 *
 * @param annotation 注解
 * @return {@link Set}<{@link Class}<{@link ?}>>
 */
public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation) {
    // 1.获取容器当中所有的class
    Set<Class<?>> classes = this.getClasses();
    if (null == classes || classes.isEmpty()) {
        log.info("没有任何信息!");
        return null;
    }
    Set<Class<?>> classSet = new HashSet<>();
    classes.forEach(clazz -> {
        if (clazz.isAnnotationPresent(annotation)) {
            // 保存到集合当中
            classSet.add(clazz);
        }
    });
    return classSet.size() > 0 ? classSet : null;
}


添加根据传入的接口,到容器当中找对应的实现类或子类:

/**
 * 根据传入的接口,到容器当中找对应的实现类或子类
 *
 * @param interfaceOrClass 接口或类
 * @return {@link Set}<{@link Class}<{@link ?}>>
 */
public Set<Class<?>> getClassBySuper(Class<?> interfaceOrClass) {
    // 1.获取容器当中所有的class
    Set<Class<?>> classes = getClasses();
    if (null == classes || classes.isEmpty()) {
        log.info("没有任何信息!");
        return null;
    }
    Set<Class<?>> classSet = new HashSet<>();
    classes.forEach(clazz -> {
        // 判断当前字节码是否为指定字节码的子类或接口并且指定的字节码不等于本身字节码
        if (interfaceOrClass.isAssignableFrom(clazz) && !Objects.equals(clazz, interfaceOrClass)) {
            classSet.add(clazz);
        }
    });
    return classSet.size() > 0 ? classSet : null;
}





依赖注入


定义相关的注解标签,实现创建被注解标记的成员变量实例,并将其注入到成员变量里,依赖注入使用:

创建 Autowired.java

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/10/25 025 9:01
 * @description
 **/
/*
及作用在成员变量上
* */
@Target(ElementType.FIELD)
/*
保留在运行时
* */
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
    String value() default "";
}


依赖注入实现思路:





依赖注入整体实现


DependencyInjector.java

/**
 * 依赖注入
 *
 * @author yby6
 * @date 2023/10/25
 */
@Slf4j
public class DependencyInjector {
    /**
     * Bean容器
     */
    private final BeanContainer beanContainer;
    /**
     * 依赖注入,构造器
     */
    public DependencyInjector() {
        beanContainer = BeanContainer.getInstance();
    }
    /**
     * 执行Ioc操作
     */
    public void doIoc() {
        // 1.遍历Bean容器中所有的Class对象
        beanContainer.getClasses().forEach(clazz -> {
            // 2.遍历Class对象的所有成员变量
            Field[] declaredFields = clazz.getDeclaredFields();
            if (declaredFields.length == 0) {
                // 当前没有成员变量,继续查找下一个
                return;
            }
            // 有成员变量
            Arrays.stream(declaredFields).forEach(field -> {
                // 3.找出被Autowired标记的成员变量
                if (field.isAnnotationPresent(Autowired.class)) {
                    // 获取注解实例
                    // 被 Autowired注解标注
                    // 4.获取这些成员变量的类型
                    Class<?> fieldType = field.getType();
                    // 5.获取成员变量的类型在容器里对应的实例
                    Object instanceObj = this.getFieldInstance(fieldType, field.getAnnotation(Autowired.class).value());
                    // 6.通过反射将对象的成员变量实例进行赋值操作
                    if (null == instanceObj) {
                        throw new RuntimeException("成员属性注入失败:" + fieldType.getName());
                    } else {
                        // 获取当前类实例
                        // 给当前类实例成员变量赋值
                        this.setField(field, beanContainer.getBean(clazz), instanceObj, true);
                    }
                }
            });
        });
    }
    /**
     * 根据Class在BeanContainer里获取其实例或者实现类
     *
     * @param fieldClass     成员变量类型
     * @param autowiredValue autowired注解的内容
     * @return {@link Object}
     */
    private Object getFieldInstance(Class<?> fieldClass, String autowiredValue) {
        Object fieldValue = beanContainer.getBean(fieldClass);
        // 字段是实现类,直接返回
        if (null != fieldValue) {
            return fieldValue;
        } else {
            // 字段是接口
            Class<?> implClass = this.getImplClass(fieldClass, autowiredValue);
            if (implClass != null) {
                return beanContainer.getBean(implClass);
            } else {
                return null;
            }
        }
    }
    /**
     * 得到impl类
     *
     * @param fieldClass     接口
     * @param autowiredValue autowired的价值
     * @return {@link Class}<{@link ?}>
     */
    private Class<?> getImplClass(Class<?> fieldClass, String autowiredValue) {
        //从容器当中获取该接口的实现类
        Set<Class<?>> classSet = beanContainer.getClassBySuper(fieldClass);
        if (null != classSet && !classSet.isEmpty()) {
            if (Objects.equals(autowiredValue, "")) {
                if (classSet.size() == 1) {
                    return classSet.iterator().next();
                } else {
                    // 如果有多个实现类
                    throw new RuntimeException("有多个实现类,请指定具体的实现类名称:" + fieldClass.getName());
                }
            } else {
                // 注解当中有设置值
                for (Class<?> clazz : classSet) {
                    if (autowiredValue.equals(clazz.getSimpleName())) {
                        return clazz;
                    }
                }
            }
        }
        return null;
    }
    /**
     * 设置字段
     *
     * @param field        字段
     * @param targetObject 目标对象
     * @param obj          obj
     * @param accessible   是否可访问
     */
    private void setField(Field field, Object targetObject, Object obj, boolean accessible) {
        field.setAccessible(accessible);
        try {
            field.set(targetObject, obj);
        } catch (IllegalAccessException e) {
            log.error("setField error:" + e);
            throw new RuntimeException(e);
        }
    }
}




测试依赖注入


创建 Service

IUserService.java

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/10/25 025 10:03
 * @description
 **/
public interface IUserService {
    /**
     * 显示
     */
    void show();
}


IUserServiceImpl.java

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/10/25 025 10:04
 * @description
 **/
@Service
public class IUserServiceImpl implements IUserService {
    @Override
    public void show() {
        System.out.println("my is IUserServiceImpl");
    }
}


修改 MyController.java

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/10/09 009 11:31
 * @description
 **/
@Controller
public class MyController {
    @Autowired
    private IUserService iUserService;
    public void testShow() {
        this.iUserService.show();
    }
}


修改 MyTest.java 当前 IUserService 只有一个实现类所以 @Autowired 是可以直接标记即可不用指定名称,在 MyTest 添加 testIoc 方法:

@Test
void testIoc() {
    beanContainer.loadBeans("top.it6666");
    MyController myController = (MyController) beanContainer.getBean(MyController.class);
    DependencyInjector dependencyInjector = new DependencyInjector();
    dependencyInjector.doIoc();
    myController.testShow();
}


运行结果如下:


如上是只有一个实现类,紧接着在来新建一个实现类,在 impl 包当中创建 ITaoBaoServiceImpl.java:

/**
 * @author yby6
 * @program SpringPro
 * @date Created in 2023/10/25 025 10:42
 * @description
 **/
@Service
public class ITaoBaoServiceImpl implements IUserService {
    @Override
    public void show() {
        System.out.println("my is ITaoBaoServiceImpl");
    }
}


再次运行测试类代码结果如下图:


修改 MyController.java:


再次运行测试类代码运行结果如下图所示:





最后


本期结束咱们下次再见👋~

🌊 关注我不迷路,如果本篇文章对你有所帮助,或者你有什么疑问,欢迎在评论区留言,我一般看到都会回复的。大家点赞支持一下哟~ 💗

相关文章
|
1月前
|
前端开发 Java 数据库连接
Spring框架初识
Spring 是一个分层的轻量级开源框架,核心功能包括控制反转(IOC)和面向切面编程(AOP)。主要模块有核心容器、Spring 上下文、AOP、DAO、ORM、Web 模块和 MVC 框架。它通过 IOC 将配置与代码分离,简化开发;AOP 提供了声明性事务管理等增强功能。
81 21
Spring框架初识
|
8天前
|
前端开发 Java 数据库连接
Spring MVC 扩展和SSM框架整合
通过以上步骤,我们可以将Spring MVC扩展并整合到SSM框架中。这个过程包括配置Spring MVC和Spring的核心配置文件,创建控制器、服务层和MyBatis的Mapper接口及映射文件。在实际开发中,可以根据具体业务需求进行进一步的扩展和优化,以构建更加灵活和高效的企业级应用程序。
21 5
|
22天前
|
存储 人工智能 开发框架
Spring AI Alibaba 应用框架挑战赛圆满落幕,恭喜获奖选手
第二届开放原子大赛 Spring AI Alibaba 应用框架挑战赛决赛于 2 月 23 日在北京圆满落幕。
|
2月前
|
SQL Java 数据库连接
对Spring、SpringMVC、MyBatis框架的介绍与解释
Spring 框架提供了全面的基础设施支持,Spring MVC 专注于 Web 层的开发,而 MyBatis 则是一个高效的持久层框架。这三个框架结合使用,可以显著提升 Java 企业级应用的开发效率和质量。通过理解它们的核心特性和使用方法,开发者可以更好地构建和维护复杂的应用程序。
146 29
|
1月前
|
XML Java 开发者
通过springboot框架创建对象(一)
在Spring Boot中,对象创建依赖于Spring框架的核心特性——控制反转(IoC)和依赖注入(DI)。IoC将对象的创建和管理交由Spring应用上下文负责,开发者只需定义依赖关系。DI通过构造函数、setter方法或字段注入实现依赖对象的传递。Spring Boot的自动配置机制基于类路径和配置文件,自动为应用程序配置Spring容器,简化开发过程。Bean的生命周期包括定义扫描、实例化、依赖注入、初始化和销毁回调,均由Spring容器管理。这些特性提高了开发效率并简化了代码维护。
|
14天前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
|
2月前
|
XML Java 开发者
Spring底层架构核心概念解析
理解 Spring 框架的核心概念对于开发和维护 Spring 应用程序至关重要。IOC 和 AOP 是其两个关键特性,通过依赖注入和面向切面编程实现了高效的模块化和松耦合设计。Spring 容器管理着 Beans 的生命周期和配置,而核心模块为各种应用场景提供了丰富的功能支持。通过全面掌握这些核心概念,开发者可以更加高效地利用 Spring 框架开发企业级应用。
91 18
|
1月前
|
传感器 监控 安全
智慧工地云平台的技术架构解析:微服务+Spring Cloud如何支撑海量数据?
慧工地解决方案依托AI、物联网和BIM技术,实现对施工现场的全方位、立体化管理。通过规范施工、减少安全隐患、节省人力、降低运营成本,提升工地管理的安全性、效率和精益度。该方案适用于大型建筑、基础设施、房地产开发等场景,具备微服务架构、大数据与AI分析、物联网设备联网、多端协同等创新点,推动建筑行业向数字化、智能化转型。未来将融合5G、区块链等技术,助力智慧城市建设。
|
7天前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
48 29
|
4天前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
23 3

推荐镜像

更多