首先,我们需要明白什么是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的定义信息。

@Override
public BeanDefinition getBeanDefinition(String beanName) throws BeansException {
    // 从缓存中获取BeanDefinition
    BeanDefinition bd = this.beanDefinitionMap.get(beanName);
    if (bd == null) {
        throw new NoSuchBeanDefinitionException(beanName);
    }
    // 返回BeanDefinition
    return bd;
}

上述方法中,beanDefinitionMap是一个ConcurrentHashMap,用来缓存BeanDefinition对象,key为Bean的名称,value为BeanDefinition对象,这个容器是Spring IOC管理Bean的核心,后面的初始化Bean和创建Bean都是基于这个容器进行的,我们可以看到这个方法先从容器中获取BeanDefinition对象,如果获取到就直接返回,如果获取不到就抛出一个NoSuchBeanDefinitionException异常。

创建Bean实例

下面是Bean实例化的主要过程(主要包含了Bean的创建,包括构造函数的调用和依赖注入等逻辑):

protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) {
    // 第一步,根据BeanDefinition获取Bean的class对象
    Class<?> beanClass = resolveBeanClass(mbd, beanName);
​
    // 第二步,校验Bean的依赖关系以及循环依赖,这里省略实现细节
​
    // 第三步,执行BeanDefinition中Scheduler的回调函数,这里省略实现细节
​
    try {
        Object beanInstance;
​
        // 第四步,判断是否启用了工厂方法构造Bean
        if (mbd.getFactoryMethodName() != null) {
            return instantiateUsingFactoryMethod(beanName, mbd, args);
        }
​
        // 第五步,执行构造函数
        Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
        if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
                mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
            return instantiateBean(beanName, mbd, args, ctors);
        }
​
        // 第六步,根据类型查找BeanFactory中是否注册了Bean的实例
        beanInstance = getSingleton(beanName);
        if (beanInstance != null) {
            return beanInstance;
        }
​
        // 第七步,通过BeanWrapper来修改Bean的属性
        BeanWrapper bw = new BeanWrapperImpl();
        bw.setConversionService(getConversionService());
        initBeanWrapper(bw);
​
        // 第八步,为Bean属性设置值
        applyPropertyValues(beanName, mbd, bw, mbd.getPropertyValues());
​
        // 第九步,在设置完Bean属性之后,校验Bean的合法性等信息,这里省略实现细节
​
        // 第十步,执行Bean的init方法,将Bean初始化完成
        beanInstance = initializeBean(beanName, beanInstance, mbd);
        if (mbd.isSingleton()) {
            // 添加原生的单例Bean
            addSingleton(beanName, beanInstance);
        }
        return beanInstance;
    }
    catch (Throwable e) {
        throw new BeanCreationException(beanName, "Instantiation of bean failed", e);
    }
}
​

上述代码就是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的依赖来帮助我们解析配置文件内的内容

<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.6.1</version>
</dependency>

定义Xml

定义⼀个外部的XML,⽤于声明Bean: applicationContext.xml

<beans>
    <bean id="userDao" class="top.serms.dao.impl.UserDaoImpl"></bean>
    <bean id="userService" class="top.serms.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
    </bean>
</beans>

编写BeanDefinition

这个类表示一个bean的定义,包含了两个属性:id和className。其中,id是bean的唯一标识符,className是该bean对应的类名property就是属性,因我们的property属性可能有多个,并且对象类型也存在不同,所以这边直接采用List 集合的方式。

public class BeanDefinition {
    private String id;
    private String className;
​
    private List propertyValues = new ArrayList();
​
    public BeanDefinition(String id, String className) {
        this.id = id;
        this.className = className;
    }
​
    public BeanDefinition() {
    }
​
    public String getId() {
        return id;
    }
​
    public void setId(String id) {
        this.id = id;
    }
​
    public String getClassName() {
        return className;
    }
​
    public void setClassName(String className) {
        this.className = className;
    }
​
    public List getPropertyValues() {
        return propertyValues;
    }
​
    public void setPropertyValues(List propertyValues) {
        this.propertyValues = propertyValues;
    }
}

编写Bean工厂接口

BeanFactory接口是一个定义了创建和获取bean对象的机制的接口。

在Java中,我们可以把bean想象成应用程序中的各种对象,例如服务、工具、数据对象等等。而BeanFactory就像是一个工厂,负责根据我们的需求来创建和提供这些对象。

下面是BeanFactory接口中定义的方法:

  1. getBean(String beanName):根据指定的beanName获取对应的bean对象。通过调用这个方法,我们可以根据bean的名称来获取具体的对象实例。

  2. containsBean(String name):检查是否存在指定名称的bean。通过调用这个方法,我们可以判断某个特定名称的bean是否已经被创建和注册。

  3. registerBean(String beanName, Object obj):手动注册一个bean。通过调用这个方法,我们可以将一个对象注册为bean,并指定它的名称。

在下述代码中,我们创建了一个名为SimpleBeanFactory的类,它是BeanFactory接口的一个简单实现。

SimpleBeanFactory类继承了DefaultSingletonBeanRegistry类,这个类实现了SingletonBeanRegistry接口,提供了单例bean的管理功能。

在后面的SimpleBeanFactory类中,我们将要重写getBean方法。当我们调用getBean方法时,它会首先检查单例bean的管理器,即DefaultSingletonBeanRegistry,看看是否存在指定名称的bean。如果存在,则返回对应的单例bean实例;如果不存在,则抛出异常。

这样,通过SimpleBeanFactory创建的bean默认是单例的,因为它继承了单例bean管理的功能。

public interface BeanFactory {
​
    /*获取bean*/
    Object getBean(String beanName) throws BeansException;
​
    /*判断包含Bean*/
    boolean containsBean(String name);
​
    /*注册Bean*/
    void registerBean(String beanName, Object obj);
}

创建SingletonBeanRegistry接口

SingletonBeanRegistry接口用于管理单例bean的注册和获取。

首先,让我们了解一下什么是单例bean。在Java中,单例bean是指只有一个实例存在的对象。在整个应用程序中,无论我们从何处获取该bean,都会得到同一个实例。

接下来,我们来解释SingletonBeanRegistry接口的方法:

  1. registerSingleton(String beanName, Object singletonObject):这个方法用于注册单例bean。我们可以通过指定的beanName将一个对象注册为单例bean。注册后,我们可以使用beanName来获取该单例bean的实例。

  2. getSingleton(String beanName):这个方法用于获取指定beanName对应的单例bean实例。如果存在该单例bean,则返回其实例;如果不存在,则返回null

  3. containsSingleton(String beanName):这个方法用于检查是否存在指定名称的单例bean。如果存在,返回true;如果不存在,返回false

  4. getAllSingletons():这个方法用于获取所有已注册的单例bean的映射关系。返回一个Map,其中键是单例bean的名称,值是对应的单例bean实例。

SingletonBeanRegistry接口主要用于管理单例Bean的注册和获取。它定义了两个方法:

  1. registerSingleton(String name, Object singleton): 将单例Bean注册到单例Bean容器中,其中name是该Bean的名称,singleton是该Bean的实例。

  2. getSingleton(String name): 获取指定名称的单例Bean实例。

  3. 在下面代码中,我们实现了SingletonBeanRegistry接口的一个默认实现类DefaultSingletonBeanRegistry

    DefaultSingletonBeanRegistry类维护了一个singletons字典,用于存储单例bean的名称和对应的实例。

    registerSingleton方法中,我们使用synchronized关键字来确保在多线程环境下对单例bean的安全管理。我们将指定的singletonObject对象与beanName关联,并将其存储在singletons字典中。

    getSingleton方法根据beanNamesingletons字典中获取相应的单例bean实例。

    containsSingleton方法用于检查singletons字典中是否存在指定名称的单例bean。

    getAllSingletons方法返回singletons字典,其中包含了所有已注册的单例bean的名称和实例。

此接口主要目的就是在整个Bean生命周期中只创建一次Bean,使用SingletonBeanRegistry可以方便地管理和获取单例Bean,保证每个单例Bean在整个应用中只存在一份,避免了重复创建和浪费资源的问题。同时,它还可以提供单例Bean之间的依赖注入和解耦的方式,使系统更加灵活、可维护和可扩展。

public interface SingletonBeanRegistry {
​
    /*注册Bean*/
    void registerSingleton(String beanName, Object singletonObject);
​
    /*通过名称获取对象*/
    Object getSingleton(String beanName);
​
    /*判断是否包含Bean*/
    boolean containsSingleton(String beanName);
​
    String[] getSingletonNames();
            
    Object createBean(BeanDefinition beanDefinition, List beanDefinitions);
}
​

实现单例接口

创建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集合进行缓存,以实现高效的访问。

class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
​
    // 存储单例Bean的ConcurrentHashMap
    private final Map<String, Object> singletons = new ConcurrentHashMap<>(256);
​
    // 存储单例Bean的名称的ConcurrentHashSet
    private final Set<String> beanNames = Collections.synchronizedSet(new HashSet<>());
​
    // 注册单例Bean
    @Override
    public void registerSingleton(String beanName, Object singletonObject) {
        synchronized (singletons) {
            System.out.println(beanName + " : " + singletonObject);
            if (singletons.containsKey(beanName)) {
                throw new IllegalStateException("Could not register object [" + singletonObject + "] under bean name '" + beanName + "': there is already object [" + singletons.get(beanName) + "] bound");
            }
            singletons.put(beanName, singletonObject);
            beanNames.add(beanName);
        }
    }
​
    // 获取Bean的名称
    @Override
    public Object getSingleton(String beanName) {
        return singletons.get(beanName);
    }
​
    //判断是否包含单例bean
    @Override
    public boolean containsSingleton(String beanName) {
        return singletons.containsKey(beanName);
    }
​
    //获取所有单例bean的名称
    @Override
    public String[] getSingletonNames() {
        return beanNames.toArray(new String[0]);
    }
​
    @Override
    public Object createBean(BeanDefinition beanDefinition, List beanDefinitions) {
        Object bean = null;
        try {
            bean = Class.forName(beanDefinition.getClassName()).newInstance();
            List<PropertyValue> propertyValues = beanDefinition.getPropertyValues();
            for (PropertyValue propertyValue : propertyValues) {
                Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
                Object dependencyBean = singletons.get(propertyValue.getValue());
                if (dependencyBean == null) {
                    dependencyBean = createBean(getBeanDefinitionById((String) propertyValue.getValue(), beanDefinitions), (List) singletons);
                    singletons.put((String) propertyValue.getValue(), dependencyBean);
                }
                declaredField.setAccessible(true);
                declaredField.set(bean, dependencyBean);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }
​
    private BeanDefinition getBeanDefinitionById(String id, List<BeanDefinition> beanDefinitions) {
        for (BeanDefinition beanDefinition : beanDefinitions) {
            if (beanDefinition.getId().equals(id)) {
                return beanDefinition;
            }
        }
        return null;
    }
}   

创建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方法。

public class ClassPathXmlApplicationContext extends DefaultSingletonBeanRegistry implements BeanFactory {

    // 存储Bean的定义信息
    private List<BeanDefinition> beanDefinitions = new ArrayList<>();

    // 构造方法,读取xml文件并构建BeanDefinition对象,实例化Bean对象
    public ClassPathXmlApplicationContext(String xmlFileName) {
        readXml(xmlFileName);
        instanceBeans();
    }

    // 读取xml文件,并将读取到的配置信息构建成BeanDefinition对象,并将其加入到beanDefinitions中
    private void readXml(String xmlFileName) {
        try {
            SAXReader reader = new SAXReader();
            Document document = reader.read(ClassPathXmlApplicationContext2.class.getClassLoader().getResourceAsStream(xmlFileName));
            Element rootElement = document.getRootElement();
            List<Element> beanElements = rootElement.elements("bean");
            for (Element beanElement : beanElements) {
                String id = beanElement.attributeValue("id");
                String className = beanElement.attributeValue("class");
                BeanDefinition beanDefinition = new BeanDefinition(id, className);
                List<Element> propertyElements = beanElement.elements("property");
                for (Element propertyElement : propertyElements) {
                    String name = propertyElement.attributeValue("name");
                    String ref = propertyElement.attributeValue("ref");
                    PropertyValue propertyValue = new PropertyValue(name, ref);
                    System.out.println(propertyValue);
                    beanDefinition.getPropertyValues().add(propertyValue);
                }
                beanDefinitions.add(beanDefinition);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }


    // 使用反射来创建Bean对象,并将其存储到singletons中
    private void instanceBeans() {
        for (BeanDefinition beanDefinition : beanDefinitions) {
            try {
//                Object bean = Class.forName(beanDefinition.getClassName()).newInstance();
                Object bean = createBean(beanDefinition, beanDefinitions);
                registerSingleton(beanDefinition.getId(), bean);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 获取Bean对象
    @Override
    public Object getBean(String beanName) throws BeansException {
        Object bean = getSingleton(beanName);
        if (bean == null) {
//            throw new BeansException("Bean is not defined: " + beanName);
        }
        return bean;
    }

    // 判断是否包含Bean对象
    @Override
    public boolean containsBean(String name) {
        return containsSingleton(name);
    }

    // 注册Bean对象
    @Override
    public void registerBean(String beanName, Object obj) {
        registerSingleton(beanName, obj);
    }
}

创建工厂实现类

接下来我们对Bean工厂做一个简单的实现,基于 Map 存储 Bean 定义和 Bean 实例的容器。它实现了 BeanFactory 接口,可以通过 getBean 方法获取 Bean 的实例,并且实现了 SingletonBeanRegistry 接口,可以注册和存储 Bean 的单例实例。

SimpleBeanFactory 主要用于演示 Spring IoC 容器的基本原理和实现方式,对于真正的应用场景来说,它的功能和扩展性都远远不足。在实际项目中,我们通常会使用更为强大和灵活的容器,如 Spring 容器或其他开源容器,这些容器提供了很多高级特性,如 AOP、事务管理、Web 开发支持等,可以大大简化应用程序的开发和维护。

public class SimpleBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {
    @Override
    public void registerSingleton(String beanName, Object singletonObject) {
        super.registerSingleton(beanName, singletonObject);
    }

    @Override
    public Object getBean(String beanName) throws BeansException {
        Object bean = getSingleton(beanName);
        if (bean == null) {
//            throw new BeansException("Bean is not defined: " + beanName);
        }
        return bean;
    }

    @Override
    public boolean containsBean(String name) {
        return containsSingleton(name);
    }

    @Override
    public void registerBean(String beanName, Object obj) {
        registerSingleton(beanName, obj);
    }
}

测试自定义IOC容器

创建Dao层以及Service层

在UserDao中创建接口,Dao实现类中实现具体的业务,Service在进行调用,由于代码过于简单就不做展示了。

创建Test测试类

创建容器对象传入applicationContext.xml文件。。。。。。。省略。。。。。

image-20230516152302642

最后我们看运行结果

可以看到Serivce对象和Dao都以及分别进行了创建,实现了单例对象,并也实现了DI注入,如上就是所有代码过程,在整个SpringIOC的源码中实现的功能,远不止上述这些,上述只是简单的实现了一个IOC容器。如有写的不对的地方,还请指点!

以上就是 手撸SpringIOC 的全部内容,看完如果对你有帮助,感谢赞助支持!。

❤️Sponsor

您的支持是我不断前进的动力,如果您恰巧财力雄厚,又感觉本文对您有所帮助的话,可以考虑打赏一下本文,用以维持本博客的运营费用,拒绝白嫖,从你我做起!🥰🥰🥰

支付宝

微信