Spring源碼------手寫體驗IOC與DI
目錄
1、前言
本篇博客並非是對Spring源碼的深入研究。而是對上一篇博客《Spring源碼------手寫體驗MVC》結構的優化。過程中主要是體驗Spring IOC和DI的初始化過程。那麼這篇博客涉及到的知識點大致有以下幾點:
- 如何自定義註解,如何通過反射機制去賦予註解強大的功能(說白了,就是體驗在反射機制下,註解功能是多麼的強大)
- Spring Ioc容器的實現原理
- Spring DI 註解注入
- Java反射機制
- Java I/O流(加載配置文件,讀取配置文件信息)
2、Spring IOC和DI原理流程
Spring IOC的基本流程:
- 讀取配置文件
- 解析配置文件,並封裝成BeanDefinition
- 把BeanDefinition對應的實例放到容器進行緩存
Spring DI的基本流程:
- 循環讀取BeanDefinition的緩存信息
- 調用getBean()方法創建對象實例
- 將創建好的對象實例包裝爲BeanWrapper對象
- 將BeanWrapper對象緩存到IOC容器中
- 循環IOC容器執行來進行注入
大致流圖:
3、核心代碼
代碼結構圖:
核心上下文applicationContext 代碼:
**
* @description: 自定義上下文
* @author: zps
* @create: 2020-05-08 15:06
**/
public class ZPSApplicationContext {
private ZPSBeanDefinitionReader reader ;
//保存BeanDefinition信息
private Map<String , ZPSBeanDefinition> beanDefinitionMap = new HashMap<String , ZPSBeanDefinition>();
private Map<String, ZPSBeanWrapper> factoryBeanInstanceCache = new HashMap<String, ZPSBeanWrapper>();
private Map<String,Object> factoryBeanObjectCache = new HashMap<String, Object>();
public ZPSApplicationContext(String... contextConfigLocation) {
//加載配置文件
reader = new ZPSBeanDefinitionReader(contextConfigLocation);
//解析配置文件,封裝成BeanDefinition
List<ZPSBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
//把BeanDefinition緩存起來
try {
doRegistBeanDefinition(beanDefinitions);
} catch (Exception e) {
e.printStackTrace();
}
//依賴注入,這裏默認沒有延遲加載
doAutowired();
}
private void doAutowired() {
//調用getBean()
//這一步,所有的Bean並沒有真正的實例化,還只是配置階段
for(Map.Entry<String , ZPSBeanDefinition> beanDefinitionEntry : this.beanDefinitionMap.entrySet()){
//根據類名進行初始化
getBean(beanDefinitionEntry.getKey());
}
}
//把BeanDefinition緩存起來
private void doRegistBeanDefinition(List<ZPSBeanDefinition> beanDefinitions) throws Exception {
for (ZPSBeanDefinition beanDefinition : beanDefinitions) {
// if(this.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())){
// throw new Exception("The " + beanDefinition.getFactoryBeanName() + "is exists");
// }
beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
beanDefinitionMap.put(beanDefinition.getBeanClassName(),beanDefinition);
}
}
//Bean的實例化,DI是從而這個方法開始的
public Object getBean(String beanName){
//1、先拿到BeanDefinition配置信息
ZPSBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
//2、反射實例化newInstance();
Object instance = instantiateBean(beanName,beanDefinition);
//3、封裝成一個叫做BeanWrapper
ZPSBeanWrapper beanWrapper = new ZPSBeanWrapper(instance);
//4、保存到IoC容器
factoryBeanInstanceCache.put(beanName,beanWrapper);
//5、執行依賴注入
populateBean(beanName,beanDefinition,beanWrapper);
return beanWrapper.getWrapperInstance();
}
//執行依賴注入
private void populateBean(String beanName, ZPSBeanDefinition beanDefinition, ZPSBeanWrapper zpsBeanWrapper) {
Object instance = zpsBeanWrapper.getWrapperInstance();
Class<?> clazz = zpsBeanWrapper.getWrappedClass();
//在Spring中@Component
if(!(clazz.isAnnotationPresent(ZPSController.class) || clazz.isAnnotationPresent(ZPSService.class))){
return;
}
//把所有的包括private/protected/default/public 修飾字段都取出來
for (Field field : clazz.getDeclaredFields()) {
if(!field.isAnnotationPresent(ZPSAutowired.class)){ continue; }
ZPSAutowired autowired = field.getAnnotation(ZPSAutowired.class);
//如果用戶沒有自定義的beanName,就默認根據類型注入
String autowiredBeanName = autowired.value().trim();
if("".equals(autowiredBeanName)){
//field.getType().getName() 獲取字段的類型
autowiredBeanName = field.getType().getName();
}
//暴力訪問
field.setAccessible(true);
try {
if(this.factoryBeanInstanceCache.get(autowiredBeanName) == null){
continue;
}
//ioc.get(beanName) 相當於通過接口的全名拿到接口的實現的實例
field.set(instance,this.factoryBeanInstanceCache.get(autowiredBeanName).getWrapperInstance());
} catch (IllegalAccessException e) {
e.printStackTrace();
continue;
}
}
}
private Object instantiateBean(String beanName, ZPSBeanDefinition beanDefinition) {
String className = beanDefinition.getBeanClassName();
Object instance = null;
try {
if(this.factoryBeanObjectCache.containsKey(beanName)){
instance = this.factoryBeanObjectCache.get(beanName);
}else {
Class<?> clazz = Class.forName(className);
//2、默認的類名首字母小寫
instance = clazz.newInstance();
this.factoryBeanObjectCache.put(beanName, instance);
}
}catch (Exception e){
e.printStackTrace();
}
return instance;
}
public Object getBean(Class<?> clazz){
return getBean(clazz.getName());
}
public int getBeanDefinitionCounts() {
return this.beanDefinitionMap.size();
}
public String[] gteBeanDefinitionNames() {
return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
}
}
對應上圖的BeanDefinition:
**
* @description: 讀取配置文件工具類
* @author: zps
* @create: 2020-05-08 15:18
**/
public class ZPSBeanDefinitionReader {
Properties properties = new Properties();
//保存掃描的bean的類名信息
private List<String> regitryBeanClasses = new ArrayList<String>();
public ZPSBeanDefinitionReader(String... configLocations){
//讀取配置文件信息
doConfig(configLocations[0]);
//對文件進行掃描,篩選出需要的類文件
doScanner(properties.getProperty("scanPackage"));
}
//對文件進行掃描
private void doScanner(String scanPackage) {
//jar 、 war 、zip 、rar
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.","/"));
File classPath = new File(url.getFile());
//當成是一個ClassPath文件夾
for (File file : classPath.listFiles()) {
if(file.isDirectory()){
doScanner(scanPackage + "." + file.getName());
}else {
if(!file.getName().endsWith(".class")){continue;}
//全類名 = 包名.類名
String className = (scanPackage + "." + file.getName().replace(".class", ""));
//Class.forName(className);
regitryBeanClasses.add(className);
}
}
}
//加載配置文件
private void doConfig(String configLocation) {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(configLocation.replaceAll("classpath:",""));
try {
properties.load(is);
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null != is){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public List<ZPSBeanDefinition> loadBeanDefinitions(){
List<ZPSBeanDefinition> result = new ArrayList<ZPSBeanDefinition>();
try {
for (String className : regitryBeanClasses) {
Class<?> beanClass = Class.forName(className);
//保存類對應的ClassName(全類名)
//還有beanName
//1、默認是類名首字母小寫
result.add(doCreateBeanDefinition(toLowerFirstCase(beanClass.getSimpleName()), beanClass.getName()));
//2、自定義
//3、接口注入
for (Class<?> i : beanClass.getInterfaces()) {
result.add(doCreateBeanDefinition(i.getName(),beanClass.getName()));
}
}
}catch (Exception e){
e.printStackTrace();
}
return result;
}
private ZPSBeanDefinition doCreateBeanDefinition(String beanName, String beanClassName) {
ZPSBeanDefinition beanDefinition = new ZPSBeanDefinition();
beanDefinition.setFactoryBeanName(beanName);
beanDefinition.setBeanClassName(beanClassName);
return beanDefinition;
}
private String toLowerFirstCase(String simpleName) {
char [] chars = simpleName.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
}
對應上圖的BeanDefinition(該類主要是封裝從配置文件中讀取的類信息):
**
* @description: 封裝類的信息
* @author: zps
* @create: 2020-05-08 15:23
**/
public class ZPSBeanDefinition {
private String factoryBeanName; //簡單類名
private String beanClassName; //全類名
public String getFactoryBeanName() {
return factoryBeanName;
}
public void setFactoryBeanName(String factoryBeanName) {
this.factoryBeanName = factoryBeanName;
}
public String getBeanClassName() {
return beanClassName;
}
public void setBeanClassName(String beanClassName) {
this.beanClassName = beanClassName;
}
}
對應上圖的BeanWrapper :
/**
* @description: bean的包裝類
* @author: zps
* @create: 2020-05-08 17:39
**/
public class ZPSBeanWrapper {
private Object wrapperInstance;
private Class<?> wrappedClass;
public ZPSBeanWrapper(Object instance) {
this.wrapperInstance = instance;
this.wrappedClass = instance.getClass();
}
public Object getWrapperInstance() {
return wrapperInstance;
}
public Class<?> getWrappedClass() {
return wrappedClass;
}
}
運行截圖:
4、總結
對Spring Ioc 和DI 大致工作流程的描述:
Spring IOC的基本流程:
- 讀取配置文件
- 解析配置文件,並封裝成BeanDefinition
- 把BeanDefinition對應的實例放到容器進行緩存
Spring DI的基本流程:
- 循環讀取BeanDefinition的緩存信息
- 調用getBean()方法創建對象實例
- 將創建好的對象實例包裝爲BeanWrapper對象
- 將BeanWrapper對象緩存到IOC容器中
- 循環IOC容器執行來進行注入