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

这篇文章主要带大家实现一个简单的Spring框架,包含单例、多例bean的获取,依赖注入、懒加载等功能。文章内容会持续更新,感兴趣的小伙伴可以持续关注一下。

目录

一、创建Java项目

二、开始实现Spring

1、创建BeanFactory接口

2、创建ApplicationContext接口

3、创建ApplicationContext接口的实现类

4、实现Spring IOC功能

创建配置类

创建自定义注解

@Lazy

@Bean 

@Scope

@Configuration

@Component

@Repository

@Service

@Controller

@ComponentScan

创建bean的定义

修改AnnotationConfigApplicationContext

5、创建单例bean和非单例bean

6、使用Spring获取bean对象

7、未完待续


一、创建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> {@Overridepublic Object getBean(String beanName) {return null;}@Overridepublic T getBean(Class<T> type) {return null;}@Overridepublic 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;}@Overridepublic Object getBean(String beanName) {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if (beanDefinition == null) {throw new NullPointerException();}return getBean(beanName, beanDefinition);}@Overridepublic T getBean(Class<T> type) {if (type == null) {throw new IllegalStateException("bean类型不能为空!");}// 保存指定类型的bean的个数AtomicInteger count = new AtomicInteger();// 保存同一类型的beanMap<String, BeanDefinition> objectMap = new HashMap<>();for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {String beanName = entry.getKey();BeanDefinition beanDefinition = entry.getValue();Class beanType = beanDefinition.getType();if (beanType.equals(type)) {count.addAndGet(1);objectMap.put(beanName, beanDefinition);}}if (count.get() == 0 || count.get() > 1) {throw new IllegalStateException();}return (T) getBean((String) objectMap.keySet().toArray()[0], (BeanDefinition) objectMap.values().toArray()[0]);}@Overridepublic 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) getBean(beanName, beanDefinition);}throw new IllegalStateException();}/*** 统一获取bean的方法* @param beanName* @param beanDefinition* @return*/private Object getBean(String beanName, BeanDefinition beanDefinition) {String scope = beanDefinition.getScope();if (isSingleton(scope)) {Object object = singletonObjects.get(beanName);// 懒加载的单例beanif (object == null) {Object bean = createBean(beanDefinition);singletonObjects.put(beanName, bean);}return singletonObjects.get(beanName);}return createBean(beanDefinition);}}

5、创建单例bean和非单例bean

创建一个UserService的单例bean

package com.example.spring;/*** @author heyunlin* @version 1.0*/
@Component
public class UserService {}

创建一个UserMapper的非单例bean

package com.example.spring;/*** @author heyunlin* @version 1.0*/
@Component
@Scope("prototype")
public class UserMapper {}

6、使用Spring获取bean对象

package com.example.spring;/*** @author heyunlin* @version 1.0*/
public class SpringExample {public static void main(String[] args) throws ClassNotFoundException {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);Object userService = applicationContext.getBean("userService");System.out.println(userService);Object userService1 = applicationContext.getBean("userService", UserService.class);System.out.println(userService1);Object userService2 = applicationContext.getBean(UserService.class);System.out.println(userService2);Object userMapper = applicationContext.getBean("userMapper");Object userMapper1 = applicationContext.getBean("userMapper");Object userMapper2 = applicationContext.getBean("userMapper");System.out.println(userMapper);System.out.println(userMapper1);System.out.println(userMapper2);}}

通过上面三种方法获取到的bean是同一个

7、未完待续

文章持续更新中,敬请期待~

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机竞赛 基于CNN实现谣言检测 - python 深度学习 机器学习

文章目录 1 前言1.1 背景 2 数据集3 实现过程4 CNN网络实现5 模型训练部分6 模型评估7 预测结果8 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于CNN实现谣言检测 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&am…

Java—实现多线程程序 | 入门

目录 一、前言 二、基本概念 进程 线程 三、Java多线程实现 java.lang.Thread类 获取线程名字及对象 获取main进程名 Thread currentThread() 四、线程优先级 设置优先级 一、前言 前期入门学习的代码中&#xff0c;全部都是单线的程序&#xff0c;也就是从头到尾…

RabbitMQ---订阅模型-Fanout

1、 订阅模型-Fanout Fanout&#xff0c;也称为广播。 流程图&#xff1a; 在广播模式下&#xff0c;消息发送流程是这样的&#xff1a; 1&#xff09; 可以有多个消费者 2&#xff09; 每个消费者有自己的queue&#xff08;队列&#xff09; 3&#xff09; 每个队列都要绑定…

Github的使用指南

首次创建仓库 1.官网创建仓库 打开giuhub官网&#xff0c;右上角点击你的头像&#xff0c;随后点击your repositories 点击New开始创建仓库 如下图为创建仓库的选项解释 出现如下界面就可以进行后续的git指令操作了 2.git上传项目 进入需上传项目的所在目录&#xff0c;打开…

grpc整合Springboot

一、grpc的依赖 <dependencies><dependency><groupId>net.devh</groupId><artifactId>grpc-server-spring-boot-starter</artifactId><version>2.9.0.RELEASE</version></dependency></dependencies><build>…

【C++心愿便利店】No.3---内联函数、auto、范围for、nullptr

文章目录 前言&#x1f31f;一、内联函数&#x1f30f;1.1.面试题&#x1f30f;1.2.内联函数概念&#x1f30f;1.3.内联函数特性 &#x1f31f;二、auto关键字&#x1f30f;2.1.类型别名思考&#x1f30f;2.2.auto简介&#x1f30f;2.3.auto的使用细节&#x1f30f;2.4.auto不能…

探索图结构:从基础到算法应用

文章目录 理解图的基本概念学习图的遍历算法学习最短路径算法案例分析&#xff1a;使用 Dijkstra 算法找出最短路径结论 &#x1f389;欢迎来到数据结构学习专栏~探索图结构&#xff1a;从基础到算法应用 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;I…

代理池在过程中一直运行

Hey&#xff0c;爬虫达人们&#xff01;在爬虫的过程中&#xff0c;要保持代理池的稳定性可不容易。今天就来和大家分享一些实用经验&#xff0c;教你如何让代理池在爬虫过程中一直运行&#xff01;方法简单易行&#xff0c;让你的爬虫工作更顺畅. 在进行爬虫工作时&#xff0…

KVM虚拟化平台安装及创建虚拟机

文章目录 一、KVM 简介二、安装KVM虚拟化平台1、方式一&#xff1a;安装操作系统时&#xff0c;添加虚拟化功能2、方式二&#xff1a;基于现有系统&#xff0c;安装虚拟化功能3、验证KVM安装是否无误 三、创建虚拟机1、创建虚拟机前环境准备工作2、创建CentOS7.5系统虚拟机 一、…

保姆级使用vmware安装Ubuntu-server版

保姆级VMware安装Ubuntu20服务器版 文章目录 保姆级VMware安装Ubuntu20服务器版前期准备一、安装vmware二、下载Ubuntu镜像 VMware安装Ubuntu201. 启动Workstation Pro或者Workstation Player,进入软件后新建一个虚拟机2. 进入引导界面选择默认的即可3. 点击下一步即可4. 选择操…

QT5.12.12通过ODBC连接到GBase 8s数据库(CentOS)

本示例使用的环境如下&#xff1a; 硬件平台&#xff1a;x86_64&#xff08;amd64&#xff09;操作系统&#xff1a;CentOS 7.8 2003数据库版本&#xff08;含CSDK&#xff09;&#xff1a;GBase 8s V8.8 3.0.0_1 为什么使用QT 5.12.10&#xff1f;该版本包含QODBC。 1&#…

1782. 统计点对的数目

给你一个无向图&#xff0c;无向图由整数 n &#xff0c;表示图中节点的数目&#xff0c;和 edges 组成&#xff0c;其中 edges[i] [ui, vi] 表示 ui 和 vi 之间有一条无向边。同时给你一个代表查询的整数数组 queries 。 第 j 个查询的答案是满足如下条件的点对 (a, b) 的数…