手撸Java Spring
手撸Java Spring
SerMs首先,我们需要明白什么是BeanFactory和Ioc容器。在Java中,BeanFactory是一种用于创建和管理对象(也称为bean)的机制,而Ioc(Inversion of Control,控制反转)容器则是负责实现BeanFactory的框架。简单来说,BeanFactory就像是一个工厂,根据我们的需求来创建和提供对象。
简介
Spring IOC容器的概念
Spring IOC(控制反转)容器是Spring框架的核心组件之一。它负责管理应用程序中的对象,实现了对象的创建、组装和管理等功能。IOC容器通过反转控制,将对象的创建和依赖注入的责任从应用程序代码转移到容器中,提供了更高的灵活性和可测试性。
DI(依赖注入)
依赖注入(DI)是IOC容器的重要特性之一。通过DI,对象的依赖关系由容器在运行时动态地注入,而不是由对象自己负责创建或查找依赖的实例。这种解耦的方式使得对象之间的协作更加灵活、可扩展和易于维护。
源码分析
获取Bean
我们先来看一下最常用的getBean()方法,在实现上,该方法主要分为三个步骤:
- 获取BeanDefinition
- 创建Bean实例
- 初始化Bean
获取BeanDefinition
前两个步骤非常简单,我们直接来看第一步的实现。获取BeanDefinition主要调用的是DefaultListableBeanFactory类中的getBeanDefinition()方法,该方法返回的就是Bean的定义信息。
1 |
|
上述方法中,beanDefinitionMap是一个ConcurrentHashMap,用来缓存BeanDefinition对象,key为Bean的名称,value为BeanDefinition对象,这个容器是Spring IOC管理Bean的核心,后面的初始化Bean和创建Bean都是基于这个容器进行的,我们可以看到这个方法先从容器中获取BeanDefinition对象,如果获取到就直接返回,如果获取不到就抛出一个NoSuchBeanDefinitionException异常。
创建Bean实例
下面是Bean实例化的主要过程(主要包含了Bean的创建,包括构造函数的调用和依赖注入等逻辑):
1 | protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) { |
上述代码就是Bean的主要构造过程,其中主要包括了工厂方法构造、构造函数调用、属性注入、初始化等逻辑。
初始化Bean
最后一步就是初始化Bean了,这个过程主要包括以下三个方法:
firstly, applyBeanPostProcessorsBeforeInitialization(ob, beanName);
secondly, invokeInitMethods(beanName, wrappedBean, mbd);
thirdly, applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
这个方法可以在BeanDefinitionReaderUtils类中找到。
Bean注入
pring在完成获取Bean和创建Bean的工作之后,需要将需要的Bean注入到需要该Bean的地方。在Spring中,Bean注入主要分为以下几种方式:
- 构造函数注入
- setter注入
- 通过注解实现的注入
对于构造函数注入,Spring主要通过查找构造函数的参数及其类型,然后根据类型及其名称去容器中找到对应的Bean,最终完成注入。
对于setter注入,Spring则是直接对Bean的属性进行注入,setter属性注入其实是Spring IOC容器的一个特殊的语法糖,开发者只需要定义好setter方法,在容器中就可以完成 Bean的注入,非常方便。
对于通过注解实现的注入,所谓的注解指的是@Resource、@Autowired注解。这种方式只需要在需要注入的属性上添加对应的@Autowired或@Resource注解,Spring IOC就会自动完成注入。
在源码中,Spring IOC的关键部分是BeanDefinition和BeanFactory的实现。BeanDefinition定义了Bean的元数据,包括类名、属性、依赖关系等信息。BeanFactory负责管理Bean的生命周期,根据BeanDefinition创建Bean实例,并处理依赖注入。
Spring IOC的核心原理是通过反射机制实现动态创建和初始化Bean对象。当容器启动时,会解析配置文件或注解,将Bean的定义转化为BeanDefinition对象,并缓存在IOC容器中。当需要获取Bean实例时,容器会根据BeanDefinition创建Bean对象,并将依赖注入到对应的属性中。
🔥开始手撕IOC
创建基本的Maven项目咱直接略过。。。。
首先我们通过读取xml配置文件的方式来实现,即需要引入demo4j的依赖来帮助我们解析配置文件内的内容
1 | <dependency> |
定义Xml
定义⼀个外部的XML,⽤于声明Bean: applicationContext.xml
1 | <beans> |
编写BeanDefinition
这个类表示一个bean的定义,包含了两个属性:id和className。其中,id是bean的唯一标识符,className是该bean对应的类名property就是属性,因我们的property属性可能有多个,并且对象类型也存在不同,所以这边直接采用List 集合的方式。
1 | public class BeanDefinition { |
编写Bean工厂接口
BeanFactory
接口是一个定义了创建和获取bean对象的机制的接口。
在Java中,我们可以把bean想象成应用程序中的各种对象,例如服务、工具、数据对象等等。而BeanFactory
就像是一个工厂,负责根据我们的需求来创建和提供这些对象。
下面是BeanFactory
接口中定义的方法:
getBean(String beanName)
:根据指定的beanName
获取对应的bean对象。通过调用这个方法,我们可以根据bean的名称来获取具体的对象实例。containsBean(String name)
:检查是否存在指定名称的bean。通过调用这个方法,我们可以判断某个特定名称的bean是否已经被创建和注册。registerBean(String beanName, Object obj)
:手动注册一个bean。通过调用这个方法,我们可以将一个对象注册为bean,并指定它的名称。
在下述代码中,我们创建了一个名为SimpleBeanFactory
的类,它是BeanFactory
接口的一个简单实现。
SimpleBeanFactory
类继承了DefaultSingletonBeanRegistry
类,这个类实现了SingletonBeanRegistry
接口,提供了单例bean的管理功能。
在后面的SimpleBeanFactory
类中,我们将要重写getBean
方法。当我们调用getBean
方法时,它会首先检查单例bean的管理器,即DefaultSingletonBeanRegistry
,看看是否存在指定名称的bean。如果存在,则返回对应的单例bean实例;如果不存在,则抛出异常。
这样,通过SimpleBeanFactory
创建的bean默认是单例的,因为它继承了单例bean管理的功能。
1 | public interface BeanFactory { |
创建SingletonBeanRegistry
接口
SingletonBeanRegistry
接口用于管理单例bean的注册和获取。
首先,让我们了解一下什么是单例bean。在Java中,单例bean是指只有一个实例存在的对象。在整个应用程序中,无论我们从何处获取该bean,都会得到同一个实例。
接下来,我们来解释SingletonBeanRegistry
接口的方法:
registerSingleton(String beanName, Object singletonObject)
:这个方法用于注册单例bean。我们可以通过指定的beanName
将一个对象注册为单例bean。注册后,我们可以使用beanName
来获取该单例bean的实例。getSingleton(String beanName)
:这个方法用于获取指定beanName
对应的单例bean实例。如果存在该单例bean,则返回其实例;如果不存在,则返回null
。containsSingleton(String beanName)
:这个方法用于检查是否存在指定名称的单例bean。如果存在,返回true
;如果不存在,返回false
。getAllSingletons()
:这个方法用于获取所有已注册的单例bean的映射关系。返回一个Map
,其中键是单例bean的名称,值是对应的单例bean实例。
SingletonBeanRegistry接口主要用于管理单例Bean的注册和获取。它定义了两个方法:
registerSingleton(String name, Object singleton): 将单例Bean注册到单例Bean容器中,其中name是该Bean的名称,singleton是该Bean的实例。
getSingleton(String name): 获取指定名称的单例Bean实例。
在下面代码中,我们实现了
SingletonBeanRegistry
接口的一个默认实现类DefaultSingletonBeanRegistry
。DefaultSingletonBeanRegistry
类维护了一个singletons
字典,用于存储单例bean的名称和对应的实例。在
registerSingleton
方法中,我们使用synchronized
关键字来确保在多线程环境下对单例bean的安全管理。我们将指定的singletonObject
对象与beanName
关联,并将其存储在singletons
字典中。getSingleton
方法根据beanName
从singletons
字典中获取相应的单例bean实例。containsSingleton
方法用于检查singletons
字典中是否存在指定名称的单例bean。getAllSingletons
方法返回singletons
字典,其中包含了所有已注册的单例bean的名称和实例。
此接口主要目的就是在整个Bean生命周期中只创建一次Bean,使用SingletonBeanRegistry可以方便地管理和获取单例Bean,保证每个单例Bean在整个应用中只存在一份,避免了重复创建和浪费资源的问题。同时,它还可以提供单例Bean之间的依赖注入和解耦的方式,使系统更加灵活、可维护和可扩展。
1 | public interface SingletonBeanRegistry { |
实现单例接口
创建DefaultSingletonBeanRegistry类,实现SingletonBeanRegistry
接口的所有方法,用于管理单例Bean的注册和获取。它维护了一个ConcurrentHashMap用于存储单例Bean,由名称和Bean对象组成,还维护了一个ConcurrentHashSet用于存储单例Bean名称。它提供了以下方法:
- registerSingleton(String beanName, Object singletonObject):用于注册单例Bean。如果集合中已经包含相同名称的Bean,则抛出异常。
- getSingleton(String beanName):用于根据名称获取单例Bean的实例。
- containsSingleton(String beanName):用于判断是否存在指定名称的Bean。
- getSingletonNames():用于获取所有单例Bean的名称。
- createBean(BeanDefinition beanDefinition, List beanDefinitions):用于根据BeanDefinition对象创建Bean的实例。其中,BeanDefinition对象是从beanDefinitions参数中获取,是bean的配置信息。
- getBeanDefinitionById(String id, List beanDefinitions):用于通过Bean的唯一标识符id获取对应的BeanDefinition对象。
在registerSingleton()方法中,使用synchronized锁定了singletons集合,以确保线程安全。在createBean()方法中,使用反射机制和依赖注入的方式创建和管理所有的Bean,若依赖对象未实例化,则会递归创建,并通过singletons集合进行缓存,以实现高效的访问。
1 | class DefaultSingletonBeanRegistry implements SingletonBeanRegistry { |
创建IOC容器
类
ClassPathXmlApplicationContext类实现了简单的IoC容器,它的作用是读取xml文件中的Bean配置信息,将其构建成BeanDefinition对象,然后使用反射创建Bean对象,并将其注册为单例对象,最后根据Bean的名称从容器中获取Bean实例。
在构造方法中,调用了readXml方法,该方法解析xml文件获取Bean的定义信息,并保存到beanDefinitions集合中。
instanceBeans方法用于创建Bean对象。它遍历beanDefinitions集合,先获取到一个Bean的定义信息,然后通过反射机制构建Bean对象,并将其添加到容器中。
getBean方法用于获取Bean对象。它内部调用了getSingleton方法,如果该Bean在容器中已经存在,就直接返回该Bean。否则,就抛出异常Bean is not defined: + beanName。
containsBean方法用于判断是否包含某个Bean对象,它内部调用了containsSingleton方法。
registerBean方法用于注册对象,它内部调用了registerSingleton方法。
1 | public class ClassPathXmlApplicationContext extends DefaultSingletonBeanRegistry implements BeanFactory { |
创建工厂实现类
接下来我们对Bean工厂做一个简单的实现,基于 Map 存储 Bean 定义和 Bean 实例的容器。它实现了 BeanFactory 接口,可以通过 getBean 方法获取 Bean 的实例,并且实现了 SingletonBeanRegistry 接口,可以注册和存储 Bean 的单例实例。
SimpleBeanFactory 主要用于演示 Spring IoC 容器的基本原理和实现方式,对于真正的应用场景来说,它的功能和扩展性都远远不足。在实际项目中,我们通常会使用更为强大和灵活的容器,如 Spring 容器或其他开源容器,这些容器提供了很多高级特性,如 AOP、事务管理、Web 开发支持等,可以大大简化应用程序的开发和维护。
1 | public class SimpleBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory { |
测试自定义IOC容器
创建Dao层以及Service层
在UserDao中创建接口,Dao实现类中实现具体的业务,Service在进行调用,由于代码过于简单就不做展示了。
创建Test
测试类
创建容器对象传入applicationContext.xml
文件。。。。。。。省略。。。。。
最后我们看运行结果
可以看到Serivce对象和Dao都以及分别进行了创建,实现了单例对象,并也实现了DI注入,如上就是所有代码过程,在整个SpringIOC的源码中实现的功能,远不止上述这些,上述只是简单的实现了一个IOC容器。如有写的不对的地方,还请指点!
以上就是 手撸SpringIOC 的全部内容,看完如果对你有帮助,感谢赞助支持!。
❤️Sponsor
您的支持是我不断前进的动力,如果您恰巧财力雄厚,又感觉本文对您有所帮助的话,可以考虑打赏一下本文,用以维持本博客的运营费用,拒绝白嫖,从你我做起!🥰🥰🥰
支付宝 | 微信 |
---|---|