一切開始之前挽拂,先了解下 JavaBean 是什么,它是一種標(biāo)準(zhǔn)和約定人灼。
一個 JavaBean:
- 所有的屬性都是 private(通過 getter/setter 進(jìn)行讀寫)
- 有一個 public 無參數(shù)的構(gòu)造器
- 實(shí)現(xiàn)了 Serializable(序列化围段,注意 Serializable 接口沒有方法也沒有字段,更多的是一種規(guī)范和約定)
1. Spring 是什么
Srping 是自動化管理 Java 對象和其中依賴關(guān)系的容器投放。
- Java世界中Web容器的事實(shí)標(biāo)準(zhǔn)
- Spring 容器 - 一個 IoC 容器
- Spring MVC - 基于 Spring 和 Servlet 的 Web 應(yīng)用框架
- Spring Boot - 集成度和自動化程度更高(內(nèi)嵌了 Servlet 容器)
- Spring MVC - 基于 Spring 和 Servlet 的 Web 應(yīng)用框架
-let 詞根奈泪,“小”的意思,servlet 小服務(wù)器(service + let)。
Servlet 將網(wǎng)絡(luò)請求封裝成對象涝桅,交給上層 WebApp(spring容器拜姿、mvc、boot)冯遂。
Servlet <-> HttpServletRequest/HttpServletResponse <-> WebApp蕊肥。
常見的 servlet 有:
- tomcat
- jetty
沒有 Spring 怎么辦?
**
- 一個 main 程序走天下:輕量簡單蛤肌,但是規(guī)模大了后難以維護(hù)壁却。
- 拆分并且手動管理:優(yōu)點(diǎn)方便測試、共同開發(fā)裸准、維護(hù)展东,但缺點(diǎn)還是規(guī)模更大后,依賴關(guān)系太復(fù)雜炒俱。
Spring 的出現(xiàn)解放了我們的雙手盐肃。
2. Spring 最簡單的用法
通過配置一種“上古”的 xml 來使用。
xml 配置文件中聲明了兩個 Bean向胡, 而這兩個 Bean 之間存在依賴關(guān)系恼蓬,因此為 OrderService 中的成員屬性 OrderDao 添加了注解惊完,用于對 OrderDao 自動裝配僵芹。
- @AutoWired(以此為例)
- @Resource
- ...
在這里,容器就是 BeanFactory小槐。有了容器之后拇派,向容器索要 Bean。
// maven 依賴
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
package spring.demo;
public class OrderDao {
public void select() {
System.out.println("select!");
}
}
package spring.demo;
import org.springframework.beans.factory.annotation.Autowired;
public class OrderService {
@Autowired // 加了這個注解才會自動裝配 OrderDao凿跳,否則 OrderService 對象中的 orderDao 是 null
private OrderDao orderDao;
public void doSomething() {
orderDao.select();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config /> <!-- 這句不加的話件豌,即使寫了上面的注解也不生效 -->
<!-- bean definitions here -->
<bean id="orderDao" class="spring.demo.OrderDao"/>
<bean id="orderService" class="spring.demo.OrderService"/>
</beans>
package spring.demo;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringMain {
public static void main(String[] args) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:spring/config.xml");
OrderService orderService = (OrderService)beanFactory.getBean("orderService");
OrderDao orderDao = (OrderDao) beanFactory.getBean("orderDao");
System.out.println(orderService);
orderService.doSomething();
System.out.println(orderDao);
}
}
現(xiàn)在沒有寫 new,卻拿到了 OrderService 對象(一個Bean)控嗜,默認(rèn)是單例模式茧彤,也就是 OrderService 對象中的 OrderDao 和再次通過 getBean 得到的 OrderDao 對象是同一個對象。

假如沒有使用 Spring疆栏,那么可能要自己手動創(chuàng)建曾掂,涉及到的各種對象的各種 new。
3. Spring 容器核心概念
- Bean
容器中的最小工作單元壁顶,通常為一個 Java 對象
- BeanFactory/ApplicationContext
容器本身對應(yīng)的 Java 對象
- 依賴注入(DI)
容器負(fù)責(zé)注入所有的依賴
- 控制反轉(zhuǎn)(IoC)
用戶將控制權(quán)交給了 Spring 容器來進(jìn)行自動裝配
4. 手寫一個簡單的 IoC 容器
- 定義 Bean
- 加載 Bean 的定義
- 實(shí)例化 Bean
- 查找依賴珠洗,實(shí)現(xiàn)依賴注入
- 要什么 Bean 就設(shè)置成什么 Bean
目錄結(jié)構(gòu):

使用方法是在字段是聲明注解(部分代碼不再羅列,這里僅供舉例):
import org.springframework.beans.factory.annotation.Autowired;
public class OrderService {
@Autowired private OrderDao orderDao;
@Autowired private UserService userService;
public void createOrder() {
orderDao.createOrder(userService.getCurrentLoginUser());
}
}
具體實(shí)現(xiàn):
Java 從初代就支持了 .properties
格式的配置文件若专,該文件用來存儲簡單的基于 key-value pairs 的參數(shù)许蓖。該配置文件處于被編譯的代碼之外。
- 先寫一個簡單的 beans.properties 配置文件,定義了 Bean 的名字和對應(yīng)的實(shí)現(xiàn)類:
# Bean 名字 和 Bean 的全限定類名
orderDao=com.github.hcsp.ioc.OrderDao
userDao=com.github.hcsp.ioc.UserDao
userService=com.github.hcsp.ioc.UserService
orderService=com.github.hcsp.ioc.OrderService
- MyIoCContainer 容器:
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class MyIoCContainer {
// 實(shí)現(xiàn)一個簡單的IoC容器膊爪,使得:
// 1. 從beans.properties里加載bean定義
// 2. 自動掃描bean中的@Autowired注解并完成依賴注入
// 定義一個容器! 存放 bean 的名字到 bean 實(shí)例對象的映射
private Map<String, Object> beans = new HashMap<>();
/**
* 依賴注入
*
* @param beanInstance bean 的 實(shí)例
*/
private void dependencyInject(Object beanInstance) {
// 拿到帶有 @AutoWired 注解的 fields
List<Field> fieldsToBeAutoWired = Stream.of(beanInstance.getClass().getDeclaredFields())
.filter(field -> field.getAnnotation(Autowired.class) != null)
.collect(Collectors.toList());
// 為當(dāng)前 bean 對象的需要依賴的字段注入依賴(設(shè)置字段值)
fieldsToBeAutoWired.forEach(field -> {
String fieldName = field.getName(); // 加了 @AutoWired 的字段名即是所要依賴的 bean 的名字
Object dependencyBeanInstance = beans.get(fieldName); // 所依賴的 bean 實(shí)例
try {
field.setAccessible(true); // 設(shè)置為 true 用來壓制針對被反射對象的訪問檢查
field.set(beanInstance, dependencyBeanInstance); // 從而可以在這里設(shè)置當(dāng)前 bean 的私有字段
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
});
}
/**
* 啟動該容器
*/
public void start() {
// bean 的初始化
Properties properties = new Properties();
// 從 InputStream 中讀取屬性列表(鍵值對)
try {
properties.load(MyIoCContainer.class.getResourceAsStream("/beans.properties"));
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println(properties);
properties.forEach((beanName, beanClassName) -> {
try {
// 通過反射拿到 bean 的實(shí)例并放入容器中
Class<?> klass = Class.forName((String) beanClassName);
Object beanInstance = klass.getConstructor().newInstance();
beans.put((String) beanName, beanInstance);
} catch (Exception e) {
throw new RuntimeException(e);
}
});
// 使用反射自阱,處理依賴關(guān)系,注入依賴
beans.forEach((beanName, beanInstance) -> dependencyInject(beanInstance));
}
/**
* 從容器中獲取一個bean
*
* @param beanName bean 的名字
* @return 返回 bean 的實(shí)例
*/
public Object getBean(String beanName) {
return beans.get(beanName);
}
public static void main(String[] args) {
MyIoCContainer container = new MyIoCContainer();
container.start();
OrderService orderService = (OrderService) container.getBean("orderService");
orderService.createOrder();
}
}
即使是兩個依賴字段存在循環(huán)依賴也沒關(guān)系蚁飒,因?yàn)樵趧?chuàng)造期間动壤,會先各自創(chuàng)建出實(shí)例對象。相關(guān)依賴字段此時(shí)是 null淮逻,不影響 bean 的創(chuàng)建琼懊。而創(chuàng)建完之后,再回頭對字段注入所依賴的 bean爬早。
當(dāng)然哼丈,以上只是個簡單的實(shí)現(xiàn),實(shí)際的 Spring 中還可以在構(gòu)造器上使用 @Autowired 注解筛严,而不推薦在私有字段上使用醉旦。這樣即使不是用 Spring,比如寫測試代碼的時(shí)候桨啃,還是可以方便的 new 一個實(shí)例车胡,否則,私有字段還要通過反射一頓操作才能創(chuàng)建實(shí)例進(jìn)行測試照瘾。
另外匈棘,實(shí)際應(yīng)用中 @Autowired 也不推薦使用了,更推薦 @Inject 注解析命,這樣除了 Spring 之外主卫,還可能受到其他類似框架的識別。
所謂的 Spring 只不過是在以上基礎(chǔ)上擴(kuò)充了無窮無盡的功能鹃愤,比如支持 xml 中配置依賴簇搅,支持構(gòu)造器注入(調(diào)用哪個構(gòu)造器,傳遞哪些參數(shù))等等软吐,從一個很簡單的思想不斷擴(kuò)充成龐大的體系瘩将。
5. Spring 啟動過程淺析
- 在 xml 里中定義 Bean
- BeanDefinition 的加載和解析
- Bean 的實(shí)例化和依賴注入
- 對外提供服務(wù)
建議 debug Spring 源碼的時(shí)候,要帶著目前來進(jìn)行凹耙,不要在無關(guān)細(xì)節(jié)里浪費(fèi)太多時(shí)間姿现。


