手写模拟Spring底层原理-简易实现版

news2024/9/20 14:31:35

通过手写模拟Spring

  • 了解Spring的底层源码启动过程
  • 了解BeanDefinition、BeanPostProcessor的概念
  • 了解Spring解析配置类等底层源码工作流程
  • 了解依赖注入,Aware回调等底层源码工作流程
  • 了解Spring AOP的底层源码工作流程

这里实现一个简化版的 Spring 框架的核心功能,模拟应用程序上下文(ApplicationContext)的创建、bean 的定义、依赖注入、作用域管理和后置处理等过程。

实际的 Spring 框架在此基础上还有更多功能和复杂性,这里只提供一个基础的理解。

核心功能:

  • BubbleApplicationContext 是主要的应用程序上下文类,负责管理 bean 的定义和实例化。
  • BeanDefinition 用于封装 bean 的定义,包括类型和作用域等信息。
  • BeanPostProcessor 接口定义了在 bean 创建过程中的后置处理方法。

数据结构:

  • beanDefinitionMap 存储了 bean 的定义信息,以 bean 名称为键。
  • singletonObjects 保存了单例 bean 的实例,以 bean 名称为键。
  • beanPostProcessorList 包含实现了 BeanPostProcessor 接口的后置处理器。

初始化流程:

  • 构造方法扫描配置类,初始化 beanDefinitionMapbeanPostProcessorList
  • 对于单例 bean,遍历 beanDefinitionMap,创建和初始化实例,存入 singletonObjects

bean 创建和初始化:

  • createBean 方法使用反射创建 bean 实例,包括依赖注入和后置处理逻辑。
  • 自动注入带有 Autowired 注解的字段。
  • 设置 bean 的名称,如果实现了 BeanNameAware 接口。
  • 执行前置后置处理器的逻辑。
  • 如果实现了 InitializingBean 接口,执行初始化逻辑。

获取 bean 实例:

  • getBeen 方法根据 bean 名称获取 bean 实例。
  • 对于单例,如果实例已存在,直接返回;否则创建并存入。
  • 对于原型,每次获取都创建新实例。

扫描和初始化:

  • scan 方法扫描配置类,初始化 bean。
  • 判断是否标注了 ComponentScan 注解,获取扫描路径。
  • 遍历类文件,解析 Component 注解,初始化 bean。

简易实现代码如下:

目录结构:

package com.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BubbleApplicationContext {

    private Class configClass;

    // 存储 BeanDefinition 对象的映射,用于管理 bean 的定义信息
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    // 存储单例对象的映射,用于管理单例 bean 的实例
    private Map<String, Object> singletonObjects = new HashMap<>();
    // 存储 BeanPostProcessor 的列表,用于后置处理 bean,,,spring用的LinkList
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    // 构造方法,在创建 ApplicationContext 时传入配置类
    public BubbleApplicationContext(Class configClass) {
        this.configClass = configClass;

        // 扫描配置类并初始化 beanDefinitionMap,,beanPostProcessorList
        scan(configClass);

        // 遍历 beanDefinitionMap,创建并初始化单例对象,并存入 singletonObjects
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            if (beanDefinition.getScope().equals("singleton")) {

                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);

            }
        }
    }
    // 创建 bean 对象的方法,包括依赖注入、后置处理等逻辑
    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getType();//com.bubble.service.UserService

        Object instance = null;

        try {
            instance = clazz.getConstructor().newInstance(); // 使用反射创建实例

            for (Field field : clazz.getDeclaredFields()) {
                // 对带有 Autowired 注解的字段进行依赖注入
                if (field.isAnnotationPresent(Autowired.class)) {

                    field.setAccessible(true);

                    field.set(instance, getBeen(field.getName()));
                }
            }

            // 若实现了 BeanNameAware 接口,设置 bean 的名称
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }
            // 执行 BeanPostProcessor 的前置处理逻辑
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
            }

            // 若实现了 InitializingBean 接口,执行初始化逻辑
            if (instance instanceof InitializingBean) {
                ((InitializingBean)instance).afterPropertiesSet();
            }

            // 执行 BeanPostProcessor 的后置处理逻辑
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
            }

        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

        return instance;
    }

    // 获取 bean 对象的方法,支持单例和原型两种作用域
    public Object getBeen(String beanName) {

        // 如果beanDefinitionMap中不包含beanName,说明传入的bean是不存在的
        if (!beanDefinitionMap.containsKey(beanName)) {
            throw new NullPointerException();
        }

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        if (beanDefinition.getScope().equals("singleton")) {
            Object singletonBean = singletonObjects.get(beanName);
            if (singletonBean == null) {
                singletonBean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, singletonBean);
            }
            return singletonBean;
        } else {
            // 原型(多例)
            Object prototypeBean = createBean(beanName, beanDefinition);
            return prototypeBean;
        }
    }

    /**
     * 扫描指定配置类,解析其中的 ComponentScan 注解,根据注解配置的扫描路径
     * 查找并解析带有 Component 注解的类,初始化对应的 BeanDefinition。
     *
     * @param configClass 配置类,用于查找 ComponentScan 注解
     */
    private void scan(Class configClass) {
        // 检查配置类是否标注了 ComponentScan 注解
        if(configClass.isAnnotationPresent(ComponentScan.class)) {
            // 获取 ComponentScan 注解实例
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            // 获取扫描路径,并将包名中的点替换为斜线,以便在类加载器中定位资源
            String path = componentScanAnnotation.value();
            path = path.replace(".","/");// com/bubble/service

            // 获取类加载器,用于加载类和资源
            ClassLoader classLoader = BubbleApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(path);// 根据扫描路径获取资源 URL
            File file = new File(resource.getFile());// 将 URL 转换为文件对象,以便遍历目录和文件

            // 判断是否为目录,如果是目录则遍历其中的文件
            if(file.isDirectory()){
                for (File f : file.listFiles()) {
                    // 获取文件的绝对路径
                    String absolutePath = f.getAbsolutePath();//E:\Dev\Spring\bubble-spring\target\classes\com\bubble\service\UserService.class

                    // 提取类路径部分,去除文件扩展名
                    absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));//com\bubble\service\UserService
                    // 将文件路径中的斜线替换为点,得到完整的类名
                    absolutePath = absolutePath.replace("\\", ".");//com.bubble.service.UserService

                    try {
                        // 使用类加载器加载类
                        Class<?> clazz = classLoader.loadClass(absolutePath);
                        // 判断类是否标注了 Component 注解,表示为一个需要管理的 bean
                        if (clazz.isAnnotationPresent(Component.class)) {

                            // 如果类实现了 BeanPostProcessor 接口,将其实例化并加入 beanPostProcessorList
                            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                BeanPostProcessor instance = (BeanPostProcessor) clazz.getConstructor().newInstance();
                                beanPostProcessorList.add(instance);
                            }

                            // 获取 Component 注解实例
                            Component componentAnnotation = clazz.getAnnotation(Component.class);
                            // 获取 bean 的名称,如果未指定,则使用类名的首字母小写作为默认名称
                            String beanName = componentAnnotation.value();
                            if ("".equals(beanName)) {
                                beanName = Introspector.decapitalize(clazz.getSimpleName());
                            }

                            // 创建并初始化 BeanDefinition 对象
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setType(clazz);

                            // 判断类是否标注了 Scope 注解,设置 bean 的作用域
                            if (clazz.isAnnotationPresent(Scope.class)) {
                                Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
                                String value = scopeAnnotation.value();
                                beanDefinition.setScope(value);
                            } else {
                                // 默认作用域为 singleton
                                beanDefinition.setScope("singleton");
                            }

                            // 将 BeanDefinition 添加到 beanDefinitionMap 中,以便后续使用
                            beanDefinitionMap.put(beanName, beanDefinition);
                        }

                    } catch (InstantiationException e) {
                        throw new RuntimeException(e);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    } catch (InvocationTargetException e) {
                        throw new RuntimeException(e);
                    } catch (NoSuchMethodException e) {
                        throw new RuntimeException(e);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }

                }
            }

        }
    }

}
package com.spring;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {
}
package com.spring;

public class BeanDefinition {
    private Class type;
    private String scope;
    private boolean isLazy;

    public Class getType() {
        return type;
    }

    public void setType(Class type) {
        this.type = type;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public boolean isLazy() {
        return isLazy;
    }

    public void setLazy(boolean lazy) {
        isLazy = lazy;
    }
}
public interface BeanNameAware {
    void setBeanName(String name);
}
public interface BeanPostProcessor {
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
}
package com.spring;

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

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

    String value() default "";
}
package com.spring;

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

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

    String value() default "";
}

public interface InitializingBean {
    void afterPropertiesSet();
}
package com.spring;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
    String value() default "";
}
package com.bubble.service;

import com.spring.BeanPostProcessor;
import com.spring.Component;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//自定义 Bean 后置处理器,用于在 Bean 初始化之后,对特定的 Bean 进行切面逻辑的处理。
@Component
public class BubbleBeanPostProcessor implements BeanPostProcessor {

    //在 Bean 初始化之后,对特定的 Bean 进行切面逻辑处理。
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        // 判断是否是特定的 Bean,这里以 "userService" 为例
        if (beanName.equals("userService")) {
            // 创建动态代理对象,用于在原始方法调用前后添加切面逻辑
            Object proxyInstance = Proxy.newProxyInstance(BubbleBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 切面
                    System.out.println("切面逻辑");

                    return method.invoke(bean, args);
                }
            });
            // 返回代理对象,即添加了切面逻辑的 Bean 实例
            return proxyInstance;
        }

        //对于其他 Bean,保持原样返回,不添加切面逻辑
        return bean;
    }
}
package com.bubble.service;

import com.spring.BeanPostProcessor;
import com.spring.Component;

import java.lang.reflect.Field;

//自定义 Bean 后置处理器,用于在 Bean 初始化之前,根据 BubbleValue 注解为特定的字段赋值。
@Component("userService")
public class BubbleValueBeanPostProcessor implements BeanPostProcessor {

    //在 Bean 初始化之前,根据 BubbleValue 注解为特定的字段赋值。
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {

        for (Field field : bean.getClass().getDeclaredFields()) {
            // 检查字段是否标注了 BubbleValue 注解
            if (field.isAnnotationPresent(BubbleValue.class)) {
                field.setAccessible(true);// 设置字段的访问权限,允许反射访问私有字段
                try {
                    field.set(bean, field.getAnnotation(BubbleValue.class).value());
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

        // bean
        return bean;
    }
}
package com.bubble.service;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BubbleValue {
    String value() default "";
}

package com.bubble.service;

import com.spring.Component;

@Component
public class OrderService {

    public void test() {
        System.out.println("test->OrderService");
    }
}
public interface UserInterface {
    public void test();
}
package com.bubble.service;

import com.spring.Autowired;
import com.spring.BeanNameAware;
import com.spring.Component;

@Component("userService")
public class UserService implements UserInterface, BeanNameAware {

    @Autowired
    private OrderService orderService;

    @BubbleValue("bubble")
    private String test;

    private String beanName;


    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }

//    public void test() {
//        System.out.println("test");
//    }

    @Override
    public void test() {
        System.out.println(beanName);
        System.out.println(test);
    }
}
package com.bubble;

import com.spring.ComponentScan;

@ComponentScan("com.bubble.service")
public class AppConfig {
}

程序调用测试

public class Test {
    public static void main(String[] args) {

        //扫描-->创建单例bean BeanDefinition BeanPostPRocess
        BubbleApplicationContext applicationContext = new BubbleApplicationContext(AppConfig.class);

        //UserService userService = (UserService)applicationContext.getBeen("userService");
        UserInterface userService = (UserInterface) applicationContext.getBeen("userService");

        userService.test();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1957360.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2024源代码加密软件评测丨五款企业源代码加密软件推荐

“李明&#xff0c;最近黑客攻击事件频发&#xff0c;咱们的代码库安全真的让人捏把汗啊。”张伟眉头紧锁&#xff0c;语气中带着几分忧虑。 “是啊&#xff0c;我听说现在市面上有不少源代码加密软件&#xff0c;但种类繁多&#xff0c;不知道该选哪个好。”李明回应道&#…

cocos creator 3学习记录01——如何替换图片

一、动态加载本地图片 1、通过将图片关联到CCClass属性上来进行代码切换。 1、这种方法&#xff0c;需要提前在脚本文件中声明好代表图片的CCClass属性。 2、然后拖动图片资源&#xff0c;到脚本内声明好的属性上以进行关联。 3、然后通过程序&#xff0c;来进行切换展示。…

Python | TypeError: ‘module’ object is not callable

Python | TypeError: ‘module’ object is not callable 在Python编程中&#xff0c;遇到“TypeError: ‘module’ object is not callable”这类错误通常表明你尝试像函数一样调用了一个模块。这种错误通常是由于导入模块时的疏忽或误解导致的。本文将深入探讨此错误的根源&…

JVM—HotSpot虚拟机对象探秘

1、对象的创建 对象只是普通对象&#xff0c;不包括数组和Class对象 类加载检查&#xff1a;当虚拟机遇到字节码New指令时&#xff0c;先检查这个指令的参数是否可以在常量池定位到一个类的符号引用&#xff0c;并且加载这个符号引用代表的类是否被加载、解析、验证、初始化过。…

【Redis】Redis的概念 | 特性 | 应用场景 | 安装 | 客户端

文章目录 Redis一、认识Redis二、Redis的特性三、Redis的应用场景四、安装Redis五、Redis的客户端 Redis 一、认识Redis Redis是一个开源的&#xff0c;在内存中存储数据。在分布式系统中更有优势。如果是单机环境下&#xff0c;直接通过变量存储数据比用Redis更有优势。在分布…

通信系统的均衡技术

1、摘要 在通信系统中&#xff0c;信号从发送端发出&#xff0c;经过信道&#xff0c;到达接收端&#xff0c;而在传输的过程中&#xff0c;信号会发生失真&#xff0c;产生失真的原因有很多&#xff0c;包括阻抗不匹配&#xff0c;干扰等。为了优化信号质量&#xff0c;需要进…

推动智慧交通建设,边缘计算赋能交通信号灯数据处理与决策能力

随着智慧城市建设的快速发展&#xff0c;智慧交通已成为城市发展的重要组成项目。智慧交通旨在通过大数据、人工智能、物联网等先进技术&#xff0c;实现交通系统的全面感知、智能分析、主动服务和协同管理。边缘计算在交通信号灯物联网应用中展现了交通信号灯数据处理与决策能…

C# 设计倒计时器、串口助手开发

文章目录 1. 实现一个简单的倒计时器开始、暂停2. 串口助手开发 1. 实现一个简单的倒计时器开始、暂停 namespace Timer {public partial class Form1 : Form{int count;//用于定时器计数int time;//存储设定的定时值bool parse false;//控制暂停计时public Form1(){Initiali…

Apollo使用(1):介绍、QuickStart

一、Apollo应用 1、介绍 项目地址&#xff1a;https://github.com/ctripcorp/apollo使用手册&#xff1a;https://github.com/ctripcorp/apollo/wiki Apollo&#xff08;阿波罗&#xff09;是携程框架部门研发的分布式配置中心&#xff0c;能够集中化管理应用不同环境、不同集…

Python如何快速定位最慢的代码?优雅了~

编写Python代码时&#xff0c;我们常常会遇到性能瓶颈&#xff0c;这不仅影响程序的执行效率&#xff0c;还可能导致用户体验下降。那么&#xff0c;如何快速定位代码中最慢的部分&#xff0c;成为每个开发者必须掌握的技能。 如何快速定位 Python 代码中的性能瓶颈&#xff1…

优雅单片机之STM32C8T6------蓝牙模块基本设置(2)

0&#xff0c;C8T6系列 1&#xff0c;入门之程序的下载 2&#xff0c;蓝牙模块基本设置&#xff08;本文&#xff09; 2&#xff0c;蓝牙模块基本应用 3&#xff0c;蓝牙小车&#xff08;待定&#xff09; 一&#xff0c;蓝牙模块基础设置 需要硬件&#xff1a;电脑&#x…

【Linux】进程间通信(2):命名管道

目录 一、命名管道的概念 二、命名管道与匿名管道的区别 1、常规区别 2、读写阻塞的区别 3、存储位置和数据处理的区别 匿名管道 命名管道 三、命名管道的创建与使用 1、在命令行中命名管道的创建与使用 2、在编程中命名管道的创建与使用 2、示例代码 四、命名管道的…

实验2-3-6 计算分段函数[3]

#include<stdio.h> #include<math.h> int main(){double x;scanf("%lf",&x);if (x10)printf("f(%.1f)%.1f",x,1/x);elseprintf("f(%.1f)%.1f",x,x);}

【CSS】css 如何实现固定宽高比

今天和同事讨论这个问题&#xff0c;一时间还想不到了&#xff0c;于是学习了下&#xff0c;就顺便当个记录吧 要在CSS中实现固定宽高比&#xff0c;有两种主要的方法可以选择。一种是使用新的aspect-ratio属性&#xff0c;另一种是利用padding技巧。随着现代浏览器对aspect-ra…

27、美国国家冰雪中心(NSIDC)海冰密集度月数据下载与处理

文章目录 一、前言二、数据下载三、使用Ponply查看数据结构四、代码一、前言 处理美国国家冰雪中心(NSIDC)的海冰密集度月度数据时,坐标转换是一个重要的步骤。NSIDC提供的数据通常采用极地球面坐标系,需要将其转换为常用的地理坐标系(如经纬度)以便进行分析和可视化。 坐…

【数据结构与算法】共享双向链表

共享双向链表 一.共享双向链表的引入二.共享双向链表的原理三.共享双向链表的结构四.共享双向链表的初始化五.共享双向链表插入六.共享链表遍历数据七.共享链表的销毁八.总结 一.共享双向链表的引入 当我们使用双向链表其数据域是固定的类型,如果我需要另外一种类型的话,就需要…

共聚ISC|赛宁网安即将亮相ISC.AI2024第十二届互联网安全大会

随着数字时代的飞速发展&#xff0c;网络安全和人工智能技术的融合已成为推动各行业数智化转型的关键。7月31日至8月1日&#xff0c;备受瞩目的ISC.AI 2024第十二届互联网安全大会将在北京国家会议中心隆重开幕。本届大会将全面聚焦于安全和人工智能两大核心领域&#xff0c;致…

深入探讨Facebook投放菲律宾slots游戏广告的优势

在深入探讨Facebook投放菲律宾游戏广告的优势时&#xff0c;我们不得不提及该平台在本地市场的深厚渗透力与高度互动性。菲律宾作为东南亚地区互联网用户增长最快的国家之一&#xff0c;Facebook不仅是人们日常社交的必备工具&#xff0c;更是信息获取与娱乐消费的重要渠道。因…

Animation Clips

动画剪辑是Unity用来表示对象运动姿态的基础资源&#xff0c;你可以从模型文件中导入动画剪辑&#xff0c;也可以在Unity内部创建简单的动画剪辑。 Import Animation From Model 在导入时只需要确保模型的Animation选项卡中的Import Animation是被激活的&#xff0c;对于没有动…

【Linux】Ubuntu解决Release 文件已经过期问题

​今天在执行update更新软件包时遇到了此问题&#xff1a;E: http://cn.archive.ubuntu.com/ubuntu/dists/jammy-updates/InRelease 的 Release 文件已经过期(已经过期了 247天 21小时 33分 15秒)。该仓库的更新将不会应用&#xff0c;如图 ​ 这个报错之前其实经常遇到&#x…