前言
在學習Spring框架原始碼時,記住一句話:原始碼并不難,只需要給你各種業務場景或者專案經理,你也能實作自己的Spring,雖然你的實作可能無法與開源團隊相媲美,但是你肯定可以實作一個0.0.1版本,因此,初次閱讀原始碼時,不要陷入太深的細節中,先了解大體邏輯,再仔細研讀,
實作功能
本文將帶領大家實作一個簡易版的Spring框架,并介紹以下功能點:
- 了解Spring的底層原始碼啟動程序
- 了解BeanDefinition的概念
- 了解Spring決議配置類等底層原始碼作業流程
- 了解依賴注入,Aware回呼等底層原始碼作業流程
- 了解Spring AOP的底層原始碼作業流程
以上功能點將使我們對Spring框架的實作有所了解,但我們并不會一下子實作整個Spring框架的業務,我們將從上述功能點入手,通過手寫模擬Spring框架來實作這些功能,
首先,我們像使用Spring一樣,傳入配置類獲取applicationContext,再通過getBean方法獲取具體物件,最后,我們呼叫方法并列印日志,如果你對這些基本流程不熟悉,可以查看我的入門系列文章:Spring入門系列:淺析知識點
詳細流程如下:
- 決議配置類上的ComponentScan注解,獲取掃描的基本路徑
- 開始決議各個被掃描到的檔案,是否是需要被Spring管理,如果是則暫存到list集合中
- 開始遍歷被Spring管理list集合,決議各個類上的注解,比如是否是懶加載,然后將這些屬性都封裝到applicationContext中的以beanName為key的BeanDefineMap中
- 針對已經決議好的bean定義進行創建物件并實體化,并將其放入以beanName為key的singletonMap實體化快取池中,
- 在實體化時,如果發現有依賴注入的物件,則將實體化快取池中的物件存入,如果快取池中沒有該物件,則進行創建后再注入,
- 判斷物件是否實作了各個Aware介面,如果實作,則進行回呼,
- 判斷物件是否屬于增強類,在這里,我們模擬了事務注解,如果有事務注解,則創建一個代理物件,并為所有方法增加攔截器,然后將該代理物件存入單例快取池中,
詳細決議
專案結構
基本路徑:com.user目錄
各個注解及背景關系類:config目錄
需要被管理的Bean:service目錄
啟動類:com.user根目錄

原始碼分析
@Component
public class UserService implements ApplicationContextAware, BeanNameAware {
@AutoWired
ServiceDemo serviceDemo;
private XiaoyuApplicationContext applicationContext;
private String beanName;
public void test() {
serviceDemo.say();
// System.out.println(serviceDemo);
System.out.println("userService:"+applicationContext.getBean("userService"));
System.out.println("beanName:"+beanName);
}
@Override
public void setApplicationContext(XiaoyuApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
}
}
UserService類主要用于測驗是否Spring已經管理了相關物件并生成了代理物件,是我們的日常業務類,我們仔細看下XiaoyuApplicationContext類,主要的流程在這邊:
public class XiaoyuApplicationContext {
//配置類
private Class config;
//初始的bean定義
private Map<String,BeanDefinition> beanDefineMap = new HashMap<>();
//單例快取池
private Map<String,Object> singleBean = new HashMap<>();
public XiaoyuApplicationContext(Class myDemoConfigClass) {
config = myDemoConfigClass;
//決議配置類
scan();
}
public void scan(){
ComponentScan declaredAnnotation = (ComponentScan) config.getDeclaredAnnotation(ComponentScan.class);
String value = https://www.cnblogs.com/guoxiaoyu/p/declaredAnnotation.basePackages();
doScan(value);
//將bean定義Map生成具體的Bean物件
beanDefineMap.entrySet().stream().forEach(item->{
String beanName = item.getKey();
BeanDefinition beanDefinition = item.getValue();
if (!beanDefinition.isLazy() &&"singleton".equals(beanDefinition.getScope())) {
Object bean = createBean(beanName);
singleBean.put(beanName,bean);
}
});
}
/**
* 決議配置類
*/
private void doScan(String value) {
String path = value.replace(".","/");
//正常走檔案決議
ClassLoader classLoader = this.getClass().getClassLoader();
URL resource = classLoader.getResource(path);
File file = new File(resource.getFile());
List<File> classFile = new ArrayList<>();
//簡單點直接雙層決議即可
if (file.isDirectory()) {
for (File f : file.listFiles()) {
if (f.isDirectory()) {
for (File f1 : f.listFiles()) {
if (!f1.isDirectory()) {
classFile.add(f1);
}
}
} else {
classFile.add(f);
}
}
}
//遍歷所有決議檔案
for (File cFile : classFile) {
String absolutePath = cFile.getAbsolutePath();
String className = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"))
.replace("\\", ".");
try {
Class<?> clazz = classLoader.loadClass(className);
//是否需要被Spring管理
if (clazz.isAnnotationPresent(Component.class)) {
//將bean上的注解封裝到bean定義中
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setType(clazz);
beanDefinition.setLazy(clazz.isAnnotationPresent(Lazy.class));
if (clazz.isAnnotationPresent(Scope.class)) {
beanDefinition.setScope(clazz.getAnnotation(Scope.class).value());
} else {
beanDefinition.setScope("singleton");
}
String beanName = clazz.getAnnotation(Component.class).value();
if (beanName.isEmpty()) {
//如果不設定beanName會默認生產唯一一個name,Spring底層也是這樣做的
beanName = Introspector.decapitalize(clazz.getSimpleName());
}
beanDefineMap.put(beanName, beanDefinition);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public Object createBean(String beanName){
BeanDefinition beanDefinition = beanDefineMap.get(beanName);
Class type = beanDefinition.getType();
try {
Object instance = type.newInstance();
//屬性填充,依賴注入
populateBean(instance);
if (instance instanceof ApplicationContextAware){
((ApplicationContextAware) instance).setApplicationContext(this);
}
if (instance instanceof BeanNameAware) {
((BeanNameAware) instance).setBeanName(beanName);
}
//是否需要AOP增強
if (type.isAnnotationPresent(Transaction.class)) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(type);
//簡單的方法切面
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//開啟事務,關閉自動提交
System.out.println("事務已開啟");
Object res = method.invoke(instance, objects);
//提交事務
System.out.println("事務已提交");
return res;
}
});
return enhancer.create();
}
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
private void populateBean(Object instance) {
Field[] declaredFields = instance.getClass().getDeclaredFields();
Arrays.stream(declaredFields).forEach(item->{
//尋找注入點
if (item.isAnnotationPresent(AutoWired.class)) {
Object bean = getBean(item.getName());
item.setAccessible(true);
try {
item.set(instance,bean);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
});
}
public Object getBean(String beanName){
if (!beanDefineMap.containsKey(beanName)) {
throw new NullPointerException();
}
if ("singleton".equals(beanDefineMap.get(beanName).getScope())) {
if (singleBean.containsKey(beanName)) {
return singleBean.get(beanName);
} else {
Object bean = createBean(beanName);
singleBean.put(beanName,bean);
return bean;
}
}
return createBean(beanName);
}
}
以上即為整個流程的基本梳理,我們在實作程序中沒有涉及Bean回圈依賴以及其他各種創建快取,但Spring在實作Bean的創建程序中確實用到了各種本地快取和同步鎖(synchronized),在學習原始碼時,不要太關注這些細節,首先要理解整個流程,再深入研究,
結語
最后,我們在gitee上提供了專案原始碼,如果需要,可以查看spring-xiaoyu,雖然我認為寫一遍自己的代碼更好,因為這是最簡單的流程,有助于理解Spring原始碼,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/549827.html
標籤:Java
上一篇:一文吃透泛型
下一篇:java發送Http請求
