SpringIOC源码学习DayOne


在源码编译的时候遇到缺少 import org.apache.commons.logging.Log;
我的解决办法是
build.gradle 文件的dependencies里

org.apache.logging.log4j:log4j-jul后加:
compile("org.slf4j:jcl-over-slf4j:${log4jVersion}")

在这里插入图片描述

BeanFactory和ApplicationContext

在这里插入图片描述
Bean工厂的存在就是为了生成bean。applicationContext继承了BeanFactory

IOC容器加载过程源码深度剖析

准备的类:
Bean

@Component
public class Car {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Car() {
		System.out.println("car加载....");
	}
}

MainConfig

@Configuration
@ComponentScan(basePackages = {"com.tuling.iocbeanlifecicle"})
public class MainConfig {
	
}

MainStarter

public class MainStarter {

	public static void main(String[] args) {

		AnnotationConfigApplicationContext context = new 
AnnotationConfigApplicationContext(MainConfig.class);

		Car car = context.getBean("car", Car.class);

		System.out.println(car.getName());
	}
}

第一步:

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		//调用构造函数
		this();
		//注册我们的配置类
		register(annotatedClasses);
		//IOC容器刷新接口
		refresh();
	}

我们先来为构造方法做一个简单的说明:

  1. 这是一个有参的构造方法,可以接收多个配置类,不过一般情况下,只会传入一个配置类。
  2. 这个配置类有两种情况,一种是传统意义上的带上@Configuration注解的配置类,还有一种是没有 带上@Configuration,但是带有@Component,@Import,@ImportResouce,@Service,
    @ComponentScan等注解的配置类,在Spring内部把前者称为Full配置类,把后者称之为Lite配置 类。在本源码分析中,有些地方也把Lite配置类称为普通Bean。

调用无参构造

public AnnotationConfigApplicationContext() {
		/**
		 * 初始化注解模式下的bean定义扫描器
		 * 调用AnnotatedBeanDefinitionReader构造方法,传入的是this(AnnotationConfigApplicationContext)对象
		 */

		this.reader = new AnnotatedBeanDefinitionReader(this);
		/**
		 * 初始化我们的classPath类型的bean定义扫描器
		 */
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

首先会调用父类无参构造,实例化beanFactory

public GenericApplicationContext() {
		/**
		 * 调用父类的构造函数,为ApplicationContext spring上下文对象
		 * 初始beanFactory
		 * 
		 * 为啥是DefaultListableBeanFactory?我们去看BeanFactory接口
		 * 的时候
		 * 
		 * 发DefaultListableBeanFactory是最底层的实现,功能是最全的
		 */
		this.beanFactory = new DefaultListableBeanFactory();
}

在这里插入图片描述
其中实现了BeanDefinitionRegistry接口,有注册bean定义的能力,接下来回到我们的子类的无参构造

public AnnotationConfigApplicationContext() {
		/**
		 * 初始化注解模式下的bean定义扫描器,读取的是配置类
		 * 
		 * 调用AnnotatedBeanDefinitionReader构造方法,
		 * 传入的是this(AnnotationConfigApplicationContext)对象
		 */

		this.reader = new AnnotatedBeanDefinitionReader(this);
		/**
		 * 初始化我们的classPath类型的bean定义扫描器
		 */
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

在这里new AnnotatedBeanDefinitionReader注册了很多后置处理器,
在这里插入图片描述
注册了很多创世纪的类
在这里插入图片描述
再说下ClassPathBeanDefinitionScanner。我们可以手动扫描。举个例子

	AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
	String name = Car.class.getPackage().getName();
	context.scan(name);
	context.refresh();
	Car car = (Car) context.getBean("car");
	System.out.println(car.getName());

总结一下this(),

  • 实例化bean工厂
  • 获取bean定义的读取器,注册了很多创世纪的类,其中很重要的:ConfigurationClassPostProcessor。给大家看下类结构图
    在这里插入图片描述
    如果不注册这个,我们的配置类将无法解析。
  • 实例化了AnnotatedBeanDefinitionReader,只是读到了bean定义,是由bean的后置处理器负责解析的,因为这种方式扩展性强,灵活度高。把创世纪的6个bean定义注册到bean工厂的beanDefinitionMap里
  • 实例化了ClassPathBeanDefinitionScanner,这里的scanner仅仅是为了程序员可以手动调用AnnotationConfigApplicationContext对象的scan方法, spring在执行工程后置处理器ConfigurationClassPostProcessor时,去扫描包时会new一个ClassPathBeanDefinitionScanner

注册我们的配置类

把配置类MainConfig注册到BeanDefinitionMap

register(annotatedClasses);

在这里插入图片描述
最终会调用DefaultListableBeanFactory中的registerBeanDefinition方法去注册,
DefaultListableBeanFactory维护着一系列信息,比如beanDefinitionNamesbeanDefinitionMap
beanDefinitionNames是一个List,用来保存beanName
beanDefinitionMap是一个Map,用来保存beanName和beanDefinition

IOC容器刷新接口

refresh()

学spring就是学这里面的十三个方法。这个是重点
先看这个方法invokeBeanFactoryPostProcessors(beanFactory);
调用我们的bean工厂的后置处理器.会在此处将class扫描成beanDefinition。
在这里插入图片描述
这个方法,调用后置处理器将注解标记为@ComponentScan,@Component,@Service等,解析出来放到beanDefinitionMap里面。如上图。当执行完这句,bean就到了map里
在这里插入图片描述
注册我们bean的后置处理器, 给我们容器中注册了我们bean的后置处理器
bean的后置处理器在什么时候进行调用?在bean的各个生命周期中都会进行调用

registerBeanPostProcessors(beanFactory);

再看这个方法,finishBeanFactoryInitialization(beanFactory);实例化我们剩余的单实例bean。

BeanFactory和FactoryBean

实现FactoryBean接口的bean,会重写getObject方法。是一种特殊的bean。

@Component
public class Car implements FactoryBean<Tank> {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Car() {
		System.out.println("car加载....");
	}

	@Override
	public Tank getObject() throws Exception {
		return new Tank();
	}

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

	@Override
	public boolean isSingleton() {
		return false;
	}
}

我的car实现了Factory接口,getObject方法返回的是Tank;

AnnotationConfigApplicationContext context = new 
					AnnotationConfigApplicationContext(MainConfig.class);
Car car = (Car) context.getBean("car");

报错:
在这里插入图片描述
那么怎么得到car呢?通过这种方式

Car car = (Car) context.getBean("&car");

这个有什么作用?当我们去集成mybatis时候,mybaitis的Mapper接口以动态代理的形式返回。
总结:

  1. BeanFactory是负责生产bean的,利用了工厂方法。
  2. 实现FactoryBean接口的bean,会重写getObject方法。返回的是getObject里的返回值

Bean的生命周期源码深度剖析

我们看refresh方法里的finishBeanFactoryInitialization(beanFactory);实例化我们剩余的单实例bean。然后主要看这个方法beanFactory.preInstantiateSingletons();实例化剩余的单实例bean。进入DefaultListableBeanFactory类的preInstantiateSingletons方法。看下面的方法,打个断点,beanName为car的,进入,发现不是工厂bean,则进入getBean(bean)方法。

//获取我们容器中所有bean定义的名称
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		//循环我们所有的bean定义名称
		for (String beanName : beanNames) {
			//合并我们的bean定义
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			/**
			 * 根据bean定义判断是不是抽象的&& 不是单例的 &&不是懒加载的
			 */
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				//是不是工厂bean
				if (isFactoryBean(beanName)) {
					//是的话 给beanName+前缀&符号
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						final FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
											((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						//调用真正的getBean的流程
						if (isEagerInit) {
							getBean(beanName);
						}
					}
				}
				else {//非工厂Bean 就是普通的bean
					getBean(beanName);
				}
			}
		}
/**
	 * 该方法是一个空壳方法,没有任何的实现逻辑 真正的逻辑调用在doGetBean()中
	 * 该接口是实现了BeanFactory的getBean(String name)接口
	 * @param name bean的名称 该名称也有可能是bean的alies(别名)
	 * @return 我们的单例对象
	 * @throws BeansException
	 */
	@Override
	public Object getBean(String name) throws BeansException {
		//真正的获取bean的逻辑
		return doGetBean(name, null, null, false);
	}

doCreateBean方法去创建bean,做了这几步:

  1. 实例化反射createBeanInstance
  2. 填充属性@Autowired @Value
  3. 初始化initMethod destoory
    在这里插入图片描述
    createBeanInstancef方法得到bean实例在这里插入图片描述
    此时还没有经过属性赋值。此处用到了装饰器设计模式,将bean的实例以及很多信息都封装到了instanceWrapper里面。
    然后调用populateBean(beanName, mbd, instanceWrapper);我们的属性进行赋值(调用set方法进行赋值)

总结

首先实例化AnnotationConfigApplicationContext,实例化AnnotatedBeanDefinitionReader,它会加载很多创世纪的类,这个创世纪的类里面,包括很多解析我们后续类的处理,比如会解析加了@Configuration的配置类,还会解析@ComponentScan@ComponentScans注解扫描的包,以及解析@import@Autowired等注解。其中最关键的处理我们bean的配置类的后置处理器.然后实例化scan,最终再解析配置类的时候所初始化的扫描器才是真正去扫描配置类的,这里在这里插入图片描述
用到的仅是手动去扫描包,来提供的支持。context.scan(name); context.refresh();
父类无参构造方法里的DefaultListableBeanFactory会实现BeanDefinitionRegistry
在这里插入图片描述

在这里插入图片描述
只是把需要的后续处理的类准备好了。然后refresh方法里
调用我们的bean工厂的后置处理器,会在此处将class扫描成beanDefinition

invokeBeanFactoryPostProcessors(beanFactory);

解析配置类,把配置类注册成bean定义。注册好后,实例化我们剩余的单实例bean.

finishBeanFactoryInitialization(beanFactory);

把我们所有的单例从bean定义注册成最终的bean。

getBean(),判断是否符合生产标准,及是不是FactoryBean,是的话通过getObject()放法返回。普通bean则从一级缓存中拿,如果拿到了直接返回,没有拿到调用createBean。最终进入doCreateBean进行实例化,填充属性,初始化,最终把bean放到1级缓存中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值