前言
學(xué)習(xí)SpringBoot,絕對避不開自動裝配這個概念,這也是SpringBoot的關(guān)鍵之一
本人也是SpringBoot的初學(xué)者,下面的一些總結(jié)都是結(jié)合個人理解和實踐得出的,如果有錯誤或者疏漏,請一定一定一定(不是歡迎,是一定)幫我指出,在評論區(qū)回復(fù)即可,一起學(xué)習(xí)!
篇幅較長分四篇了,希望你可以有耐心.
如果只關(guān)心SpringBoot裝配過程,可以直接跳到第7部分
想要理解spring自動裝配,需要明確兩個含義:
- 裝配,裝配什么?
- 自動,怎么自動?
1. Warm up
在開始之前,讓我們先來看點簡單的開胃菜:spring中bean注入的三種形式
首先我們先來一個Person類,這里為了篇幅長度考慮使用了lombok
如果你不知道lombok是什么,那就最好不要知道,加了幾個注解之后我的pojo類Person就完成了
/**
* @author dzzhyk
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private String name;
private Integer age;
private Boolean sex;
}
在Spring中(不是Spring Boot),要實現(xiàn)bean的注入,我們有3種注入方式:
1.1 setter注入
這是最基本的注入方式
首先我們創(chuàng)建applicationContext.xml文件,在里面加入:
<bean id="person" class="pojo.Person">
<property name="name" value="dzzhyk"/>
<property name="age" value="20"/>
<property name="sex" value="true"/>
<span class="hljs-name"bean>
這里使用property為bean對象賦值
緊接著我們會在test包下寫一個version1.TestVersion1類
/**
* 第一種bean注入實現(xiàn)方式 - 在xml文件中直接配置屬性
*/
public class TestVersion1 {
@Test
public void test(){
ApplicationContext ca = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = ca.getBean("person", Person.class);
System.out.println(person);
}
}
這里我使用了ClassPathXmlApplicationContext來加載spring配置文件并且讀取其中定義的bean,然后使用getBean方法使用id和類來獲取這個Person的Bean對象,結(jié)果成功輸出:
Person(name=dzzhyk, age=20, sex=true)
1.2 構(gòu)造器注入
接下來是使用構(gòu)造器注入,我們需要更改applicationContext.xml文件中的property為construct-arg
class="pojo.Person">
index="0" type="java.lang.String" value="dzzhyk" />
index="1" type="java.lang.Integer" value="20"/>
index="2" type="java.lang.Boolean" value="true"/>
class="hljs-name"bean>
version2.TestVersion2內(nèi)容不變:
public class TestVersion2 {
@Test
public void test(){
ApplicationContext ca = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = ca.getBean("person", Person.class);
System.out.println(person);
}
}
依然正常輸出結(jié)果:
Person(name=dzzhyk, age=20, sex=true)
1.3 屬性注入
使用注解方式的屬性注入Bean是比較優(yōu)雅的做法
首先我們需要在applicationContext.xml中開啟注解支持和自動包掃描:
<context:annotation-config />
<context:component-scan base-package="pojo"/>
在pojo類中對Person類加上@Component注解,將其標(biāo)記為組件,并且使用@Value注解為各屬性賦初值
@Component
public class Person {
@Value("dzzhyk")
private String name;
@Value("20")
private Integer age;
@Value("true")
private Boolean sex;
}
然后添加新的測試類version3.TestVersion3
public class TestVersion3 {
@Test
public void test(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = ac.getBean("person", Person.class);
System.out.println(person);
}
}
運行也可以得到如下結(jié)果:
Person(name=dzzhyk, age=20, sex=true)
2. Warm up again
什么?還有什么?接下來我們來聊聊Spring的兩種配置方式:基于XML的配置和基于JavaConfig類的配置方式,這對于理解SpringBoot的自動裝配原理是非常重要的。
首先我們在Person的基礎(chǔ)上再創(chuàng)建幾個pojo類:這個Person有Car、有Dog
public class Car {
private String brand;
private Integer price;
}
public class Dog {
private String name;
private Integer age;
}
public class Person {
private String name;
private Integer age;
private Boolean sex;
private Dog dog;
private Car car;
}
2.1 基于XML的配置
接下來讓我們嘗試使用XML的配置方式來為一個Person注入
class="pojo.Person">
name="name" value="dzzhyk"/>
name="age" value="20"/>
name="sex" value="true"/>
name="dog" ref="dog"/>
name="car" ref="car"/>
class="hljs-name"bean>
class="pojo.Dog">
name="name" value="旺財"/>
name="age" value="5" />
class="hljs-name"bean>
class="pojo.Car">
name="brand" value="奧迪雙鉆"/>
name="price" value="100000"/>
class="hljs-name"bean>
然后跟普通的Bean注入一樣,使用ClassPathXmlApplicationContext來加載配置文件,然后獲取Bean
/**
* 使用XML配置
*/
public class TestVersion1 {
@Test
public void test(){
ClassPathXmlApplicationContext ca = new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = ca.getBean("person", Person.class);
System.out.println(person);
}
}
輸出結(jié)果如下:
Person(name=dzzhyk, age=20, sex=true, dog=Dog(name=旺財, age=5), car=Car(brand=奧迪雙鉆, price=100000))
2.2 基于JavaConfig類的配置
想要成為JavaConfig類,需要使用@Configuration注解
我們新建一個包命名為config,在config中新增一個PersonConfig類
@Configuration
@ComponentScan
public class PersonConfig {
@Bean
public Person person(Dog dog, Car car){
return new Person("dzzhyk", 20, true, dog, car);
}
@Bean
public Dog dog(){
return new Dog("旺財", 5);
}
@Bean
public Car car(){
return new Car("奧迪雙鉆", 100000);
}
}
此時我們的XML配置文件可以完全為空了,此時應(yīng)該使用AnnotationConfigApplicationContext來獲取注解配置
/**
* 使用JavaConfig配置
*/
public class TestVersion2 {
@Test
public void test(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PersonConfig.class);
Person person = ac.getBean("person", Person.class);
System.out.println(person);
}
}
仍然正常輸出了結(jié)果:
Person(name=dzzhyk, age=20, sex=true, dog=Dog(name=旺財, age=5), car=Car(brand=奧迪雙鉆, price=100000))
3. BeanDefinition
AbstractBeanDefinition
是spring中所有bean的抽象定義對象,我把他叫做bean定義
當(dāng)bean.class被JVM類加載到內(nèi)存中時,會被spring掃描到一個map容器中:
BeanDefinitionMap
這個容器存儲了bean定義,但是bean此時還沒有進(jìn)行實例化,在進(jìn)行實例化之前,還有一個
BeanFactoryPostProcessor
可以對bean對象進(jìn)行一些自定義處理
我們打開BeanFactoryProcessor這個接口的源碼可以發(fā)現(xiàn)如下內(nèi)容:
/*
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
*/
在spring完成標(biāo)準(zhǔn)的初始化過程后,實現(xiàn)BeanFactoryPostProcessor接口的對象可以用于定制bean factory,所有的bean definition都會被加載,但是此時還沒有被實例化。這個接口允許對一些bean定義做出屬性上的改動。
簡言之就是實現(xiàn)了BeanFactoryPostProcessor這個接口的類,可以在bean實例化之前完成一些對bean的改動。
大致流程我畫了個圖:
至此我們能總結(jié)出springIOC容器的本質(zhì):(我的理解)
由BeanDefinitionMap、BeanFactoryPostProcessor、BeanPostProcessor、BeanMap等等容器共同組成、共同完成、提供依賴注入和控制反轉(zhuǎn)功能的一組集合,叫IOC容器。
-
spring
+關(guān)注
關(guān)注
0文章
340瀏覽量
14344 -
源碼分析
+關(guān)注
關(guān)注
0文章
5瀏覽量
5552 -
自動裝配
+關(guān)注
關(guān)注
0文章
7瀏覽量
653
發(fā)布評論請先 登錄
相關(guān)推薦
評論