Spring 源码学习过程:

框架源码系列六:Spring源码学习之Spring IOC源码学习

 1. 搞明白IOC能做什么?

   IOC是用为用户创建、管理实例对象的。用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建,从而达到与具体类解耦。 

 2. IOC是怎么做到的,即它的实现步骤是怎么样的?

 框架源码系列六:Spring源码学习之Spring IOC源码学习

2.1 用户配置bean定义

我们使用Spring IOC时有几种方式来配置bean定义呢?

xml的方式:

    <bean id="abean" class="com.study.spring.samples.ABean">
        <constructor-arg type="String" value="abean01"></constructor-arg>
        <constructor-arg ref="cbean"></constructor-arg>
    </bean>
    <bean id="cbean" class="com.study.spring.samples.CBean">
        <constructor-arg type="String" value="cbean01"></constructor-arg>
    </bean>

注解方式:

package com.study.spring.samples;

import com.study.spring.context.config.annotation.Autowired;
import com.study.spring.context.config.annotation.Component;
import com.study.spring.context.config.annotation.Qualifier;
import com.study.spring.context.config.annotation.Value;

@Component(initMethodName = "init", destroyMethodName = "destroy")
public class ABean {

    private String name;

    private CBean cb;

    @Autowired
    private DBean dbean;

    @Autowired
    public ABean(@Value("leesmall") String name, @Qualifier("cbean01") CBean cb) {
        super();
        this.name = name;
        this.cb = cb;
        System.out.println("调用了含有CBean参数的构造方法");
    }

    public ABean(String name, CCBean cb) {
        super();
        this.name = name;
        this.cb = cb;
        System.out.println("调用了含有CCBean参数的构造方法");
    }

    public ABean(CBean cb) {
        super();
        this.cb = cb;
    }
}

Java-based容器配置方式:

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }

}

上面的AppConfig类等价于:

<beans>
    <bean />
</beans>

想了解java容器配置的朋友请看这篇文章:

Spring Java-based容器配置

2.2 IOC容器加载bean定义

用户以上面的三种方式配置bean定义以后,Spring IOC容器怎么来加载用户的bean定义呢,这就需要我们来告诉它了

xml的方式告诉Spring IOC容器怎么加载bean定义:

        //类路径下加载xml配置文件创建bean定义
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");

        //文件系统下加载xml配置文件创建bean定义
        ApplicationContext context1 = new FileSystemXmlApplicationContext("e:/study/application.xml");

        //通用的xml方式加载xml配置文件创建bean定义
        ApplicationContext context3 = new GenericXmlApplicationContext("file:e:/study/application.xml");

注解方式告诉Spring IOC容器怎么加载bean定义

xml方式指定注解要扫描的基础包:

    <beans>
        <context:component-scan base-package="com.study" />
    </beans>

注解方式指定注解要扫描的基础包:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages="com.study")
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }

}

也可在代码中通过API指定注解扫描的基础包:

        // 扫描注解的方式创建bean定义
        ApplicationContext ctx= new AnnotationConfigApplicationContext();
        ctx.scan("com.study");
        ctx.refresh();
        MyService myService = ctx.getBean(MyService.class);

Java-based容器配置告诉Spring IOC容器怎么加载bean定义

使用AnnotationConfigApplicationContext告诉Spring IOC容器怎么加载bean定义配置

跟实例化一个ClassPathXmlApplicationContext时将Spring XML文件用作输入相似,在实例化一个AnnotationConfigApplicationContext时能够使用@Configuration类作为输入。这就等等于Spring容器全然零XML配置:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

AnnotationConfigApplicationContext不局限于仅仅使用@Configuration类。不论什么@Component或JSR-330注解的类都能够作为AnnotationConfigApplicationContext构造器的输入:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

使用无參的构造器实例化AnnotationConfigApplicationContext,然后使用register()方法对容器进行配置。这样的方式在以编程方式构造一个AnnotationConfigApplicationContext时非常实用:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(AppConfig.class, OtherConfig.class);
    ctx.register(AdditionalConfig.class);
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

启用scan(String…​)的组件扫描:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages="com.study")
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }

}

scan方法扫描:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.scan("com.study");
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
}

 二、搞明白Spring IOC的从整体到部分

 框架源码系列六:Spring源码学习之Spring IOC源码学习

IOC整体是由以上几部分组成起来工作的

三、找到Spring IOC入口,先理清楚主干流程,然后再去研究各个流程的细节

 我们从上面的使用示例,很清楚地看到,我们使用Spring  IOC,只需要使用Spring提供的ApplicationContext这个API。ApplicationContext就是IOC容器。ApplicationContext就是Spring IOC的入口,源码的学习就从它开始!

 1. ApplicationContext是什么

 首先来了解ApplicationContext都是什么,即它都有哪些角色、责任。它通过继承很多接口而有很多角色。

ApplicationContext继承的接口(角色)如下:

 框架源码系列六:Spring源码学习之Spring IOC源码学习

每个角色拥有的职责(方法):

 框架源码系列六:Spring源码学习之Spring IOC源码学习

再来了解 ApplicationContext 自己中定义的方法:

 框架源码系列六:Spring源码学习之Spring IOC源码学习

2. Application的子实现

 框架源码系列六:Spring源码学习之Spring IOC源码学习

说明:

从 AbstarctApplicationContext 之后分为两类:xml 配置方式的实现和通用实现。它们的基本使用示例如下:

package com.study.leesmall.spring;

import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

import com.study.leesmall.spring.service.Abean;
import com.study.leesmall.spring.service.CombatService;

//Spring不同的方式创建bean实例使用代码示例
@Configuration
public class TestApplication {

    public static void main(String[] args) {

        //类路径下加载xml配置文件创建bean定义
        ApplicationContext context1 = new ClassPathXmlApplicationContext("application.xml");
        CombatService cs = context1.getBean(CombatService.class);
        cs.doInit();
        cs.combating();

        //文件系统下加载xml配置文件创建bean定义
        ApplicationContext context2 = new FileSystemXmlApplicationContext("e:/study/application.xml");
        cs = context2.getBean(CombatService.class);
        cs.doInit();
        cs.combating();

        //通用的xml方式加载xml配置文件创建bean定义
        ApplicationContext context3 = new GenericXmlApplicationContext("file:e:/study/application.xml");
        cs = context3.getBean(CombatService.class);
        cs.doInit();
        cs.combating();

        // 扫描注解的方式创建bean定义
        ApplicationContext context4 = new AnnotationConfigApplicationContext(TestApplication.class);
        CombatService cs2 = context4.getBean(CombatService.class);
        cs2.combating();
        
        //通用的方式加载xml配置文件或者扫描指定包下的类创建bean定义
        System.out.println("------------------------------------------------------");
        GenericApplicationContext context5 = new GenericApplicationContext();
        new XmlBeanDefinitionReader(context5).loadBeanDefinitions("classpath:application.xml");
        new ClassPathBeanDefinitionScanner(context5).scan("com.study.leesmall.spring.service");
        // 一定要刷新
        context5.refresh();
        cs2 = context5.getBean(CombatService.class);
        cs2.combating();
        Abean ab = context5.getBean(Abean.class);
        ab.doSomething();
    }

    @Bean
    public CombatService getCombatService() {
        return new CombatService(120);
    }
}

接下来,可以打开每个子去了解它们分别加入了什么、实现了什么?

1)ConfigurableApplicationContext 加入了什么:

 框架源码系列六:Spring源码学习之Spring IOC源码学习

说明:

void addApplicationListener(ApplicationListener<?> listener):这个方法添加监听 在这里可以进行发布订阅监听的工作
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor):这个方法可以对bean工厂进行获取前后的AOP增强
void refresh() throws BeansException, IllegalStateException:这个方法是用来刷新IOC容器的,当往IOC容器里面注册了新的Bean定义时,调用这个方法去创建bean实例

2)AbstractApplicationContext里面对前面的接口就开始有具体的实现了,比如addApplicationListener、addBeanFactoryPostProcessor、refresh等等

框架源码系列六:Spring源码学习之Spring IOC源码学习

框架源码系列六:Spring源码学习之Spring IOC源码学习

3)通用的实现GenericApplicationContext

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {

    private final DefaultListableBeanFactory beanFactory;

    @Nullable
    private ResourceLoader resourceLoader;

    private boolean customClassLoader = false;

    private final AtomicBoolean refreshed = new AtomicBoolean();

它实现了BeanDefinitionRegistry接口,该接口定义了bean定义信息的注册行为。即我们可以直接往GenericApplicationContext中注册bean定义。
了解一下BeanDefinitionRegistry中定义的行为:

 框架源码系列六:Spring源码学习之Spring IOC源码学习

都有谁实现了 BeanDefinitionRegistry 接口:

 框架源码系列六:Spring源码学习之Spring IOC源码学习

GenericApplicationContext中持有DefaultListableBeanFactory,GenericApplicationContext的bean定义注册委托给了持有的DefaultListableBeanFactory

 框架源码系列六:Spring源码学习之Spring IOC源码学习

org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(String, BeanDefinition)对应代码:

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        if (existingDefinition != null) {
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
            }
            else if (existingDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (logger.isInfoEnabled()) {
                    logger.info("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            existingDefinition + "] with [" + beanDefinition + "]");
                }
            }
            else if (!beanDefinition.equals(existingDefinition)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                if (logger.isTraceEnabled()) {
                    logger.trace("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            else {
                // Still in startup registration phase
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if (existingDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }
View Code

相关文章: