Spring

Spring | 0.9 IoC

2020-05-12  本文已影响0人  不一样的卡梅利多

IoC Beans部分

核心需求 :依据配置信息创建对象和将对象连接起来。

核心类

Ioc 容器类定义.png

各个类职责:
BeanFactory:获取bean,是bean 容器,接口
ListableBeanFactory: 查询beanFactory 注册的bean信息,接口
AbstractBeanFactory:依据bean 注册信息,创建bean 和完成bean 依赖关系组装。核心实现
ListableBeanFactoryImpl: 完成bean 配置信息注册的具体实现
XmlBeanFactory : 从配置文件中完成bean配置(定义)信息的注册,还包括xml 文件具体解析

使用

配置xml 文件并从容器中获取bean。

BeanFactory  factory =new XMLBeanFactory(xmlfile)
Object bean =factory.getBean(“beanName”)

实现

1、new XMLBeanFactory(xmlfile) 流程

    public XmlBeanFactory(Document doc, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        loadBeanDefinitions(doc);
    }

loadBeanDefinitions :
1、完成xml 文件解析
2、将xml 配置文件解析成AbstractBeanDefinition 对象
3、将AbstractBeanDefinition 注册到容器中

XMLBeanFactory 职责是比较单一的,完成xml文件读取,然后将xml 文件转换成AbstractBeanDefinition 对象,并且将对象注册到容器中。注册的具体实现由父类完成。

2、factory.getBean(“beanName”) 实现在AbstractBeanFactory 中
getBeanInternal

private Object getBeanInternal(String name, Map newlyCreatedBeans) {
AbstractBeanDefinition bd = getBeanDefinition(transformedBeanName(name));
return bd.isSingleton() ? 
getSharedInstance(name, newlyCreatedBeans) :
 createBean(name, newlyCreatedBeans);   
}

getSharedInstance:
如果bean实现FactoryBean 接口,生成bean使用自定义工厂方法创建。会创建二次bean,1次创建FactoryBean ,2 次依据FactoryBean 在创建自定bean,这是后续集成AOP 的一个基础

createBean(name, newlyCreatedBeans);
if (beanInstance instanceof FactoryBean) {
FactoryBean factory = (FactoryBean) beanInstance;
beanInstance = factory.getObject();

// 设置bean 的属性值,可能是配置的静态值,有可能是引用值
if (factory.getPropertyValues() != null) {
                    logger.debug("Applying pass-through properties to bean with name '" + name + "'");
                    new BeanWrapperImpl(beanInstance).setPropertyValues(factory.getPropertyValues());
                }
}

核心逻辑createBean

private Object createBean(String name, Map newlyCreatedBeans) throws BeansException {
        if (newlyCreatedBeans == null) {
            newlyCreatedBeans = new HashMap();
        }
        Object bean = getBeanWrapperForNewInstance(name, newlyCreatedBeans).getWrappedInstance();
        callLifecycleMethodsIfNecessary(bean, name);
        return bean;
    }
private void callLifecycleMethodsIfNecessary(Object bean, String name) throws BeansException {
        if (bean instanceof InitializingBean) { 
                ((InitializingBean) bean).afterPropertiesSet();
        }
        if (bean instanceof Lifecycle) {
                ((Lifecycle) bean).setBeanFactory(this);
            
        }
    }

1、创建bean 并且给设置属性值
2、callLifecycleMethodsIfNecessary 扩展点。bean 实现了xxx 接口,会在bean 创建好后调用 bean 的扩展接口。

private BeanWrapper getBeanWrapperForNewInstance(String name, Map newlyCreatedBeans) throws BeansException {
               // 获取bean 定义
        AbstractBeanDefinition bd = getBeanDefinition(name);

        BeanWrapper instanceWrapper = null;
        if (bd instanceof RootBeanDefinition) {
        RootBeanDefinition rbd = (RootBeanDefinition) bd;           instanceWrapper=rbd.getBeanWrapperForNewInstance();
        }
        else if (bd instanceof ChildBeanDefinition) {
            ChildBeanDefinition ibd = (ChildBeanDefinition) bd;
            instanceWrapper = getBeanWrapperForNewInstance(ibd.getParentName(), newlyCreatedBeans);
        }
    
        newlyCreatedBeans.put(name, instanceWrapper.getWrappedInstance());
        PropertyValues pvs = bd.getPropertyValues();
// 设置bean 的属性以及依赖值,引用值处理resolveValueIfNecessary()
        applyPropertyValues(instanceWrapper, pvs, name, newlyCreatedBeans);
        return instanceWrapper;
    }

小结:
可以看到第一版的bean 容器实现还是很简单的,但是实现和核心的功能,后续的版本都是以此为基础。类结构和名称(BeanFactory)很多一直延续至今。也可以看到最初的设计是非常稳定的。

IoC Context 部分

beans 实现了核心的需求,从配置到获取bean。但是我们作为应用开发,还需要集成应用程序部分。用 Context 来装饰BeanFactory ,对BeanFactroy 的功能加强 。从这个设计思路,我们还可以看到后续的Spring boot 项目也是类似的实现方式。用Application 来装饰 Context,加强Context 的功能。

核心类

Context 类.png

1、AbstractApplicationContext 是连接应用与bean 的核心实现类,并且设计了很多抽象方法供子类自定义实现。比如如何获取是创建BeanFactroy 留给子类实现。
2、AbstractXmlApplicationContext :负责创建XMLFactroy
3、FileSystemXmlApplicationContext :主要负责配置文件位置定义。

使用

自动完成bean 的创建工作,不用beanFactory.get()

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(xmlfile)

源码

new ClassPathXmlApplicationContext(xmlfile)
1、指定配置文件在classpath 下面。按照输入流读取到xml 配置文件

protected InputStream getResourceByPath(String path) throws IOException {
        if (!path.startsWith("/")) {
            // always use root,
            // as loading relative to this class' package doesn't make sense
            path = "/" + path;
        }
        return getClass().getResourceAsStream(path);
    }

2、创建context ,调用 核心 refresh() 方法

public FileSystemXmlApplicationContext(String[] locations)
        throws ApplicationContextException, IOException {
                ApplicationContext parent=createParentContext(parentLocations);
            setParent(parent);
        
        // initialize this context
        refresh();
    }

3、AbstractApplicationContext#refresh

    public final void refresh() throws ApplicationContextException {
        refreshBeanFactory();
        configureAllManagedObjects();
        refreshListeners();
        loadOptions();
        onRefresh();
        publishEvent(new ContextRefreshedEvent(this));
    }

refreshBeanFactory : 留给子类实现,AbstractXmlApplicationContext 默认实现为创建XMLBeanFactory。
configureAllManagedObjects :默认创建所容器中所有bean,所以客户端在使用前,bean 都已经创建好了。

private void configureAllManagedObjects() throws ApplicationContextException {
    
        String[] beanNames = getBeanDefinitionNames();

        for (int i = 0; i < beanNames.length; i++) {
            String beanName = beanNames[i];
            if (isSingleton(beanName)) {
                try {
                    Object bean = getBeanFactory().getBean(beanName);
                    configureManagedObject(bean);
        
            }
        }
    }

bean 扩展点,如果实现了ApplicationContextAware 接口,创建bean 完成后,会调用该方法

protected void configureManagedObject(Object o) throws ApplicationContextException {
        if (o instanceof ApplicationContextAware) {
            aca.setApplicationContext(this);
        }
    }

refreshListeners :bean 如果实现了ApplicationListener 接口,将是一个特殊的bean,会接受到ApplicationEvent 事件,也就是最后一步
publishEvent(ContextRefreshedEvent)

    private void refreshListeners() throws ApplicationContextException {
        String[] listenerNames = getBeanDefinitionNames(ApplicationListener.class);
        for (int i = 0; i < listenerNames.length; i++) {
            String beanName = listenerNames[i];
            try {
                Object bean = getBeanFactory().getBean(beanName);
                ApplicationListener l = (ApplicationListener) bean;
                addListener(l);
        }
    }

onRefresh : 留给子类实现,比如在后续版本中,onRefresh 改方法实现了启动tomcat 服务器。
publishEvent: 给实现ApplicationListener 接口的bean 发布广播通知。

小结:
Context 集成 BeanFactory ,并且默认完成所有bean 的创建工作 ,还提供了应用的一些扩展点 比如 ApplicationListener,ApplicationContextAware。Spring 的扩展方式1:通过实现父类预留的接口,2、通过实现预定的接口。这两种方式都有代码入侵性,不是很友好。但是这种扩展方式的思想一种沿用至今,虽然目前的spring 版本代码有较大的改动,但是核心的部分还是未改变。通过最基础的版本代码阅读,更容易理解核心实现和核心思路。在实现自定义功能时候,我们容易知道什么地方有扩展点。

我们使用spring 框架一般是使用ApplicationContext,很少单独使用BeanFactory,所以就重点回顾下ApplicationContext 的执行流程。

  1. ApplicationContext 的创建过程 核心逻辑都实现在AbstractApplicationContext#refresh 中

  2. ApplicationContext 预览了很多抽象方法给子类配置使用,单都是非核心的配置功能,比如
    如何获取BeanFactory,如何制定配置文件位置,如何添加默认实现的bean 注册到BeanFactory中

  3. 重点流程还AbstractApplicationContext#refresh
    1、 refreshBeanFactory:获取并且创建 BeanFactory,创建BeanFactory的同时完成bean 配置的注册
    2、configureAllManagedObjects :实例化BeanFactory 中所有的配置的bean。

2.1 创建bean,更bean 设置属性值
2.2 如果bean实现了FactoryBean ,那么还会以及FactoryBean.getObjecct() 创建bean
2.3 创建bean 完成后 会判断bean 有没有实现InitializingBean,Lifecycle 接口
如果bean 实现了这些接口还会调用((InitializingBean) bean).afterPropertiesSet();
((Lifecycle) bean).setBeanFactory(this);
2.4 如果bean 实现了ApplicationContextAware 接口还会调用setApplicationContext方法
2.1 ~2.3 的扩展接口(FactoryBean,InitializingBean,Lifecycle)是在beans 定义的
2.4 的扩展接口(ApplicationContextAware)是在Context 模块中定义的

  1. refreshListeners :获取BeanFactory 所有实现ApplicationListener 接口的bean 。
  2. onRefresh hook 给子类实现
  3. publishEvent,将Context 完成

Spring 专题

上一篇下一篇

猜你喜欢

热点阅读