日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

徒手撸一个简单的 IOC

發布時間:2025/3/21 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 徒手撸一个简单的 IOC 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Spring框架中最經典的兩個就是IOC和AOP,其中IOC(Inversion of Control)是什么呢?控制反轉,簡單來說就是將控制實體Bean的動作交給了Spring容器進行管理。再簡單點來說就是例如之前想用一個類,必須new一個,但是使用了Spring那么直接用@Autowired注解或者用xml配置的方式就能直接獲得此對象,而且你也不用管它的生命周期啊等等之類的。就不用自己new一個對象了。

如果是之前沒有使用IOC的話,那么這些對象的創建以及賦值都是由我們自己創建了,下面簡單的演示了如果有上面四個對象依賴的話,那么沒有IOC我們必須要創建對象并且賦值。僅僅四個對象就這么多,那么一旦項目大了,對象成百上千,如果還這樣寫的話,那么絕對是一場災難。

對象A?a?=?new?對象A(); 對象B?b?=?new?對象B(); 對象C?c?=?new?對象C(); 對象D?d?=?new?對象D(); a.setB(b); a.setC(c); b.setD(d); c.setD(d);

因此在Spring中通過IOC將所有的對象統一放到Spring的容器中進行管理,所以就簡單了很多。上面的實例化對象的代碼也不需要我們寫了。

?

上面說了那么多,其實就是一句話IOC非常重要,但是如果直接看Spring源碼的話會非常懵逼,所以就簡單的寫一個IOC的小例子來理解這種思想。

分析并編寫代碼

還是編寫代碼前的分析階段,Spring的IOC其實就是將所有的Bean放在統一容器中進行管理起來,然后在在獲取的時候進行初始化,所以需要我們在程序啟動的時候將被標記的類進行存儲在自定義的容器中管理。

  • 初始化階段:將被@MyIoc類似于Spring中@Service標記的類放入到自定義的容器中。

  • 使用:通過自定義的獲取Bean的類進行統一獲取。

現在我們就以上面兩個步驟進行詳細點的分析

數據準備階段

首先初始化階段我們要先建立兩個注解類用于類的發現(@MyIoc類似于@Service)。

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public?@interface?MyIoc?{}

然后要初始化信息進自定義容器的話用什么類型的容器去存儲這些信息呢?這里可以想到是用Map來存,用key為類名,value用什么呢?value就是要放在容器中進行管理的類的信息了,那么一個類有什么信息呢即類是由什么組成呢?有以下幾個信息

  • 類名

  • 構造函數

  • 屬性值

  • 父類

所以根據上面的分析我們可以建立一個實體類來存儲這些信息,此時我們就不考慮復雜的構造函數了,就都是初始化的無參構造函數。然后父類的屬性就不進行分析注入了。所以此時類實體類就簡單了。

@Data public?class?BeanDefinition?{private?String?className;private?String?alias;private?String?superNames; }

初始化階段

有了存儲類信息的類了,那么我們在程序啟動的時候就應該將這些信息給加載到Map中,此時建立一個啟動類用于初始化被@MyIoc標記的類的信息。

@Component @Order(value?=?1) public?class?IoCInitConifg?implements?CommandLineRunner{@Overridepublic?void?run(String...?args){ConcurrentHashMap<String,BeanDefinition>?concurrentHashMap?=?new?ConcurrentHashMap<>();Reflections?reflections?=?new?Reflections();//獲得項目中所有被MyIoc標記得類Set<Class<?>>?typesAnnotatedWith?=?reflections.getTypesAnnotatedWith(MyIoc.class);//將其信息初始進自定義容器MyBeanFactory中for?(Class?clazz?:?typesAnnotatedWith){BeanDefinition?beanDefinition?=?new?BeanDefinition();String?className?=?clazz.getName();String?superclassName?=?clazz.getSuperclass().getName();beanDefinition.setClassName(className);beanDefinition.setSuperNames(superclassName);beanDefinition.setAlias(getClassName(className));concurrentHashMap.put(className,beanDefinition);}MyBeanFactoryImpl.setBeanDineMap(concurrentHashMap);}private?String?getClassName(String?beanClassName)?{String?className?=?beanClassName.substring(beanClassName.lastIndexOf(".")?+?1);className?=?className.substring(0,?1).toLowerCase()?+?className.substring(1);return?className;} }

此時得說一下自定義的統一容器管理的類MyBeanFactory此類用作統一獲得類的途徑

public?interface?MyBeanFactory?{Object?getBeanByName(String?name)?throws?Exception; }

此時還有其實現類

@Log4j public?class?MyBeanFactoryImpl?implements?MyBeanFactory{//存儲對象名稱和已經實例化的對象映射private?static?ConcurrentHashMap<String,Object>?beanMap?=?new?ConcurrentHashMap<>();//存儲對象名稱和對應對象信息的映射private?static?ConcurrentHashMap<String,BeanDefinition>?beanDefineMap=?new?ConcurrentHashMap<>();//存儲存儲在容器中對象的名稱private?static?Set<String>?beanNameSet?=?Collections.synchronizedSet(new?HashSet<>());@Overridepublic?Object?getBeanByName(String?name)?throws?Exception?{//看有沒有已經實例化的對象,有的話就直接返回Object?object?=?beanMap.get(name);if?(object?!=?null){return?object;}//沒有的話就實例化一個對象object?=?getObject(beanDefineMap.get(name));if?(object?!=?null){//對實例化中對象的注入需要的參數setFild(object);//將實例化的對象放入Map中,便于下次使用beanMap.put(name,object);}return?object;}public?void?setFild(Object?bean)?throws?Exception?{Field[]?declaredFields?=?bean.getClass().getDeclaredFields();for?(Field?field:?declaredFields){String?filedAllName?=?field.getType().getName();if?(beanNameSet.contains(filedAllName)){Object?findBean?=?getBeanByName(filedAllName);//為對象中的屬性賦值field.setAccessible(true);field.set(bean,findBean);}}}public?Object?getObject(BeanDefinition?beanDefinition)?throws?Exception?{String?className?=?beanDefinition.getClassName();Class<?>?clazz?=?null;try?{clazz?=?Class.forName(className);}?catch?(ClassNotFoundException?e)?{log.info("can?not?find?bean?by?beanName:?"+className);throw?new?Exception("can?not?find?bean?by?beanName:?"+className);}return?clazz;}public?static?void?setBeanDineMap(ConcurrentHashMap<String,BeanDefinition>?beanDefineMap){MyBeanFactoryImpl.beanDefineMap?=?beanDefineMap;}public?static?void?setBeanNameSet(Set<String>?beanNameSet){MyBeanFactoryImpl.beanNameSet?=?beanNameSet;}}

此時初始化的階段已經完成了,即已經將所有被@MyIoc標記的類已經被全部存放在了自定義的容器中了。其實在這里我們已經能使用自己的自定義的容器進行獲得Bean了。

@MyIoc @Data public?class?User?{private?Student?student; }@MyIoc public?class?Student?{public?String?play(){return?"student"+?this.toString();} }

此時我們在啟動類中寫如下

????????User?user1?=?(User)beanFactory.getBeanByName("com.example.ioc.domain.User");User?user2?=?(User)beanFactory.getBeanByName("com.example.ioc.domain.User");Student?student1?=?user1.getStudent();Student?student2?=?user1.getStudent();Student?student3?=?(Student)beanFactory.getBeanByName("com.example.ioc.domain.Student");System.out.println(user1);System.out.println(user2);System.out.println(student1);System.out.println(student2);System.out.println(student3);

發現控制臺中輸出的對象都是同一個對象,并且在User中也自動注入了Student對象。此時一個簡單的IOC就完成了。

User(student=com.example.ioc.domain.Student@705e7b93) User(student=com.example.ioc.domain.Student@705e7b93) com.example.ioc.domain.Student@705e7b93 com.example.ioc.domain.Student@705e7b93 com.example.ioc.domain.Student@705e7b93

總結

本來一開始的想法的是想要寫一個類似于@Autowired注解的自定義注解,但是在編碼過程中遇到了一個困難,就是例如下面的代碼,實例化B容易,但是如何將B注入到每一個實例化的A中,這個問題困擾了我好幾天,也查找了許多的資料,至今還是沒有解決,估計是只有研究Spring源碼才能夠了解是如何做到的。

@MyIoc public?class?A{@MyIocUse private?B?b;}

https://github.com/modouxiansheng/SpringBoot-Practice

參考文章

  • https://juejin.im/post/5a5875a4518825733a30a463

  • https://juejin.im/entry/599f8ba6518825241f788ad1

總結

以上是生活随笔為你收集整理的徒手撸一个简单的 IOC的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。