spring官网笔记1

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的调用逻辑大概是:

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

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

  • 通过实例工厂方法的方式

测试代码(配置文件不变)这种方式和@Bean的方式一样,就不多赘述了。

d`方法中。通过静态工厂方法这种方式特殊之处在于,包含这个静态方法的类,不需要实例化,不需要被Spring管理。Spring的调用逻辑大概是:

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

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

  • 通过实例工厂方法的方式

测试代码(配置文件不变)这种方式和@Bean的方式一样,就不多赘述了。

这样只是产生了一个Bean对象,还没走完bean的生命周期,接下来笔者会按照官网的节奏分析Bean的生命周期。

猜你喜欢

转载自blog.csdn.net/jessionlist/article/details/108760745