从源码逐步分析IOC的执行流程(含流程图及总结)

Posted 小艾路西里

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从源码逐步分析IOC的执行流程(含流程图及总结)相关的知识,希望对你有一定的参考价值。

1. IOC执行流程图

在这里插入图片描述

2. 从源码分析IOC的执行流程

1.new ClassPathXmlApplicationContext();new AnnotationConfigApplicationContext();

创建 ClassPathXmlApplicationContext或AnnotationConfigApplicationContext,并传入配置文件或包名,得到配置信息或类的信息

xml是通过配置信息读取到类的信息,而注解是通过扫描包的方式获取到类的信息

2. refresh()

refresh()即是上述俩种方式中的都会执行的方法,可以说是ioc的核心

补充:在调用refresh()方法前其实还会调用super()(执行父类的构造器)
在这里插入图片描述

refresh方法内的主要步骤

(1)prepareRefresh(); //启动前的准备工作

设置容器关闭为false,容器激活为true,
在这里插入图片描述
(2)obtainFreshBeanFactory();//准备启动beanFactory

① 调用obtainFreshBeanFactory()方法,获得一个新的空bean工厂
在这里插入图片描述
② 调用prepareBeanFactory()方法,读取xml配置文件中的属性值并封装到BeanDefinition中
在这里插入图片描述
(3) PostProcessBeanFactory(); //用于在bean初始化前,改变bean的定义信息

调用PostProcessBeanFactory()方法,可以在初始化bean前,改变bean的定义信息,该方法为空,所以需要重写
在这里插入图片描述
在这里插入图片描述
(4) 中间有很多执行方法,包括执行PostProcess,添加bean创建过程的观察者,初始化特殊的bean等,不过不重要,就省略了…

(5) finishBeanFactoryInitialization(beanFactory); //传入bean工厂,并实例化bean(重点)

调用finishBeanFactoryInitialization(beanFactory)方法,传入bean工厂,并实例化bean(重点)(实例化所有的非懒加载的单例对象)
在这里插入图片描述

finishBeanFactoryInitialization(beanFactory)内的步骤

preInstantiateSingletons(); // 保存并遍历beanName查找是否有现成的,没有就调用getBean()方法

在preInstantiateSingletons()方法中,beanName会使用一个arraylist存储起来,去遍历beanName看有没有现成的bean,如果没有,然后就是调用getBean()方法后的一层层方法的嵌套…直到下一步
在这里插入图片描述
在这里插入图片描述

Instaniate(); //bean的实例化

经过自getBean()方法后的一层层嵌套,最终会到达Instaniate()方法中进行实例化

也就是在这个方法中,我们终于见到了反射机制
在这里插入图片描述
在这个方法的最后,调用instantiateClass()去完成最后的实例化
在这里插入图片描述
instantiateClass(); // 将初始化的bean实例化成一个完整的bean

在 instantiateClass()方法中,newInstance()实例化!
在这里插入图片描述
调用populateBean方法填充属性!
在这里插入图片描述
在实例化、填充属性之后,会先执行aware的方法和aop的一系列操作配置(即beforeInitalizetion和afterInitalizetion()方法去截取出切面类),添加方法,最后形成一个完整的bean对象
在这里插入图片描述
补充:单例模式的ioc容器就是一个ConcurrentHashMap<String,Object>
在这里插入图片描述
在这里插入图片描述

3. bean的生命周期

1. ioc启动时,寻找Bean的定义信息并将其实例化

2. 使用依赖注入,spring按照Bean定义信息配置Bean的所有属性

3. 如果Bean实现了BeanNameAware接口,工厂调用Bean的SetBeanName()方法设置BeanName

4. 如果Bean实现了BeanFactoryAware接口,工厂调用setBeanFactory()方法传设置BeanFactory(Bean工厂)

5. 如果BeanPostProcessor和Bean关联,那么调用 postProcessBeforeInitialization()方法

6. 如果Bean指定了init-method方法,将被调用

7. 最后,如果有BeanPostProcessor和Bean关联,那么调用postProcessAfterInitialization()方法

此时,Bean已经可以被应用系统使用,并将被保留在BeanFactory中知道他不再被需要。有两种方式可以将其从BeanFactory中删除掉的方法

① 如果Bean实现了DisposableBean接口,destroy()方法将被调用

② 如指定了定制的销毁方法,就调用这个方法

在这里插入图片描述

bean生命周期的简要总结

① 使用BeanDefinition生产bean

② bean实例化前后的操作

③ 实例化bean

④ 配置bean的信息

⑤ setBeanName()

⑥ setBeanFactory()

⑦ 前置处理器和后置处理器(bean初始化前后的操作)

⑧ inti bean(初始化bean)

⑨ 使用bean

⑩ 销毁bean(destroy())

(1)在bean实例化前后进行操作

实例化或初始化前后添加操作,都是实现BeanPostProcessor接口或其子接口

① 创建实体类User,加上ioc注解

@Component
@Data
@AllArgsConstructor
//@NoArgsConstructor
public class User {
    public User(){
        System.out.println("执行了构造方法,完成了实例化");
    }
    @Value("123")
    int id;
    @Value("小明")
    String name;
}

② 创建一个类,实现InstantiationAwareBeanPostProcessor子接口

@Component
public class UserBeanAwareInstaniate implements InstantiationAwareBeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if(beanName.equals("user")){
            System.out.println("执行了实例化前的操作");
            //return new User(233,"实例化前就构造的bean");
        }

        return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if(beanName.equals("user")){
            System.out.println("执行了实例后的操作");
        }
        return InstantiationAwareBeanPostProcessor.super.postProcessAfterInstantiation(bean, beanName);
    }
}

③ 在main方法中进行测试

ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class); //读取配置类

Object user1 = applicationContext.getBean("user");
User user2 = applicationContext.getBean(User.class);

System.out.println(user1);
System.out.println("=====");
System.out.println(user2);
System.out.println(user1==user2);
System.out.println(user1.equals(user2));

在这里插入图片描述

注意:如果实例化前对比beanName中,返回了一个对象,那么这个对象就会成为返回的bean,而且不会执行实例化后的方法,也不会执行初始化前的方法,但是初始化后的方法会执行

(2)在bean初始化前后进行操作

① User类实现InitializingBean接口,用于记录初始化结束的时间

public class User implements InitializingBean {
    public User(){
        System.out.println("执行了构造方法,完成了实例化");
    }
    @Value("123")
    int id;
    @Value("小明")
    String name;

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("初始化完成");
    }
}

② 创建一个类,同样实现InstantiationAwareBeanPostProcessor接口,添加初始化前后的操作

@Component
public class UserBeanInit implements InstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(beanName.equals("user")) {
            System.out.println("===============");
            System.out.println("执行了初始化前的方法");
//            return new User(233,"初始化前创建的userBean");
        }
        return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(beanName.equals("user")) {
            System.out.println("执行了初始化后的方法");
//            return new User(233,"初始化后创建的userBean");
        }
        return InstantiationAwareBeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

③ 执行
在这里插入图片描述
同样的,如果在初始化前后返回一个对象,那么这个对象就会成为bean

AOP也就是在初始化后实现的,调用了getProxy()方法,将对象加工成了一个代理类返回

4. bean的作用域

使用@Scope(“xxx”)或xml配置bean的作用域

作用域描述
singleton在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值
prototype每次从容器中调用Bean时,都返回一个新的实例,相当于每次调用getBean()时,都执行newXxxBean()
request每次HTTP请求都会创建一个新的Bean
request同一个HTTP Session共享一个Bean,不同Session使用不同的Bean
application限定一个Bean的作用域为ServletContext的整个生命周期

注意:request、request、application仅适用于web下的Spring WebApplicationContext环境

BeanFactory和FactoryBean,ApplicationContext的区别

① BeanFactory是一个Factory接口,是用来管理bean的IOC容器或对象工厂,较为古老,不支持spring的一些插件。
BeanFactory使用了懒加载,适合多例模式。
懒加载就是BeanFactory在启动的时候不会去实例化Bean,当使用getBean()方法时才会去对应实例化bean;

② FactoryBean是一个Bean接口,是一个可以生产或者装饰对象的工厂Bean,可以通过实现该接口自定义的实例化Bean的逻辑。

③ApplicationConext是BeanFactory的子接口,扩展了其功能,ApplicationContext是立即加载,适合单例模式。ApplicationContext在启动的时候就把所有的Bean全部实例化了。它还可以为Bean配置lazy-init=true来让Bean懒加载;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(1) BeanFactory的使用

① 先创建一个实体类User

@Data
@AllArgsConstructor
//@NoArgsConstructor
public class User {
    User(){
        System.out.println("执行了构造方法");
    }
    int id;
    String name;
}

② 在main方法中使用BeanFactory

    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); //创建一个Bean工厂

        beanFactory.registerSingleton("user",new User()); // 向Bean工厂中注册一个单例bean

        Object user1 = beanFactory.getBean("user");
        Object user2 = beanFactory.getBean("user");

        System.out.println(user1);
        System.out.println("=====");
        System.out.println(user2);
        System.out.println(user1==user2);
        System.out.println(user1.equals(user2));

在这里插入图片描述

(2) FactoryBean的使用

① 创建一个UserFactoryBean类实现FactoryBean接口

public class UserFactoryBean implements FactoryBean {
    @Override
    public Object getObject() throws Exception {
        return new User(233,"这是FactoryBean创建的user对象");
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}

② main方法中进行测试

 UserFactoryBean userFactoryBean = new UserFactoryBean();

        User user1 = null;
        User user2 = null;
        try {
             user1 = (User) userFactoryBean.getObject();  // 从userFactoryBean中获取User的bean
             user2 = (User) userFactoryBean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(user1);
        System.out.println("=====");
        System.out.println(user2);
        System.out.println(user1==user2);
        System.out.println(user1.equals(user2));

在这里插入图片描述
这里的equals方法经过调试发现是使用的String里的equals方法,有知道原因的希望评论区说一下

(3) ApplicationContext的使用

ApplicationContext就是ioc现在最常用的方式,有xml、注解方式去使用,下面以注解的方式使用为例

① 创建一个配置类,加上@ComponentScan注解,指定ioc要扫描的包

@ComponentScan("com.ruoxi")
public class Config {
}

② 实体类上加上ioc的注解

@Component
@Data
@AllArgsConstructor
//@NoArgsConstructor
public class User {
    User(){
        System.out.println("执行了构造方法");
    }
    @Value("123")
    int id;
    @Value("小明")
    String name;
}

③ main方法中进行测试

 ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class); //读取配置类

        Object user1 = applicationContext.getBean("user");
        User user2 = applicationContext.getBean(User.class);

        System.out.println(user1);
        System.out.println("=====");
        System.out.println(user2);
        System.ou.println(user1==user2);

在这里插入图片描述

5. IOC的执行流程总结

1. 通过xml或注解的方式,获取到类的信息

2. 调用super()方法等

3. 调用refresh()方法

在refresh()方法中的主要操作

(1) 执行创建BeanFactory(bean工厂)

(2) 将类的信息封装成BeanDefinition对象

(3) 创建PostProcessBeanFactory(用于改变bean的定义信息)

(4) 调用finishBeanFactoryInitialization(beanFactory)方法,传入bean工厂,进行一层层的方法嵌套,搜索现有的bean,如果不存在,就最终在Instaniate()方法中使用反射机制,调用InstaniateClass()方法

(5) 在InstaniateClass()方法中,完成创建bean的操作,bean属性的赋值,aop的操作(init初始化bean前后的方法),形成一个完成的bean对象并返回

如有错误望评论区指正

以上是关于从源码逐步分析IOC的执行流程(含流程图及总结)的主要内容,如果未能解决你的问题,请参考以下文章

深入浅出Spring原理及实战「原理分析专题」不看源码就带你剖析IOC容器核心流程以及运作原理

Spring IOC容器初始化流程源码分析

Spring IOC容器初始化流程源码分析

Spring的IoC容器创建Bean流程从理论到源码分析

浅谈SpringMVC核心组件及执行流程(含源码解析)

浅谈SpringMVC核心组件及执行流程(含源码解析)