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的生命周期,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/123654.html
標籤:其他
