05|实现完整的IoC容器:构建工厂体系并添加容器事件

你好,我是郭屹。 前面我们已经实现了IoC的核心部分,骨架已经有了,那怎么让这个IoC丰满起来呢?这就需要实现更多的功能,让我们的IoC更加完备。所以这节课我们将通过建立BeanFactory体系,添加容器事件等一系列操作,进一步完善IoC的功能。

实现一个完整的IoC容器

为了让我们的MiniSpring更加专业一点,也更像Spring一点,我们将实现3个功能点。

  1. 进一步增强扩展性,新增4个接口。
  • ListableBeanFactory

  • ConfigurableBeanFactory

  • ConfigurableListableBeanFactory

  • EnvironmentCapable

  1. 实现DefaultListableBeanFactory,该类就是Spring IoC的引擎。
  2. 改造ApplicationContext。 下面我们就一条条来看。

增强扩展性

首先我们来增强BeanFactory的扩展性,使它具有不同的特性。 我们以前定义的AutowireCapableBeanFactory就是在通用的BeanFactory的基础上添加了Autowired注解特性。比如可以将Factory内部管理的Bean作为一个集合来对待,获取Bean的数量,得到所有Bean的名字,按照某个类型获取Bean列表等等。这个特性就定义在ListableBeanFactory中。

1
2
3
4
5
6
7
public interface ListableBeanFactory extends BeanFactory {
    boolean containsBeanDefinition(String beanName);
    int getBeanDefinitionCount();
    String[] getBeanDefinitionNames();
    String[] getBeanNamesForType(Class<?> type);
    <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException;
}

我们还可以将维护Bean之间的依赖关系以及支持Bean处理器也看作一个独立的特性,这个特性定义在ConfigurableBeanFactory接口中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public interface ConfigurableBeanFactory extends
BeanFactory,SingletonBeanRegistry {
    String SCOPE_SINGLETON = "singleton";
    String SCOPE_PROTOTYPE = "prototype";
    void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
    int getBeanPostProcessorCount();
    void registerDependentBean(String beanName, String dependentBeanName);
    String[] getDependentBeans(String beanName);
    String[] getDependenciesForBean(String beanName);
}

然后还可以集成,用一个ConfigurableListableBeanFactory接口把AutowireCapableBeanFactory、ListableBeanFactory和ConfigurableBeanFactory合并在一起。

1
2
3
4
5
6
package com.minis.beans.factory.config;
import com.minis.beans.factory.ListableBeanFactory;
public interface ConfigurableListableBeanFactory
        extends ListableBeanFactory, AutowireCapableBeanFactory,
ConfigurableBeanFactory {
}

接口隔离原则在Spring框架中的应用

在Java语言的设计中,接口(Interface)是一种代表特性或能力的机制。通过将不同的特性或能力分别抽象成独立的接口,可以实现模块间的低耦合和高内聚。这种设计原则被称为接口隔离原则(Interface Segregation Principle)。在Spring框架中,这一原则得到了广泛的应用。

1. 接口的定义与应用

接口隔离原则的核心在于,不应该强迫任何客户端依赖于它们不使用的方法。在Spring框架中,通过定义多个细粒度的接口,我们可以为BeanFactory和BeanDefinition添加各种处理方法,从而增强其功能。

2. ConfigurableListableBeanFactory与AutowireCapableBeanFactory的关系

由于ConfigurableListableBeanFactory继承了AutowireCapableBeanFactory,我们需要对AutowireCapableBeanFactory的定义进行调整。具体来说,应该将其从类(class)改为接口(interface),以更好地遵循接口隔离原则。

3. 调整AutowireCapableBeanFactory

为了遵循接口隔离原则,我们需要重新设计AutowireCapableBeanFactory,使其成为一个接口,而不是一个类。这样可以更灵活地组合不同的特性,同时减少不必要的依赖。

4. 总结

通过将特性或能力抽象成独立的接口,并在需要时实现这些接口,我们可以实现代码的模块化和可重用性。Spring框架正是通过这种方式,提供了灵活且强大的依赖注入和Bean管理功能。

1
2
3
4
5
6
7
8
9
public interface AutowireCapableBeanFactory  extends BeanFactory{
    int AUTOWIRE_NO = 0;
    int AUTOWIRE_BY_NAME = 1;
    int AUTOWIRE_BY_TYPE = 2;
    Object applyBeanPostProcessorsBeforeInitialization(Object existingBean,
String beanName) throws BeansException;
    Object applyBeanPostProcessorsAfterInitialization(Object existingBean,
String beanName) throws BeansException;
}

新增抽象类AbstractAutowireCapableBeanFactory替代原有的实现类。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public abstract class AbstractAutowireCapableBeanFactory
                        extends AbstractBeanFactory implements
AutowireCapableBeanFactory{
    private final List<BeanPostProcessor> beanPostProcessors = new
ArrayList<BeanPostProcessor>();

    public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        this.beanPostProcessors.remove(beanPostProcessor);
        this.beanPostProcessors.add(beanPostProcessor);
    }
    public int getBeanPostProcessorCount() {
        return this.beanPostProcessors.size();
    }
    public List<BeanPostProcessor> getBeanPostProcessors() {
        return this.beanPostProcessors;
    }
    public Object applyBeanPostProcessorsBeforeInitialization(Object
existingBean, String beanName)
            throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            beanProcessor.setBeanFactory(this);
            result = beanProcessor.postProcessBeforeInitialization(result,
beanName);
            if (result == null) {
                return result;
            }
        }
        return result;
    }
    public Object applyBeanPostProcessorsAfterInitialization(Object
existingBean, String beanName)
            throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            result = beanProcessor.postProcessAfterInitialization(result,
beanName);
            if (result == null) {
                return result;
            }
        }
        return result;
    }
}

上述代码与之前的实现类一致,在此不多赘述。

环境

除了扩充BeanFactory体系,我们还打算给容器增加一些环境因素,使一些容器整体所需要的属性有个地方存储访问。

在core目录下新建env目录,增加PropertyResolver.java、EnvironmentCapable.java、Environment.java三个接口类。EnvironmentCapable主要用于获取Environment实例,Environment则继承PropertyResoulver接口,用于获取属性。所有的ApplicationContext都实现了Environment接口。

Environment.java 接口

1
2
3
4
5
public interface Environment extends PropertyResolver {
    String[] getActiveProfiles();
    String[] getDefaultProfiles();
    boolean acceptsProfiles(String... profiles);
}

EnvironmentCapable.java 接口

1
2
3
public interface EnvironmentCapable {
    Environment getEnvironment();
}

PropertyResolver.java 接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public interface PropertyResolver {
    boolean containsProperty(String key);
    String getProperty(String key);
    String getProperty(String key, String defaultValue);
    <T> T getProperty(String key, Class<T> targetType);
    <T> T getProperty(String key, Class<T> targetType, T defaultValue);
    <T> Class<T> getPropertyAsClass(String key, Class<T> targetType);
    String getRequiredProperty(String key) throws IllegalStateException;
    <T> T getRequiredProperty(String key, Class<T> targetType) throws
IllegalStateException;
    String resolvePlaceholders(String text);
    String resolveRequiredPlaceholders(String text) throws
IllegalArgumentException;
}

IoC引擎

接下来我们看看IoC引擎——DefaultListableBeanFactory的实现。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class DefaultListableBeanFactory extends
AbstractAutowireCapableBeanFactory
                    implements ConfigurableListableBeanFactory{
    public int getBeanDefinitionCount() {
        return this.beanDefinitionMap.size();
    }
    public String[] getBeanDefinitionNames() {
        return (String[]) this.beanDefinitionNames.toArray();
    }
    public String[] getBeanNamesForType(Class<?> type) {
        List<String> result = new ArrayList<>();
        for (String beanName : this.beanDefinitionNames) {
            boolean matchFound = false;
            BeanDefinition mbd = this.getBeanDefinition(beanName);
            Class<?> classToMatch = mbd.getClass();
            if (type.isAssignableFrom(classToMatch)) {
                matchFound = true;
            }
            else {
                matchFound = false;
            }
            if (matchFound) {
                result.add(beanName);
            }
        }
        return (String[]) result.toArray();
    }
    @SuppressWarnings("unchecked")
    @Override
    public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException
{
        String[] beanNames = getBeanNamesForType(type);
        Map<String, T> result = new LinkedHashMap<>(beanNames.length);
        for (String beanName : beanNames) {
            Object beanInstance = getBean(beanName);
            result.put(beanName, (T) beanInstance);
        }
        return result;
    }
}

从上述代码中,似乎看不出这个类是如何成为IoC引擎的,因为它的实现都是很简单地获取各种属性的方法。它成为引擎的秘诀在于 它继承了其他BeanFactory类来实现Bean的创建管理功能。从代码可以看出它继承了AbstractAutowireCapableBeanFactory并实现了ConfigurableListableBeanFactory接口。
图片
可以看出,我们的MiniSpring与Spring框架设计得几乎一模一样。当然,这是我们有意为之,我们手写MiniSpring就是为了深入理解Spring。当ClassPathXmlApplicationContext这个Spring核心启动类运行时,注入了DefaultListableBeanFactory,为整个Spring框架做了默认实现,这样就完成了框架内部的逻辑闭环。接着我们来完善事件的发布与监听,包括ApplicationEvent、ApplicationListener、ApplicationEventPublisher以及ContextRefreshEvent,事件一经发布就能让监听者监听到。

1
2
3
4
5
6
7
8
public class ApplicationEvent extends EventObject {
    private static final long serialVersionUID = 1L;
    protected String msg = null;
    public ApplicationEvent(Object arg0) {
        super(arg0);
        this.msg = arg0.toString();
    }
}

ApplicationListener

1
2
3
4
5
public class ApplicationListener implements EventListener {
    void onApplicationEvent(ApplicationEvent event) {
        System.out.println(event.toString());
    }
}

ContextRefreshEvent

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class ContextRefreshEvent extends ApplicationEvent{
    private static final long serialVersionUID = 1L;
    public ContextRefreshEvent(Object arg0) {
        super(arg0);
    }

    public String toString() {
        return this.msg;
    }
}

ApplicationEventPublisher

1
2
3
4
public interface ApplicationEventPublisher {
    void publishEvent(ApplicationEvent event);
    void addApplicationListener(ApplicationListener listener);
}

可以看出,框架的EventPublisher,本质是对JDK事件类的封装。接口已经定义好了,接下来我们实现一个最简单的事件发布者SimpleApplicationEventPublisher。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class SimpleApplicationEventPublisher implements
ApplicationEventPublisher{
    List<ApplicationListener> listeners = new ArrayList<>();
    @Override
    public void publishEvent(ApplicationEvent event) {
        for (ApplicationListener listener : listeners) {
            listener.onApplicationEvent(event);
        }
    }
    @Override
    public void addApplicationListener(ApplicationListener listener) {
        this.listeners.add(listener);
    }
}

这个事件发布监听机制就可以为后面ApplicationContext的使用服务了。

完整的ApplicationContext

最后,我们来完善ApplicationContext,并把它作为公共接口,所有的上下文都实现自

ApplicationContext,支持上下文环境和事件发布。

1
2
3
4
public interface ApplicationContext
        extends EnvironmentCapable, ListableBeanFactory, ConfigurableBeanFactory,
ApplicationEventPublisher{
}

我们计划做4件事。

  1. 抽取ApplicationContext接口,实现更多有关上下文的内容

  2. 支持事件的发布与监听

  3. 新增AbstractApplicationContext,规范刷新上下文refresh方法的步骤规范,且将每一步骤进行抽象,提供默认实现类,同时支持自定义

  4. 完成刷新之后发布事件。 首先我们来增加ApplicationContext接口的内容,丰富它的功能。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public interface ApplicationContext
        extends EnvironmentCapable, ListableBeanFactory,
ConfigurableBeanFactory, ApplicationEventPublisher{
    String getApplicationName();
    long getStartupDate();
    ConfigurableListableBeanFactory getBeanFactory() throws
IllegalStateException;
    void setEnvironment(Environment environment);
    Environment getEnvironment();
    void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
    void refresh() throws BeansException, IllegalStateException;
    void close();
    boolean isActive();
}

我们首先定义一个接口,然后通过一个抽象类来搭建框架的基础结构,最后提供一个具体的实现类来进行默认的实现。Spring的这种interface-abstract-class模式极大地增强了框架的扩展性。 接下来,我们将重点分析AbstractApplicationContext的实现。虽然目前我们仅支持从XML中读取配置以获取应用上下文信息,但Spring框架实际上支持多种方式。然而,无论哪种方式,其核心都是对应用上下文的处理。因此,我们需要抽象出ApplicationContext的公共部分。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public abstract class AbstractApplicationContext implements ApplicationContext{
    private Environment environment;
    private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new
ArrayList<>();
    private long startupDate;
    private final AtomicBoolean active = new AtomicBoolean();
    private final AtomicBoolean closed = new AtomicBoolean();
    private ApplicationEventPublisher applicationEventPublisher;
    @Override
    public Object getBean(String beanName) throws BeansException {
        return getBeanFactory().getBean(beanName);
    }
    public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() {
        return this.beanFactoryPostProcessors;
    }
    public void refresh() throws BeansException, IllegalStateException {
        postProcessBeanFactory(getBeanFactory());
        registerBeanPostProcessors(getBeanFactory());
        initApplicationEventPublisher();
        onRefresh();
        registerListeners();
        finishRefresh();
    }
    abstract void registerListeners();
    abstract void initApplicationEventPublisher();
    abstract void postProcessBeanFactory(ConfigurableListableBeanFactory
beanFactory);
    abstract void registerBeanPostProcessors(ConfigurableListableBeanFactory
beanFactory);
    abstract void onRefresh();
    abstract void finishRefresh();
    @Override
    public String getApplicationName() {
        return "";
    }
    @Override
    public long getStartupDate() {
        return this.startupDate;
    }
    @Override
    public abstract ConfigurableListableBeanFactory getBeanFactory() throws
IllegalStateException;
    @Override
    public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor
postProcessor) {
        this.beanFactoryPostProcessors.add(postProcessor);
    }
    @Override
    public void close() {
    }
    @Override
    public boolean isActive(){
        return true;
    }
    //省略包装beanfactory的方法
}

上面这段代码的核心是refresh()方法的定义,而这个方法又由下面这几个步骤组成。

1
2
3
4
5
6
7
8
    abstract void registerListeners();
    abstract void initApplicationEventPublisher();
    abstract void postProcessBeanFactory(ConfigurableListableBeanFactory
beanFactory);
    abstract void registerBeanPostProcessors(ConfigurableListableBeanFactory
beanFactory);
    abstract void onRefresh();
    abstract void finishRefresh();

理解Spring框架中的几个关键步骤

在Spring框架中,理解以下几个关键步骤对于自定义扩展和应用上下文管理至关重要:

  1. 注册监听者:这是Spring应用的第一步,通过注册监听者,我们可以在应用启动或关闭时执行特定的操作。

  2. 初始化事件发布者:事件发布者负责发布事件,使得监听者能够响应这些事件。初始化这一组件是确保事件能够正确传递的关键。

  3. 处理Bean:在Spring中,Bean是构成应用的核心组件。处理Bean涉及到创建、配置和管理这些Bean的生命周期。

  4. Bean状态操作:对Bean的状态进行操作,比如设置Bean的属性或者调用初始化和销毁方法,是确保Bean正确行为的重要步骤。

  5. 应用上下文刷新:完成Bean的初始化后,需要刷新应用上下文,这会触发Bean的生命周期事件,并使Bean准备好被使用。

  6. 完成刷新后的自定义操作:在应用上下文刷新后,可以执行一些自定义操作,比如执行特定的初始化代码或者验证配置。 这些步骤都通过abstract修饰,允许用户根据需要自定义处理,从而增强了框架的扩展性。

ClassPathXmlApplicationContext的实现

以ClassPathXmlApplicationContext为例,我们可以按照上述步骤实现Spring应用:

  • 注册监听者:首先,我们需要注册监听者以响应应用的启动和关闭事件。

  • 初始化事件发布者:接着,初始化事件发布者,确保事件能够被正确发布和监听。

  • 处理Bean:然后,处理Bean,包括创建和配置Bean。

  • Bean状态操作:对Bean的状态进行必要的操作,以确保它们能够按照预期工作。

  • 应用上下文刷新:完成Bean的初始化后,刷新应用上下文,触发Bean的生命周期事件。

  • 完成刷新后的自定义操作:最后,在应用上下文刷新后执行自定义操作,完成应用的初始化。 通过这样的步骤,ClassPathXmlApplicationContext能够有效地管理和使用Spring中的Bean。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class ClassPathXmlApplicationContext extends AbstractApplicationContext{
    DefaultListableBeanFactory beanFactory;
    private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new
ArrayList<>();
    public ClassPathXmlApplicationContext(String fileName) {
        this(fileName, true);
    }
    public ClassPathXmlApplicationContext(String fileName, boolean isRefresh) {
        Resource resource = new ClassPathXmlResource(fileName);
        DefaultListableBeanFactory beanFactory = new
DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new
XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions(resource);
        this.beanFactory = beanFactory;
        if (isRefresh) {
            try {
                refresh();
            }
       }
    }
    @Override
    void registerListeners() {
        ApplicationListener listener = new ApplicationListener();
        this.getApplicationEventPublisher().addApplicationListener(listener);
    }
    @Override
    void initApplicationEventPublisher() {
        ApplicationEventPublisher aep = new SimpleApplicationEventPublisher();
        this.setApplicationEventPublisher(aep);
    }
    @Override
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    }
    @Override
    public void publishEvent(ApplicationEvent event) {
        this.getApplicationEventPublisher().publishEvent(event);
    }
    @Override
    public void addApplicationListener(ApplicationListener listener) {
        this.getApplicationEventPublisher().addApplicationListener(listener);
    }
    public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor
postProcessor) {
        this.beanFactoryPostProcessors.add(postProcessor);
    }
    @Override
    void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory)
{
        this.beanFactory.addBeanPostProcessor(new
AutowiredAnnotationBeanPostProcessor());
    }
    @Override
    void onRefresh() {
        this.beanFactory.refresh();
    }
    @Override
    public ConfigurableListableBeanFactory getBeanFactory() throws
IllegalStateException {
        return this.beanFactory;
    }
    @Override
    void finishRefresh() {
        publishEvent(new ContextRefreshEvent("Context Refreshed..."));
    }
}

aaaaaaaSpring框架通过使用抽象类来解耦组件,提供了一个非常灵活且可扩展的基础。在上述代码示例中,我们观察到ClassPathXmlApplicationContext类通过实现几个关键的抽象方法,高效地融入到了ApplicationContext框架之中。这样的设计模式不仅使得系统更加模块化,还极大地增强了用户根据自身需求进行定制的能力,这也是Spring框架之所以强大并广受欢迎的原因之一。 采用这种方式,Spring能够轻松集成诸如MyBatis、MySQL以及Redis等多种外部框架和技术,从而为开发者提供了构建复杂应用所需的各种工具和解决方案。 虽然本例中的IoC(控制反转)容器相对简单,但它包含了所有必要的组成部分,为我们提供了一个理解Spring框架内部工作原理的理想案例。这种简化的实现帮助开发者更好地把握了如何利用设计模式来提升软件架构的质量与灵活性。 aaaaaaa

小结

在这节课中,我们学习了如何构建一个基础的IoC容器,它具备以下四个核心功能:

  1. 识别Bean定义:容器能够识别配置文件中的Bean定义,并据此创建Bean实例,随后将这些Bean管理起来。

  2. 依赖注入:支持通过配置文件或注解的方式,实现Bean之间的依赖注入。

  3. BeanFactory体系:构建了BeanFactory体系,用于管理Bean的生命周期和依赖关系。

  4. 容器应用上下文与事件发布:容器支持应用上下文和事件发布机制,增强了容器的功能性和灵活性。 这些功能构成了Spring IoC容器的核心。通过使用IoC容器,我们可以将业务逻辑封装在Bean中,并将Bean的创建和管理交给框架来处理,实现了所谓的“控制反转”。这样,应用程序与框架可以协同工作,共同完成程序的运行。 在实现这些概念和特性时,我们模仿了Spring框架的结构和命名,这将有助于我们更平滑地学习和理解Spring框架的代码。通过不断学习和借鉴大师的作品,我们的MiniSpring将逐渐成长为一个强大的框架。

完整源代码

想要查看完整的源代码,请访问以下链接:MiniSpring源代码

课后思考题

学完本节课后,留给你的思考题是:考虑到我们的容器以单例模式管理所有Bean,那么在多线程环境下,我们应该如何确保容器的线程安全呢?欢迎在留言区分享你的想法和解决方案,也可以将这节课分享给需要的朋友。我们下节课再见!