手写Spring源码——实现一个简单的spring framework

news2024/12/23 1:24:21

这篇文章主要带大家实现一个简单的Spring框架,包含单例、多例bean的获取,依赖注入、懒加载等功能。

一、创建Java项目

首先,需要创建一个Java工程,名字就叫spring。

创建完成后,如下图,再依次创建三级包

二、开始实现Spring

Spring中最重要也是最基础的类就是Spring容器,Spring容器用于创建管理对象,为了方便实现类型转换功能,给接口设置一个参数化类型(泛型)。

1、创建BeanFactory接口

BeanFactory是spring容器的顶级接口,在该接口中定义三个重载的获取bean的方法。

package com.example.spring;

/**
 * @author heyunlin
 * @version 1.0
 */
public interface BeanFactory<T> {
    
    Object getBean(String beanName);
    
    T getBean(Class<T> type);
    
    T getBean(String beanName, Class<T> type);
}

2、创建ApplicationContext接口

ApplicationContext接口扩展自BeanFactory接口

package com.example.spring;

/**
 * @author heyunlin
 * @version 1.0
 */
public interface ApplicationContext<T> extends BeanFactory<T> {
    
}

3、创建ApplicationContext接口的实现类

创建一个ApplicationContext接口的实现类,实现接口中定义的所有抽象方法。

package com.example.spring;

/**
 * @author heyunlin
 * @version 1.0
 */
public class AnnotationConfigApplicationContext<T> implements ApplicationContext<T> {

    @Override
    public Object getBean(String beanName) {
        return null;
    }

    @Override
    public T getBean(Class<T> type) {
        return null;
    }

    @Override
    public T getBean(String beanName, Class<T> type) {
        return null;
    }

}

4、实现Spring IOC功能

首先,组件扫描需要一个扫描路径,可以通过配置类上的@ComponentScan注解指定,如果不指定,则默认为配置类所在的包。

创建配置类

在当前包下创建一个类,配置包扫描路径。

package com.example.spring;

/**
 * @author heyunlin
 * @version 1.0
 */
@ComponentScan("com.example.spring")
public class SpringConfig {
    
}

创建自定义注解

@Lazy
package com.example.spring;

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

/**
 * @author heyunlin
 * @version 1.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Lazy {

    boolean value() default false;
}
@Bean 
package com.example.spring;

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

/**
 * @author heyunlin
 * @version 1.0
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {

    String value();
}

@Scope
package com.example.spring;

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

/**
 * @author heyunlin
 * @version 1.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {

    String value();
}
@Configuration
package com.example.spring;

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

/**
 * @author heyunlin
 * @version 1.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Configuration {

    String value();
}

@Component
package com.example.spring;

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

/**
 * @author heyunlin
 * @version 1.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {

    String value() default "";
}

@Repository
package com.example.spring;

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

/**
 * @author heyunlin
 * @version 1.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Repository {

    String value();
}

@Service
package com.example.spring;

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

/**
 * @author heyunlin
 * @version 1.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Service {

    String value();
}

@Controller
package com.example.spring;

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

/**
 * @author heyunlin
 * @version 1.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Controller {

    String value();
}

@ComponentScan
package com.example.spring;

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

/**
 * @author heyunlin
 * @version 1.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    
    String value();
}

创建bean的定义

package com.example.spring;

/**
 * @author heyunlin
 * @version 1.0
 */
public class BeanDefinition {

    /**
     * bean的类型
     */
    private Class type;

    /**
     * bean的作用域
     */
    private String scope;

    /**
     * 是否懒加载
     */
    private boolean lazy;

    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 lazy;
    }

    public void setLazy(boolean lazy) {
        this.lazy = lazy;
    }

}

修改AnnotationConfigApplicationContext

1、添加一个属性clazz,用于保存实例化时传递的配置类对象参数

2、Spring容器中创建用于保存bean的定义的map

3、创建单例对象池,也是一个map,保存单例bean

package com.example.spring;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author heyunlin
 * @version 1.0
 */
public class AnnotationConfigApplicationContext<T> implements ApplicationContext<T> {

    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    /**
     * 单例对象池
     */
    private Map<String, Object> singletonObjects = new HashMap<>();

    public final Class<T> clazz;

    public AnnotationConfigApplicationContext(Class<T> clazz) throws ClassNotFoundException {
        this.clazz = clazz;

        // 扫描组件,保存到BeanDefinition中
        scan(clazz);

        // 把组件中非懒加载的单例bean保存到单例池
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();

            if(isSingleton(beanDefinition.getScope()) && !beanDefinition.isLazy()) {
                Object bean = createBean(beanDefinition);

                singletonObjects.put(beanName, bean);
            }
        }
    }

    /**
     * 创建bean对象
     * @param beanDefinition bean的定义
     * @return Object 创建好的bean对象
     */
    private Object createBean(BeanDefinition beanDefinition) {
        Object bean = null;
        Class beanType = beanDefinition.getType();

        // 获取所有构造方法
        Constructor[] constructors = beanType.getConstructors();

        try {
            /**
             * 推断构造方法
             * 1、没有提供构造方法:调用默认的无参构造
             * 2、提供了构造方法:
             *   - 构造方法个数为1
             *     - 构造方法参数个数为0:无参构造
             *     - 构造方法参数个数不为0:传入多个为空的参数
             *   - 构造方法个数 > 1:推断失败,抛出异常
             */
            // 无参构造方法
            Constructor constructor = beanType.getConstructor();

            bean = constructor.newInstance();
//            if (isEmpty(constructors)) {
//                // 无参构造方法
//                Constructor constructor = beanType.getConstructor();
//
//                bean = constructor.newInstance();
//            } else if (constructors.length == 1) {
//                Constructor constructor = constructors[0];
//                // 得到构造方法参数个数
//                int parameterCount = constructor.getParameterCount();
//
//                if (parameterCount == 0) {
//                    // 无参构造方法
//                    bean = constructor.newInstance();
//                } else {
//                    // 多个参数的构造方法
//                    Object[] array = new Object[parameterCount];
//
//                    bean = constructor.newInstance(array);
//                }
//            } else {
//                throw new IllegalStateException();
//            }
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }

        return bean;
    }

    private boolean isEmpty(Object[] array) {
        return array.length == 0;
    }

    private boolean isSingleton(String scope) {
        return "singleton".equals(scope);
    }

    private void scan(Class<T> clazz) throws ClassNotFoundException {
        if (clazz.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScan = clazz.getAnnotation(ComponentScan.class);
            String value = componentScan.value();

            if (!"".equals(value)) {
                String path = value;
                path = path.replace(".", "/");

                URL resource = clazz.getClassLoader().getResource(path);
                File file = new File(resource.getFile());

                loopFor(file);
            }
        }
    }

    private void loopFor(File file) throws ClassNotFoundException {
        if (file.isDirectory()) {
            for (File listFile : file.listFiles()) {
                if (listFile.isDirectory()) {
                    loopFor(listFile);

                    continue;
                }

                toBeanDefinitionMap(listFile);
            }
        } else if (file.isFile()) {
            toBeanDefinitionMap(file);
        }
    }

    private void toBeanDefinitionMap(File file) throws ClassNotFoundException {
        String absolutePath = file.getAbsolutePath();
        absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
        absolutePath = absolutePath.replace("\\", ".");
        Class<?> loadClass = clazz.getClassLoader().loadClass(absolutePath);

        String beanName;

        if (loadClass.isAnnotationPresent(Component.class)) {
            Component component = loadClass.getAnnotation(Component.class);
            beanName = component.value();

            if ("".equals(beanName)) {
                beanName = getBeanName(loadClass);
            }

            boolean lazy = false;
            String scope = "singleton";

            // 类上使用了@Scope注解
            if (loadClass.isAnnotationPresent(Scope.class)) {
                // 获取@Scope注解
                Scope annotation = loadClass.getAnnotation(Scope.class);

                // 单例
                if (isSingleton(annotation.value())) {
                    if (loadClass.isAnnotationPresent(Lazy.class)) {
                        Lazy loadClassAnnotation = loadClass.getAnnotation(Lazy.class);

                        if (loadClassAnnotation.value()) {
                            lazy = true;
                        }
                    }
                } else {
                    // 非单例
                    scope = annotation.value();
                }
            } else {
                // 类上没有使用@Scope注解,默认是单例的
                if (loadClass.isAnnotationPresent(Lazy.class)) {
                    Lazy annotation = loadClass.getAnnotation(Lazy.class);

                    if (annotation.value()) {
                        lazy = true;
                    }
                }
            }

            // 保存bean的定义
            BeanDefinition beanDefinition = new BeanDefinition();

            beanDefinition.setType(loadClass);
            beanDefinition.setLazy(lazy);
            beanDefinition.setScope(scope);

            beanDefinitionMap.put(beanName, beanDefinition);
        }
    }

    /**
     * 根据类对象获取beanName
     * @param loadClass bean的Class对象
     * @return String beanName
     */
    private String getBeanName(Class<?> loadClass) {
        String beanName = loadClass.getSimpleName();

        // 判断是否以双大写字母开头
        String className = beanName.replaceAll("([A-Z])([A-Z])", "$1_$2");

        // 正常的大驼峰命名:bean名称为类名首字母大写
        if (className.indexOf("_") != 1) {
            beanName = beanName.substring(0, 1).toLowerCase().concat(beanName.substring(1));
        }
//        else { // 否则,bean名称为类名
//            beanName = beanName;
//        }

        return beanName;
    }

    @Override
    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        if (beanDefinition == null) {
            throw new NullPointerException();
        }

        String scope = beanDefinition.getScope();

        if (isSingleton(scope)) {
            Object object = singletonObjects.get(beanName);

            // 懒加载的单例bean
            if (object == null) {
                Object bean = createBean(beanDefinition);

                singletonObjects.put(beanName, bean);
            }
        }

        return singletonObjects.get(beanName);
    }

    @Override
    public T getBean(Class<T> type) {
        if (type == null) {
            throw new IllegalStateException("bean类型不能为空!");
        }

        BeanDefinition beanDefinition = null;
        // 保存指定类型的bean的个数
        AtomicInteger count = new AtomicInteger();

        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            beanDefinition = entry.getValue();
            Class beanType = beanDefinition.getType();

            if (beanType.equals(type)) {
                count.addAndGet(1);
            }
        }

        if (count.get() == 0 || count.get() > 1) {
            throw new IllegalStateException();
        }

        return (T) createBean(beanDefinition);
    }

    @Override
    public T getBean(String beanName, Class<T> type) {
        if (type == null) {
            throw new IllegalStateException("bean类型不能为空!");
        }

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        if (type.equals(beanDefinition.getType())) {
            return (T) createBean(beanDefinition);
        }

        throw new IllegalStateException();
    }

}

5、使用Spring获取bean对象

package com.example.spring;

/**
 * @author heyunlin
 * @version 1.0
 */
public class SpringExample {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
        Object bean = applicationContext.getBean("beanName");
        
        System.out.println(bean);
    }

}

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

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

相关文章

Linux系统编程系列之进程基础

一、什么是进程 关于进程的定义很多&#xff0c;这里讲一种比较直接的&#xff0c;进程就是程序中的代码和数据被加载到内存中运行的过程&#xff0c;就是程序的执行过程。进程是动态的&#xff0c;而程序是静态的。程序存储在硬盘里&#xff0c;进程只有在程序被执行后&#x…

生信分析Python实战练习 1 | 视频18

开源生信 Python教程 生信专用简明 Python 文字和视频教程 源码在&#xff1a;https://github.com/Tong-Chen/Bioinfo_course_python 目录 背景介绍 编程开篇为什么学习Python如何安装Python如何运行Python命令和脚本使用什么编辑器写Python脚本Python程序事例Python基本语法 数…

STM32 Cubemx配置串口收发

文章目录 前言注意事项Cubemx配置printf重定向修改工程属性修改源码 测试函数 前言 最近学到了串口收发&#xff0c;简单记录一下注意事项。 注意事项 Cubemx配置 以使用USART1为例。 USART1需配置成异步工作模式Asynchronous。 并且需要使能NVIC。 printf重定向 我偏向…

使用cgroup工具对服务器某些/全部用户进行计算资源限制

使用cgroup工具对服务器某些/全部用户进行计算资源限制 主要介绍&#xff0c;如何对指定/所有用户进行资源限定&#xff08;这里主要介绍cpu和内存占用限制&#xff09;&#xff0c;防止某些用户大量占用服务器计算资源&#xff0c;影响和挤占他人正常使用服务器。 安装cgrou…

Transformer代码计算过程全解

条件设置 batch_size1 src_len 8 # 源句子的最大长度 根据这个进行padding的填充 tgt_len 7 # 目标输入句子的最大长度 根据这个进行padding的填充 d_model512 # embedding的维度 d_ff2048 # 全连接层的维度 h_head8 # Multi-Head Attention 的…

【C++】—— C++11之可变参数模板

前言&#xff1a; 在C语言中&#xff0c;我们谈论了有关可变参数的相关知识。在C11中引入了一个新特性---即可变参数模板。本期&#xff0c;我们将要介绍的就是有关可变参数模板的相关知识&#xff01;&#xff01;&#xff01; 目录 序言 &#xff08;一&#xff09;可变参…

深度学习10:Attention 机制

目录 Attention 的本质是什么 Attention 的3大优点 Attention 的原理 Attention 的 N 种类型 Attention 的本质是什么 Attention&#xff08;注意力&#xff09;机制如果浅层的理解&#xff0c;跟他的名字非常匹配。他的核心逻辑就是「从关注全部到关注重点」。 Attention…

ServiceManager接收APP的跨进程Binder通信流程分析

现在一起来分析Server端接收&#xff08;来自APP端&#xff09;Binder数据的整个过程&#xff0c;还是以ServiceManager这个Server为例进行分析,这是一个至下而上的分析过程。 在分析之前先思考ServiceManager是什么&#xff1f;它其实是一个独立的进程&#xff0c;由init解析i…

windows11不允许安装winpcap4.1.3

问题&#xff1a;下载安装包后在安装时显示与电脑系统不兼容&#xff0c;不能安装。 原因&#xff1a;winpcap是一个用于Windows操作系统的网络抓包库&#xff0c;有一些安全漏洞&#xff0c;存在被黑客攻击的风险。Windows11为了加强系统安全而禁用了这个库&#xff0c;因此不…

java.8 - java -overrideoverload 重写和重载

重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变&#xff0c;核心重写&#xff01; 重写的好处在于子类可以根据需要&#xff0c;定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。 重写方法不…

【GAMES202】Real-Time Environment Mapping1—实时环境光照1

一、Distance field soft shadows Inigo Quilez :: computer graphics, mathematics, shaders, fractals, demoscene and more (iquilezles.org) 在开始我们的实时环境光照之前&#xff0c;我们再说一种现在的实现实时软阴影的方式&#xff0c;也就是Distance field soft shado…

SpringBoot实现文件上传和下载笔记分享(提供Gitee源码)

前言&#xff1a;这边汇总了一下目前SpringBoot项目当中常见文件上传和下载的功能&#xff0c;一共三种常见的下载方式和一种上传方式&#xff0c;特此做一个笔记分享。 目录 一、pom依赖 二、yml配置文件 三、文件下载 3.1、使用Spring框架提供的下载方式 3.2、通过IOUti…

分布式 - 服务器Nginx:一小时入门系列之 return 指令

文章目录 1. return 指令语法2. return code URL 示例3. return code text 示例4. return URL 示例 1. return 指令语法 return指令用于立即停止当前请求的处理&#xff0c;并返回指定的HTTP状态码和响应头信息&#xff0c;它可以用于在Nginx中生成自定义错误页面&#xff0c;…

分布式事务-seata框架

文章目录 分布式事务0.学习目标1.分布式事务问题1.1.本地事务1.2.分布式事务1.3.演示分布式事务问题 2.理论基础2.1.CAP定理2.1.1.一致性2.1.2.可用性2.1.3.分区容错2.1.4.矛盾 2.2.BASE理论2.3.解决分布式事务的思路 3.初识Seata3.1.Seata的架构3.2.部署TC服务3.3.微服务集成S…

CAPL - Panel和TestModule结合实现测试项可选

目录 一、定义脚本编号和脚本组编号 1、测试组定义 2、测试脚本编号定义

【C++】初步认识模板

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;C的学习之路 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录 前言一、泛型编程二、函数模板2.1 函…

Java10(异常处理)

0.复习面向对象 1.异常的体系结构 异常&#xff1a;在Java语言中&#xff0c;将程序执行中发生的不正常情况.(开发中的语法错误和逻辑错误不是异常) 异常事件分两类&#xff08;它们上一级为java.lang.Throwable&#xff09;&#xff1a; Error Java虚拟机无法解决的严重问…

算法通过村第三关-数组黄金笔记|数组难解

文章目录 前言数组中出现超过一半的数字数组中只出现一次的数字颜色的分类问题(荷兰国旗问题)基于冒泡排序的双指针&#xff08;快慢指针&#xff09;基于快排的双指针&#xff08;对撞指针&#xff09; 总结 前言 提示&#xff1a;苦不来自外在环境中的人、事、物&#xff0c;…

最新人工智能源码搭建部署教程/ChatGPT程序源码/AI系统/H5端+微信公众号版本源码

一、AI系统 如何搭建部署人工智能源码、AI创作系统、ChatGPT系统呢&#xff1f;小编这里写一个详细图文教程吧&#xff01; SparkAi使用Nestjs和Vue3框架技术&#xff0c;持续集成AI能力到AIGC系统&#xff01; 1.1 程序核心功能 程序已支持ChatGPT3.5/GPT-4提问、AI绘画、…

Vue3.0极速入门- 目录和文件说明

目录结构 以下文件均为npm create helloworld自动生成的文件目录结构 目录截图 目录说明 目录/文件说明node_modulesnpm 加载的项目依赖模块src这里是我们要开发的目录&#xff0c;基本上要做的事情都在这个目录里assets放置一些图片&#xff0c;如logo等。componentsvue组件…