Spring容器
容器是什么?
容器如何工作?
Spring容器
容器是什么?
我们先看官网中的一句话:
翻译如下:
org.springframework.context。ApplicationContext接口表示Spring IoC容器,并负责实例化、配置和组装bean。
那么我们就可以说:
从代码层次来看:Spring容器就是一个实现了ApplicationContext的接口的对象。
从功能上来看:SPring容器是Spring框架的核心,是用来管理对象的。容器将创建对象,把它们连接在一起配置他们,并管理它们的整个生命周期从创建到销毁。
容器如何工作?
我们直接看官网上的一张图片,如下
Spring容器通过我们提交的pojo类以及配置元数据产生一个充分配置的可使用的系统。
这里说的配置原数据,实际上就是我们提供的XML配置文件,或者通过注解方式提供的一些配置信息
Spring Bean
如何实例化一个Bean?
从官网上来看,主要有三种方式
``
1、构造方法
2、通过静态工厂方法
3、通过实例工厂方法
这三种例子,官网都有例子,这里就不在贴了,我们通过自己查阅部分源码,来验证我们在官网得出的结论,然后通过debug等方式验证。
我们再从代码的角度分析一下,我们直接定位到
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance 具体定位后面再来分析,大家可以通过形如下面的这段代码
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd"
>
<bean id="myServiceImpl" class="com.phr.tx.MyServiceImpl">
</bean>
</beans>
public static void main(String[] args) {
ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("classpath:application.xml");
MyServiceImpl myService = (MyServiceImpl) cc.getBean("myServiceImpl");
}
然后打个断点打在上图如是位置。直接运行main方法。然后在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance打个断点,debug它
此时的beanName为我们需要的。接下来我们对这个方法进行分析,代码如下
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
//获取这个bean的class属性,确保BeanDefinition中的beanClass已经完成解析
// 我们通过xml从<bean>标签中解析出来的class属性在刚刚开始的时候必定是个字符串
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());
}
//2.通过beanDefinition中的supplier实例化这个bean
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// 3.通过FactoryMethod实例化这个bean
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
// 4.下面这段代码都是在通过构造函数实例化这个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?
//第二次调用后置处理器 作用:推断创建这个bean的构造方法
//后置处理器和方法:
//SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors
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.
return instantiateBean(beanName, mbd);
}
我们主要关注进行实例化的几个方法:
1、通过BeanDefinitionz中的instanceSupplier直接获取一个实例对象。这个instanceSupplier在org.springframework.context.support.GenericApplicationContext这个类里面
经过断点调试,在实例化对象时会进入上面的方法。下面是测试代码。
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.registerBean("service", Service.class,Service::new);
ac.refresh();
System.out.println(ac.getBean("service"));
}
这个方法一般不常用,笔者认为这是spring提供给外部的一种实例化方式。这里不过多讨论。
1、接下来,我们通过不同的方式创建bean,来分别验证对象的实例化方法。
通过@compent @service 等方式创建
@Component
public class ServiceTest {
}
@Configuration
@ComponentScan(value = {
"com.phr.tx"})
public class Config {
}
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
System.out.println(ac.getBean(ServiceTest.class));
}
观测debug
看javadoc就知道,默认使用无参构造方法进行实例化。
通过普通xml方式和@component类似,这里就不赘述了
通过@configuration
public static void main(String[] args) {
// AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
// System.out.println(ac.getBean(ServiceTest.class));
// 通过配置类扫描
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
// 这里将测试对象换为config即可,同时记得将条件断点更改为beanName.equlas("config")
System.out.println(ac.getBean(Config.class));
}
查看debug效果图
同样也是通过无参构造,不过看beanClass就知道这个是走了cglib代理。对于这种现象,笔者后面再来分析。
通过@Bean的方式
@Bean
public Service service(){
return new Service();
}
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
System.out.println(ac.getBean(Service.class));
可以发现我们通过@Bean方法创建对象时,Spring底层是通过factoryMethod的方法进行实例化对象的,Spring会在我们需要实例化的这个对应的Beandefinition中记录factoryBeanName是什么 如图所示,这个factoryBeanName是config。最后通过factoryBeanName
获取一个Bean然后反射调用factoryMethod
实例化一个对象。
<?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="myServiceImpl" class="com.dmz.official.service.Service"/>-->
<!-- the factory bean, which contains a method called get() -->
<bean id="myFactoryBean" class="com.phr.tx.MyFactoryBean">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- 测试实例工厂方法创建对象-->
<!-- <bean id="clientService"-->
<!-- factory-bean="myFactoryBean"-->
<!-- factory-method="get"/>-->
<!--测试静态工厂方法创建对象-->
<bean id="service"
class="com.phr.tx.MyFactoryBean"
factory-method="staticGet"/>
</beans>
public static void main(String[] args) {
ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("classpath:application.xml");
System.out.println(cc.getBean(Service.class));
}
可以发现,这种情况也进入了instantiateUsingFactoryMethod
方法中。通过静态工厂方法这种方式特殊之处在于,包含这个静态方法的类,不需要实例化,不需要被Spring管理。Spring的调用逻辑大概是:
- 通过
<bean>
标签中的class属性得到一个Class对象 - 通过Class对象获取到对应的方法名称的Method对象
- 最后反射调用
Method.invoke(null,args)
因为是静态方法,方法在执行时,不需要一个对象。
- 通过实例工厂方法的方式
测试代码(配置文件不变)这种方式和@Bean的方式一样,就不多赘述了。
d`方法中。通过静态工厂方法这种方式特殊之处在于,包含这个静态方法的类,不需要实例化,不需要被Spring管理。Spring的调用逻辑大概是:
- 通过
<bean>
标签中的class属性得到一个Class对象 - 通过Class对象获取到对应的方法名称的Method对象
- 最后反射调用
Method.invoke(null,args)
因为是静态方法,方法在执行时,不需要一个对象。
- 通过实例工厂方法的方式
测试代码(配置文件不变)这种方式和@Bean的方式一样,就不多赘述了。
这样只是产生了一个Bean对象,还没走完bean的生命周期,接下来笔者会按照官网的节奏分析Bean的生命周期。