Spring源码分析(1)-Spring容器以及Bean的实例化

Spring容器以及Bean的实例化

最近在看Spring源码相关的内容,结合鲁班学院的子路老师,记录一下自己的理解,以及在源码阅读中的一些问题。版本基于5.2.5

1 Spring容器

1.1 容器是什么?

The org.springframework.context.ApplicationContext interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the beans.

这句话的大概意思有两个:

  1. Spring IOC容器就是一个org.springframework.context.ApplicationContext的实例化对象
  2. 容器负责了实例化,配置以及装配一个bean

从两个角度去考虑Spring 容器

  1. 从代码层次来看:Spring容器就是一个实现了ApplicationContext接口的对象(如ClassPathXmlApplicationContext;
  2. 从功能上来看: Spring 容器是 Spring 框架的核心,是用来管理对象的。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。

具体可以参考图如下
Spring源码分析(1)-Spring容器以及Bean的实例化

1.2 容器是如何工作的?

Spring源码分析(1)-Spring容器以及Bean的实例化

Spring容器通过我们提交的pojo类以及配置元数据产生一个充分配置的可以使用的系统

这里说的配置元数据,实际上我们就是我们提供的XML配置文件,或者通过注解方式提供的一些配置信息

2 Spring Bean

2.1 如何实例化一个Bean

Spring源码分析(1)-Spring容器以及Bean的实例化

从官网上看,有三种方法

  1. 构造方法
  2. 通过静态工厂
  3. 通过实例化工厂

从源码的角度来分析,直接定位到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance这个方法中,具体定位步骤不再演示了,通过形如下面这段代码:

package com.bloom.spring;

import com.bloom.spring.bean.MyBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 *
 */
public class Test1 {

   public static void main(String[] args)  {

      ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
      MyBean myBean = context.getBean("myBean", MyBean.class);
      System.out.println(myBean.getMyName() + "---" + myBean.getMyAge());
      
      /*MyBean myBean = context.getBean("myBean", MyBean.class);
      System.out.println(myBean.getMyName() + "---" + myBean.getMyAge());
      myBean.myHi();
      System.out.println(context.getBean(ISomeService.class));
      */
   }
}

直接main方法运行,然后在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance这个方法的入口打一个断点,如图:
Spring源码分析(1)-Spring容器以及Bean的实例化

Spring源码分析(1)-Spring容器以及Bean的实例化

分析createBeanInstance方法的代码

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// Make sure bean class is actually resolved at this point.
		// 确保BeanDefinition中的beanClass熟悉已经完成解析
		Class<?> beanClass = resolveBeanClass(mbd, beanName);

		if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
		}
		// 通过BeanDefinition中的Supplier实例化这个bean
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}
		// 通过FactoryMethod来实例化这个bean
		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

		// Shortcut when re-creating the same bean...
		// 下面这段代码都是在通过构造函数实例化这个Bean,分两种情况,一种是通过默认的无参构造,一种                   是通过推断出来的构造函数
		boolean resolved = false;
		boolean autowireNecessary = false;
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					resolved = true;
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
		if (resolved) {
			if (autowireNecessary) {
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				return instantiateBean(beanName, mbd);
			}
		}

		// Candidate constructors for autowiring?
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// Preferred constructors for default construction?
		ctors = mbd.getPreferredConstructors();
		if (ctors != null) {
			return autowireConstructor(beanName, mbd, ctors, null);
		}

		// No special handling: simply use no-arg constructor.
    	// 通过见到的构造方法来实例化Bean
		return instantiateBean(beanName, mbd);
	}

关注实例化对象的几个方法

1 通过BeanDefinition中的instanceSupplier直接获取一个实例化的对象。这个instanceSupplier属性我本身不是特别理解,在xml中的标签以及注解的方式都没有找到方式配置这个属性。后来在org.springframework.context.support.GenericApplicationContext这个类中找到了以下两个方法

Spring源码分析(1)-Spring容器以及Bean的实例化

这种通过supplier来实例化Bean在日常上基本不能见到,在这里也就不探究。可能是Spring提供的一种方便外部扩展的方法手段

2 通过不同的创建bean的手段,来分别验证对象的实例化方法

  • 通过@compent,@Service等注解的方式

    测试代码

    package com.bloom.spring.bean;
    
    import org.springframework.stereotype.Component;
    
    /**
     * @program: bloom
     * @description:
     * @author: hao.yu
     * @create: 2020-04-16 16:23
     **/
    @Component
    public class Service {
    }
    package com.bloom.spring;
    
    import com.bloom.spring.bean.Service;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.context.annotation.ComponentScan;
    
    /**
     * @program: bloom
     * @description:
     * @author: hao.yu
     * @create: 2020-04-16 16:17
     **/
    @ComponentScan
    public class Test2 {
    
    
    	public static void main(String[] args) {
    		AnnotationConfigApplicationContext ac = new 	AnnotationConfigApplicationContext(Test2.class);
    		ac.refresh();
    		System.out.println(ac.getBean(Service.class.getName()));
    	}
    
    }
    
    

观察debug:
Spring源码分析(1)-Spring容器以及Bean的实例化

可以发现,代码执行到最后一行,同时我们看代码上面的注释可以知道,当没有进行特殊的处理的时候,默认会使用无参构造函数进行对象的实例化。

  • 通过普通XML的方式(同@compent注解,这里就不赘诉了)
  • 通过@Configuration注解的方式
@Configuration
@ComponentScan("com.bloom")
public class Config {
	@Bean
	public Service service(){
		return new Service();
	}
}

package com.bloom.spring.bean;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @program: bloom
 * @description:
 * @author: hao.yu
 * @create: 2020-04-16 17:00
 **/
public class Test3 {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ac =
				new AnnotationConfigApplicationContext(Config.class);
		System.out.println(ac.getBean("service"));
	}
}

同样,断点也进入最后一行

  • 通过@Bean的方式

断点结果
Spring源码分析(1)-Spring容器以及Bean的实例化

可以发现,通过@Bean方法创建对象时,Spring底层是通过factoryMethod的方法进行实例化对象的。Spring会在我们需要实例化的这个对象对应的BeanDefinition中记录factoryBeanName是什么(在上面的例子中factoryBeanName就是config),同时会记录这个factoryBean中创建对象的factoryMethodName是什么,最后通过factoryBeanName获取一个Bean然后反射调用factoryMethod实例化一个对象。

需要注意的是

  1. 这里所说的通过静态工厂方式通过factoryBeanName获取一个Bean,注意,这个Bean,不是一个FactoryBean。也就是说不是一个实现了org.springframework.beans.factory.FactoryBean接口的Bean。至于什么是FactoryBean我们在后面的文章会认真分析
  2. 提到了一个概念BeanDefinition,它就是Spring对自己所管理的Bean的一个抽象。不懂可以暂且跳过。
  • 通过静态工厂的方式

测试代码

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd">

   <bean id="myBean" class="com.bloom.spring.bean.MyBean">
      <property name="myAge" value="100"/>
      <property name="myName" value="张三丰"/>
   </bean>

   <bean id="heBean" class="com.bloom.spring.bean.HeBean">
      <property name="heName" value="张翠山"/>
   </bean>
   <bean id="companyBean" class="com.bloom.spring.factory.CompanyFactoryBean">
      <property name="companyInfo" value="拉勾,中关村,500"/>
   </bean>

   <bean id="myService" class="com.bloom.spring.factory.ServiceFactory" factory-method="getSomeService">

   </bean>

</beans>


package com.bloom.spring.factory;

/**
 * @program: bloom
 * @description:
 * @author: hao.yu
 * @create: 2020-04-16 17:09
 **/
public class ServiceFactory {
	public static ISomeService getSomeService() {
		return new SomeServiceImpl();
	}
}

package com.bloom.spring.factory;

/**
 * @program: bloom
 * @description:
 * @author: hao.yu
 * @create: 2020-04-16 17:10
 **/
public class SomeServiceImpl implements ISomeService {

	public SomeServiceImpl() {
		System.out.println("执行无参构造器");
	}
	@Override
	public void doSome() {
		System.out.println("执行doSome()方法");
	}
}

package com.bloom.spring.factory;

/**
 * @program: bloom
 * @description:
 * @author: hao.yu
 * @create: 2020-04-16 17:09
 **/
public interface ISomeService {
	void doSome();
}


断点如下:

Spring源码分析(1)-Spring容器以及Bean的实例化

可以发现,这种情况也进入了instantiateUsingFactoryMethod方法中。通过静态工厂方法这种方式特殊之处在于,包含这个静态方法的类,不需要实例化,不需要被Spring管理。Spring的调用逻辑大概是:

  1. 通过bean标签中的class属性得到一个Class对象
  2. 过Class对象获取到对应的方法名称的Method对象
  3. 最后反射调用Method.invoke(null,args)

因为是静态方法,方法在执行时,不需要一个对象。

  • 通过实例工厂方法的方式(@Bean方式执行的流程是一样的)
实例化总结
  1. 象实例化,只是得到一个对象,还不是一个完全的Spring中的Bean,我们实例化后的这个对象还没有完成依赖注入,没有走完一系列的声明周期;

  2. Spring官网上指明了,在Spring中实例化一个对象有三种方式:

    a. 构造函数

    b. 实例工厂方法

    c. 静态工厂方法

  3. 总结

    Spring通过解析我们的配置元数据,以及我们提供的类对象得到一个Beanfinition对象。通过这个对象可以实例化出一个java bean对象。主要流程如图:

    Spring源码分析(1)-Spring容器以及Bean的实例化

上一篇:Go语言:常用限流策略——漏桶与令牌桶介绍


下一篇:大家一起学Golang——Go语言关键字与基本语法