Spring源码系列:初探底层,手写Spring

这篇具有很好参考价值的文章主要介绍了Spring源码系列:初探底层,手写Spring。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

在学习Spring框架源码时,记住一句话:源码并不难,只需要给你各种业务场景或者项目经理,你也能实现自己的Spring。虽然你的实现可能无法与开源团队相媲美,但是你肯定可以实现一个0.0.1版本。因此,初次阅读源码时,不要陷入太深的细节中。先了解大体逻辑,再仔细研读。

实现功能

本文将带领大家实现一个简易版的Spring框架,并介绍以下功能点:

  1. 了解Spring的底层源码启动过程
  2. 了解BeanDefinition的概念
  3. 了解Spring解析配置类等底层源码工作流程
  4. 了解依赖注入,Aware回调等底层源码工作流程
  5. 了解Spring AOP的底层源码工作流程

以上功能点将使我们对Spring框架的实现有所了解,但我们并不会一下子实现整个Spring框架的业务。我们将从上述功能点入手,通过手写模拟Spring框架来实现这些功能。

首先,我们像使用Spring一样,传入配置类获取applicationContext,再通过getBean方法获取具体对象。最后,我们调用方法并打印日志。如果你对这些基本流程不熟悉,可以查看我的入门系列文章:Spring入门系列:浅析知识点

详细流程如下:

  1. 解析配置类上的ComponentScan注解,获取扫描的基本路径
  2. 开始解析各个被扫描到的文件,是否是需要被Spring管理,如果是则暂存到list集合中
  3. 开始遍历被Spring管理list集合,解析各个类上的注解,比如是否是懒加载,然后将这些属性都封装到applicationContext中的以beanName为key的BeanDefineMap中
  4. 针对已经解析好的bean定义进行创建对象并实例化,并将其放入以beanName为key的singletonMap实例化缓存池中。
  5. 在实例化时,如果发现有依赖注入的对象,则将实例化缓存池中的对象存入。如果缓存池中没有该对象,则进行创建后再注入。
  6. 判断对象是否实现了各个Aware接口,如果实现,则进行回调。
  7. 判断对象是否属于增强类。在这里,我们模拟了事务注解。如果有事务注解,则创建一个代理对象,并为所有方法增加拦截器。然后将该代理对象存入单例缓存池中。

详细解析

项目结构

基本路径:com.user目录

各个注解及上下文类:config目录

需要被管理的Bean:service目录

启动类:com.user根目录

截图

源码分析

@Component
public class UserService implements ApplicationContextAware, BeanNameAware {

    @AutoWired
    ServiceDemo serviceDemo;

    private XiaoyuApplicationContext applicationContext;
    private String beanName;

    public void test() {
        serviceDemo.say();
//        System.out.println(serviceDemo);
        System.out.println("userService:"+applicationContext.getBean("userService"));
        System.out.println("beanName:"+beanName);
    }

    @Override
    public void setApplicationContext(XiaoyuApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

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

UserService类主要用于测试是否Spring已经管理了相关对象并生成了代理对象,是我们的日常业务类,我们仔细看下XiaoyuApplicationContext类,主要的流程在这边:

public class XiaoyuApplicationContext {


    //配置类
    private Class config;
    //初始的bean定义
    private Map<String,BeanDefinition> beanDefineMap = new HashMap<>();
    //单例缓存池
    private Map<String,Object> singleBean = new HashMap<>();

    public XiaoyuApplicationContext(Class myDemoConfigClass) {
        config = myDemoConfigClass;
        //解析配置类
        scan();

    }

    public void scan(){
        ComponentScan declaredAnnotation = (ComponentScan) config.getDeclaredAnnotation(ComponentScan.class);
        String value = declaredAnnotation.basePackages();
        doScan(value);
        //将bean定义Map生成具体的Bean对象
        beanDefineMap.entrySet().stream().forEach(item->{
            String beanName = item.getKey();
            BeanDefinition beanDefinition = item.getValue();
            if (!beanDefinition.isLazy() && "singleton".equals(beanDefinition.getScope())) {
                Object bean = createBean(beanName);
                singleBean.put(beanName,bean);
            }
        });
    }

    /**
     *     解析配置类
     */
    private void doScan(String value) {
        String path = value.replace(".","/");
        //正常走文件解析
        ClassLoader classLoader = this.getClass().getClassLoader();
        URL resource = classLoader.getResource(path);
        File file = new File(resource.getFile());

        List<File> classFile = new ArrayList<>();
        //简单点直接双层解析即可
        if (file.isDirectory()) {
            for (File f : file.listFiles()) {
                if (f.isDirectory()) {
                    for (File f1 : f.listFiles()) {
                        if (!f1.isDirectory()) {
                            classFile.add(f1);
                        }
                    }
                } else {
                    classFile.add(f);
                }
            }
        }
        //遍历所有解析文件
        for (File cFile : classFile) {
            String absolutePath = cFile.getAbsolutePath();
            String className = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"))
                    .replace("\\", ".");

            try {
                Class<?> clazz = classLoader.loadClass(className);
                //是否需要被Spring管理
                if (clazz.isAnnotationPresent(Component.class)) {
                    //将bean上的注解封装到bean定义中
                    BeanDefinition beanDefinition = new BeanDefinition();
                    beanDefinition.setType(clazz);
                    beanDefinition.setLazy(clazz.isAnnotationPresent(Lazy.class));
                    if (clazz.isAnnotationPresent(Scope.class)) {
                        beanDefinition.setScope(clazz.getAnnotation(Scope.class).value());
                    } else {
                        beanDefinition.setScope("singleton");
                    }

                    String beanName = clazz.getAnnotation(Component.class).value();
                    if (beanName.isEmpty()) {
                        //如果不设置beanName会默认生产唯一一个name,Spring底层也是这样做的
                        beanName = Introspector.decapitalize(clazz.getSimpleName());
                    }

                    beanDefineMap.put(beanName, beanDefinition);

                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

        }
    }

    public Object createBean(String beanName){

        BeanDefinition beanDefinition = beanDefineMap.get(beanName);
        Class type = beanDefinition.getType();

        try {
            Object instance = type.newInstance();
            //属性填充,依赖注入
            populateBean(instance);

            if (instance instanceof ApplicationContextAware){
                ((ApplicationContextAware) instance).setApplicationContext(this);
            }
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }
            //是否需要AOP增强
            if (type.isAnnotationPresent(Transaction.class)) {
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(type);
                //简单的方法切面
                enhancer.setCallback(new MethodInterceptor() {
                    @Override
                    public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        //开启事务,关闭自动提交
                        System.out.println("事务已开启");
                        Object res = method.invoke(instance, objects);
                        //提交事务
                        System.out.println("事务已提交");
                        return res;
                    }
                });
                return enhancer.create();
            }
            return instance;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return null;
    }

    private void populateBean(Object instance) {
        Field[] declaredFields = instance.getClass().getDeclaredFields();

        Arrays.stream(declaredFields).forEach(item->{
            //寻找注入点
            if (item.isAnnotationPresent(AutoWired.class)) {
                Object bean = getBean(item.getName());
                item.setAccessible(true);
                try {
                    item.set(instance,bean);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        });

    }


    public Object getBean(String beanName){
        if (!beanDefineMap.containsKey(beanName)) {
           throw new NullPointerException();
        }
        if ("singleton".equals(beanDefineMap.get(beanName).getScope())) {
            if (singleBean.containsKey(beanName)) {
                return singleBean.get(beanName);
            } else {
                Object bean = createBean(beanName);
                singleBean.put(beanName,bean);
                return bean;
            }
        }
        return createBean(beanName);
    }
}

以上即为整个流程的基本梳理。我们在实现过程中没有涉及Bean循环依赖以及其他各种创建缓存,但Spring在实现Bean的创建过程中确实用到了各种本地缓存和同步锁(synchronized)。在学习源码时,不要太关注这些细节。首先要理解整个流程,再深入研究。

结语

最后,我们在gitee上提供了项目源码。如果需要,可以查看spring-xiaoyu。虽然我认为写一遍自己的代码更好,因为这是最简单的流程,有助于理解Spring源码文章来源地址https://www.toymoban.com/news/detail-411436.html

到了这里,关于Spring源码系列:初探底层,手写Spring的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 从零开始学Spring Boot系列-前言

    在数字化和信息化的时代,Java作为一种成熟、稳定且广泛应用的编程语言,已经成为构建企业级应用的首选。而在Java生态系统中,Spring框架无疑是其中最为耀眼的一颗明星。它提供了全面的编程和配置模型,用于构建企业级应用。随着Spring Boot的出现,这一框架变得更加易于

    2024年02月22日
    浏览(11)
  • Spring系列五:手动实现Spring底层机制

    Spring系列五:手动实现Spring底层机制

    上文中, 我们学习到了 Spring系列四:AOP切面编程 接下来我们学习, 手动实现Spring底层机制 ● java的类加载器 3 种 Bootstrap类加载器---------------------对应路径jrelib Ext类加载器------------------------------对应路径jrelibext App类加载器-----------------------------对应路径classpath ●classpath 类路

    2024年02月12日
    浏览(3)
  • Spring手写模拟源码篇(你值得拥有)

    Spring手写模拟源码篇(你值得拥有)

    下面是本文章关于Spring底层原理的章节 类-》推断构造方法-》根据构造方法创建普通对象-》依赖注入(@Autowired等进行属性注入)-》初始化前(@PostConstruct)-初始化(InitializingBean)-》初始化后(AOP)-》代理对象(没有开启AOP就会把普通对象放入单例池)-》放入单例池-》Bean对象

    2024年02月01日
    浏览(12)
  • 【Spring】Spring之事务底层源码解析

    能使用spring事务解决开发需求 了解spring事务是如何被spring管理的 了解spring事务底层原理实现,比如代理、事务传播机制等 配置数据源及事务管理器: 业务逻辑代码: 调用: 结果: 如上,就是Spring中事务的简单应用,接下来分析Spring事务的实现原理。 开启Spring事务本质上

    2024年02月14日
    浏览(12)
  • 【Spring】Spring之AOP底层源码解析

    【Spring】Spring之AOP底层源码解析

    理解Spring中AOP的实现原理 Spring中有关AOP功能的使用 什么是动态代理: 为 其他对象 提供一种 代理 以控制对这个对象的访问,增强一个类中的某个方法,对程序进行扩展。 如下一个service类,如果想不改变test方法代码的情况下,增加特定的逻辑,该怎么做呢? 此时就可以用

    2024年02月14日
    浏览(10)
  • 手写axios源码系列二:创建axios函数对象

    手写axios源码系列二:创建axios函数对象

    当前篇章正式进入手写 axios 源码系列,我们要真枪实弹的开始写代码了。 因为 axios 源码的代码量比较庞大,所以我们这里只抓主线,让大家对 axios 的主要核心功能有个大概了解,至于具体的细节深入,需要大家去通读 axios 源码了解。手写 axios 源码系列只将其中的4个主要功

    2023年04月23日
    浏览(8)
  • 【框架源码】手写Spring框架IOC容器核心流程

    【框架源码】手写Spring框架IOC容器核心流程

    要是想要了解Spring IOC底层,首先我们就得先了解什么是IOC。 IOC就是控制反转,把对象创建和对象之间的调用过程交给Spring进行管理。 使用IOC目的就是之前我们创建对象的方式都是用new的方式创建,这个方式有个缺点,被创建对象的类的位置一旦被改动,new就会报错,类与类

    2024年02月06日
    浏览(14)
  • Spring源码(二)Spring底层架构核心概念解析

    BeanDefinition表示 Bean定义 ,BeanDefinition中存在很多属性用来描述一个Bean的特点。比如: class,表示Bean类型 scope,表示Bean作用域,单例或原型等 lazyInit:表示Bean是否是懒加载 initMethodName:表示Bean初始化时要执行的方法 destroyMethodName:表示Bean销毁时要执行的方法 在Spring中,我

    2024年02月15日
    浏览(10)
  • 手写axios源码系列一:axios核心知识点

    手写axios源码系列一:axios核心知识点

    最近从头搭建了一个vue小项目,想使用 axios 作为请求接口的第三方库。结果使用了 axios 这么长时间,想封装一下 axios ,也就是大部分项目中的 src/utils/request.js 文件,我居然无法默写出来,create 和 interceptors 这样的 api 都不知道怎么使用;所以决定深入一下 axios 源码,一探究

    2023年04月22日
    浏览(9)
  • 【手撕Spring源码】一些值得注意的Spring底层细节

    【手撕Spring源码】一些值得注意的Spring底层细节

    FactoryBean是一个Factory对象,用于生成其他bean示例。当一个bean实现FactoryBean接口后,Spring容器调用其getObject方法返回该工厂所生成的bean,而不是该FactoryBean本身。但是工厂生成的产品对象只能说是一部分受到Spring容器的管理,我们来看看下面的例子: 我们把一个Bean1通过工厂Bean的

    2024年02月09日
    浏览(10)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包