Spring原始碼簡易手寫實現(學習過程記錄)(二)
2.1 建立Scope註解
context.getBean("userService");
在建立bean物件的時候,會傳入一個beanName,根據這個beanName,首先框架會去判斷這個beanName是單例bean還是原型bean
在實體類中被componen註解的類,我們需要一個Scope註解,來表示這個類是單例還是原型,如果沒有Scope註解預設是單例
@Component("userService")
@Scope("prototype")
public class UserService {
}
首先我們來建立一個Scope註解
package com.rainwood.spring; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Scope { String value(); }
value並不是預設的
那麼如何通過簡單得context.getBean("userService")
就可以知道這個beanName是單例還是原型呢
單例代表著,每次getBean取出來的是同一個bean物件,而原型每次取出來的都是新建立的bean物件
我們可以通過map (key value)存取的方式來存bean物件,這樣getBean的時候,傳進一個beanName,就可以通過map取出這個bean物件
//定義一個map,來存放單例的bean物件 private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();//單例池
可以建立一個單例池來存放單例bean物件
那麼如何根據beanName來判斷是單例還是原型
2.2 beanDefination物件
我們需要建立一個beanDefination物件來實現
package com.rainwood.spring; public class BeanDefination { private Class clazz; private String scope; public Class getClazz() { return clazz; } public void setClazz(Class clazz) { this.clazz = clazz; } public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } }
這個物件儲存了bean所對應的class類還有它的scope
在掃描包的時候,建立該beanDefination物件,然後將beanDefination物件存在map裡
private ConcurrentHashMap<String, BeanDefination> beanDefinationMap = new ConcurrentHashMap<>();//儲存所有掃描到的bean的定義
呼叫getBean時候:
public Object getBean(String beanName) {
if(beanDefinationMap.containsKey(beanName)) {
BeanDefination beanDefination = beanDefinationMap.get(beanName);
String scope = beanDefination.getScope();
Class clazz = beanDefination.getClazz();
System.out.println(clazz);
if(scope == "singleton") {
Object o = singletonObjects.get(beanName);
return o;
} else {
Object bean = createBean(beanDefination);
return bean;
}
} else {
//不存在對應的Bean
throw new NullPointerException();
}
}
先是根據beanName取到對應的class和scope,判斷scope是單例還是原型
如果是單例,從單例池裡拿到對應的bean物件,singletonObjects.get(beanName);
如果是原型,我們需要建立一個createBean方法,將beanDefination傳進去進一步處理(下一節再學)
public Object createBean(BeanDefination beanDefination) {
Class clazz = beanDefination.getClazz();
try {
Object instance = clazz.getDeclaredConstructor().newInstance();
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
2.3 掃描並且建立單例池和beanDefinationMap
上一節中,我們已經通過掃描獲取了Component註解的類
掃描:
try {
//這個地方不是放的class檔案的url,而是這個類的全限定名
Class<?> aClass = classLoader.loadClass(className);
//判斷這個類是不是有Component註解
if(aClass.isAnnotationPresent(Component.class)) {
//表示當前這個類是一個Bean
//解析類,判斷當前bean是單例bean還是prototype的bean 解析之後會生成一個BeanDefination物件
//BeanDefination
//拿到Component 對應的beanName
Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
String beanName = componentAnnotation.value();
BeanDefination beanDefination = new BeanDefination();
beanDefination.setClazz(aClass);
//如果可以拿到Scope註解
//如果拿不到表示這個類預設是一個單例
if(aClass.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = aClass.getDeclaredAnnotation(Scope.class);
String scope = scopeAnnotation.value();
beanDefination.setScope(scope);
} else {
beanDefination.setScope("singleton");
}
beanDefinationMap.put(beanName, beanDefination);
}
} catch (Exception e) {
e.printStackTrace();
}
呼叫scan方法掃描後,已經將beanDefinationMap中放入了所有的beanDefination物件和beanName
還需要將bean單例依次放入單例池中:
for (Map.Entry<String, BeanDefination> entry : beanDefinationMap.entrySet()) {
String beanName = entry.getKey();
BeanDefination beanDefination = entry.getValue();
if(beanDefination.getScope().equals("singleton")) {
Object bean = createBean(beanDefination); // 建立單例bean物件
singletonObjects.put(beanName, bean);
}
}
下面是limingApplicationContext.java:
package com.rainwood.spring;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class limingApplicationContext {
private Class configClass;
//定義一個map,來存放單例的bean物件
private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();//單例池
private ConcurrentHashMap<String, BeanDefination> beanDefinationMap = new ConcurrentHashMap<>();//儲存所有掃描到的bean的定義
public limingApplicationContext(Class configClass) throws ClassNotFoundException {
this.configClass = configClass;
//解析配置類
//ComponentScan註解--》掃描路徑 -->掃描-->Beandefination--->BeanDefinationMap
scan(configClass);
for (Map.Entry<String, BeanDefination> entry : beanDefinationMap.entrySet()) {
String beanName = entry.getKey();
BeanDefination beanDefination = entry.getValue();
if(beanDefination.getScope().equals("singleton")) {
Object bean = createBean(beanDefination); // 建立單例bean物件
singletonObjects.put(beanName, bean);
}
}
}
public Object createBean(BeanDefination beanDefination) {
Class clazz = beanDefination.getClazz();
try {
Object instance = clazz.getDeclaredConstructor().newInstance();
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
private void scan(Class configClass) {
//解析配置類
//ComponentScan註解--》掃描路徑
ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
String path = componentScanAnnotation.value(); //掃描路徑
path = path.replace(".","/");
System.out.println(path);
//掃描
//類載入器:
//Bootstrap -->jre/lib
//Ext----->jre/ext/lib
//App---->classpath
ClassLoader classLoader = limingApplicationContext.class.getClassLoader(); //app 取得的是classpath對應的類載入器
//resource對應的是一個目錄 file:/D:/spring%e6%ba%90%e7%a0%81%e5%ad%a6%e4%b9%a0/target/classes/com/rainwood/liming/service
URL resource = classLoader.getResource(path);
File file = new File(resource.getFile());
//判斷是不是一個目錄
if(file.isDirectory()) {
//可以獲取到這個目錄下所有的編譯好的class檔案路徑
File[] files = file.listFiles();
for (File file1 : files) {
System.out.println(file1);
//D:\springResourceLearn\target\classes\com\rainwood\liming\service\UserService.class 轉化成com.rainwood.liming.service.UserService
String fileName = file1.getAbsolutePath();
//判斷是不是class檔案再進一步處理
if(fileName.endsWith(".class")) {
String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));
className = className.replace("\\",".");
try {
//這個地方不是放的class檔案的url,而是這個類的全限定名
Class<?> aClass = classLoader.loadClass(className);
//判斷這個類是不是有Component註解
if(aClass.isAnnotationPresent(Component.class)) {
//表示當前這個類是一個Bean
//解析類,判斷當前bean是單例bean還是prototype的bean 解析之後會生成一個BeanDefination物件
//BeanDefination
//拿到Component 對應的beanName
Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
String beanName = componentAnnotation.value();
BeanDefination beanDefination = new BeanDefination();
beanDefination.setClazz(aClass);
//如果可以拿到Scope註解
//如果拿不到表示這個類預設是一個單例
if(aClass.isAnnotationPresent(Scope.class)) {
Scope scopeAnnotation = aClass.getDeclaredAnnotation(Scope.class);
String scope = scopeAnnotation.value();
beanDefination.setScope(scope);
} else {
beanDefination.setScope("singleton");
}
beanDefinationMap.put(beanName, beanDefination);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public Object getBean(String beanName) {
if(beanDefinationMap.containsKey(beanName)) {
BeanDefination beanDefination = beanDefinationMap.get(beanName);
String scope = beanDefination.getScope();
Class clazz = beanDefination.getClazz();
System.out.println(clazz);
if(scope == "singleton") {
Object o = singletonObjects.get(beanName);
return o;
} else {
Object bean = createBean(beanDefination);
return bean;
}
} else {
//不存在對應的Bean
throw new NullPointerException();
}
}
}
test
package com.rainwood.liming;
import com.rainwood.spring.limingApplicationContext;
public class test {
public static void main(String[] args) throws ClassNotFoundException {
limingApplicationContext context = new limingApplicationContext(AppConfig.class);
//如果是單例bean getBean呼叫多次獲得的都是同一個bean物件 (可以通過map來實現, key就是beanName, 對應的value就是bean物件) 單例池
//如果是原型bean 會返回不同的bean物件
// Object bean = context.getBean("userService");
System.out.println(context.getBean("userService"));
System.out.println(context.getBean("userService"));
System.out.println(context.getBean("userService"));
// System.out.println(bean);
}
}
打印出getBean,如果是原型的話執行結果:
可見雜湊值不同
com/rainwood/liming/service
D:\springResourceLearn\target\classes\com\rainwood\liming\service\UserService.class
D:\springResourceLearn\target\classes\com\rainwood\liming\service\XxUtil.class
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@5e2de80c
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@1d44bcfa
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@266474c2
如果是單例則:
com/rainwood/liming/service
D:\springResourceLearn\target\classes\com\rainwood\liming\service\UserService.class
D:\springResourceLearn\target\classes\com\rainwood\liming\service\XxUtil.class
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@60e53b93
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@60e53b93
class com.rainwood.liming.service.UserService
com.rainwood.liming.service.UserService@60e53b93
雜湊值相同