手写Spring框架学习Spring原理之IOC(一)
GitHub地址: https://github.com/dixinjoker/king-spring-theory
为什么使用Spring框架
Spring-IOC原理
IOC是什么?
- 控制反转。将创建对象的权力交给Spring工厂,从new对象 ====》 向工厂去获取一个对象。
为什么用IOC?
- 面向接口编程,较之new对象的方式更利于解耦(跟具体的实现类解耦)。
- IOC工厂达成易于扩展、替换实现类的功能。使程序更灵活。
- AOP的基石
Spring-IOC的实现原理
ApplicationContext 扩展了 BeanFactory 接口,持有了一个BeanFactory 。BeanFactory 是Spring中bean的管理工厂。
开始手写
难道用脚写?
注意:idea新建项目为Spring Initializr类型(什么都不用勾选),这里我用的JDK版本是1.8。
项目结构:
第一步:定义BeanFactory 接口
IOC是一个bean的工厂,为我们提供Bean对象。
第二步:提供实现类 DefaultBeanFactory
- 创建实现类DefaultBeanFactory。
public class DefaultBeanFactory implements BeanFactory {
@Override
public Object getBean(String beanName) throws Throwable {
//如何创建这个名字的 bean实例
//需要知道用户的Bean定义信息----》首先提供一个功能让用户可以给出他的bean定义信息
return null;
}
2.test下sample包中创建 用户接口与实现类
public interface UserService {
void fun0();
}
public class UserServiceImpl implements UserService{
@Override
public void fun0() {
System.out.println(this + ".fun0()");
}
}
3.创建BeanDefinition 类,实现用户给出他的bean定义信息的功能
public class BeanDefinition {
private Class<?> beanClass;
public BeanDefinition(Class<?> beanClass) {
this.beanClass = beanClass;
}
public Class<?> getBeanClass() {
return beanClass;
}
public void setBeanClass(Class<?> beanClass) {
this.beanClass = beanClass;
}
}
第三步:提供 BeanDefinition 注册功能
提供给用户一个可以给入bean的定义信息的功能 BeanDefinition 注册功能
创建 BeanDefinitionRegistry接口 ,提供 注册功能接口
public interface BeanDefinitionRegistry {
void registerBeanDefinition(String beanName,BeanDefinition beanDefinition);
}
第四步:实现一个最简单的BeanFactory
1.完善DefaultBeanFactory
public class DefaultBeanFactory implements BeanFactory,BeanDefinitionRegistry {
//Bean定义的map,以beanName为key
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
@Override
public Object getBean(String beanName) throws Throwable {
//如何创建这个名字的 bean实例
//需要知道用户的Bean定义信息----》首先提供一个功能让用户可以给出他的bean定义信息
Object bean = null;
bean = createBeanInstance(beanName);
return bean;
}
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
//注册保存====》使用map存=====》在getBean方法中使用
this.beanDefinitionMap.put(beanName, beanDefinition);
}
private Object createBeanInstance(String beanName) throws IllegalAccessException, InstantiationException {
//使用最简单方法(调用构造方法)来创建实例
return beanDefinitionMap.get(beanName).getBeanClass().newInstance();
}
}
在这对以上代码进行说明:
大手子应该瞄一眼就看懂了吧。滑稽.jpg。简单说明一下
- DefaultBeanFactory 类实现bean工厂和bean定义注册接口;
- 选用map存放bean定义信息(new一个concurrentHashMap),key为beanName;
- registerBeanDefinition()方法内往map里面存beanName和beanDefinition;
- getBean()中创建bean实例并返回bean
- 快捷生成createBeanInstance(),使用最简单的构造方法创建实例。
2 .写完生成getBean()的测试类进行测试。没错,写完(一半了)!
class DefaultBeanFactoryTest {
DefaultBeanFactory defaultBeanFactory;
@BeforeEach
public void init() throws Throwable {
defaultBeanFactory = new DefaultBeanFactory();
BeanDefinition bd = new BeanDefinition(UserServiceImpl.class);
defaultBeanFactory.registerBeanDefinition("userService",bd);
}
@Test
void getBean() throws Throwable {
UserService userService = (UserService) defaultBeanFactory.getBean("userService");
userService.fun0();
}
}
在这对以上代码进行说明:
对我自己说明,哈哈哈哈- 完成初始化,进行测试。
- 这里需要注意的是 @BeforeEach注解。我用的是junit 5,如果是使用junit 4的靓仔这里替换为@Before食用。
运行结果:
第五步:实现单例的支持
用户需要在beanDefinition中告诉我们这个Bean是单例
我为什么贴源码贴的这么全啊,这都不能把各位靓仔骗到我的 gayHub上了。
- 在BeanDefinition中加上以下代码,get,set方法没贴
public static enum Scop{
singleton,prototype;
}
//默认单例
private Scop scop = Scop.singleton;
第六步:增加单例的处理
DefaultBeanFactory 增加单例的处理
//保存单例实例,因为不知道是什么类,所以v为Object
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
此时我们的getBean()改为:
@Override
public Object getBean(String beanName) throws Throwable {
//1.如何创建这个名字的 bean实例
//2.需要知道用户的Bean定义信息----》首先提供一个功能让用户可以给出他的bean定义信息
//3.单例 第一次获取,创建实例,后续获取前面创建的实例===》如何持有当前的单例实例===》使用map
//判断是否是单例的bean
BeanDefinition bd = this.beanDefinitionMap.get(beanName);
if (null == bd){
throw new IllegalArgumentException(beanName+"bean 不存在!");
}
Object bean = null;
if (bd.getScop()==BeanDefinition.Scop.singleton){
bean = this.singletonObjects.get(beanName);
if (null == bean){
//需要创建bean单例
bean = createBeanInstance(beanName);
this.singletonObjects.put(beanName,bean);
}
}
else {
bean = createBeanInstance(beanName);
}
return bean;
}
看似牛b,用脑壳一想如果是并发怎么办?怎么保证创建的是单例?
那就再改进,使用 双重检查+锁机制。getBean()又被搞 了:
public Object getBean(String beanName) throws Throwable {
//1.如何创建这个名字的 bean实例
//2.需要知道用户的Bean定义信息----》首先提供一个功能让用户可以给出他的bean定义信息
//3.单例 第一次获取,创建实例,后续获取前面创建的实例===》如何持有当前的单例实例===》使用map
//判断是否是单例的bean
BeanDefinition bd = this.beanDefinitionMap.get(beanName);
if (null == bd){
throw new IllegalArgumentException(beanName+"bean 不存在!");
}
Object bean = null;
if (bd.getScop()==BeanDefinition.Scop.singleton){
bean = this.singletonObjects.get(beanName);
if (null == bean){//第一次检查
//需要创建bean单例
synchronized (this.singletonObjects) { //双重检查+锁机制
bean = this.singletonObjects.get(beanName);//查看此刻有无
if (null == bean) { //第二次检查
bean = createBeanInstance(beanName);
this.singletonObjects.put(beanName, bean);
}
}
}
}
else {//非单例
bean = createBeanInstance(beanName);
}
return bean;
}
猛男搓搓小手开始测试
延伸:单例Bean能否初始时实例化
单例bean能否在容器启动时完成实例化?有没有必要?有什么好处?怎么做?
写在最后:
关于延伸的部分我也有写,但是不知道有没有写出来的必要。写完这些我已经感觉写的臭长了。
博主第一次写文章,修修改改的不尽人意。应该也没多少人看,看的也只有靓仔。滑稽.jpg
不周到之处希望靓仔们海涵之余尽管指正,谢过。
喝水不搞挖井人,在这感谢众生,我爱这个魔幻的世界!(在他们身上学到太多)。
我,将继承挖井之意志。将吾之学,尽皆共享。初步打算是先记录Spring里面涉及的源码,这篇文章只是个开始。
推荐阅读: 手写Spring框架学习Spring原理 之xml(二)
联系方式: