徒手撸一个简单的 IOC
Spring框架中最經(jīng)典的兩個就是IOC和AOP,其中IOC(Inversion of Control)是什么呢?控制反轉,簡單來說就是將控制實體Bean的動作交給了Spring容器進行管理。再簡單點來說就是例如之前想用一個類,必須new一個,但是使用了Spring那么直接用@Autowired注解或者用xml配置的方式就能直接獲得此對象,而且你也不用管它的生命周期啊等等之類的。就不用自己new一個對象了。
如果是之前沒有使用IOC的話,那么這些對象的創(chuàng)建以及賦值都是由我們自己創(chuàng)建了,下面簡單的演示了如果有上面四個對象依賴的話,那么沒有IOC我們必須要創(chuàng)建對象并且賦值。僅僅四個對象就這么多,那么一旦項目大了,對象成百上千,如果還這樣寫的話,那么絕對是一場災難。
對象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將所有的對象統(tǒng)一放到Spring的容器中進行管理,所以就簡單了很多。上面的實例化對象的代碼也不需要我們寫了。
?
上面說了那么多,其實就是一句話IOC非常重要,但是如果直接看Spring源碼的話會非常懵逼,所以就簡單的寫一個IOC的小例子來理解這種思想。
分析并編寫代碼
還是編寫代碼前的分析階段,Spring的IOC其實就是將所有的Bean放在統(tǒng)一容器中進行管理起來,然后在在獲取的時候進行初始化,所以需要我們在程序啟動的時候將被標記的類進行存儲在自定義的容器中管理。
-
初始化階段:將被@MyIoc類似于Spring中@Service標記的類放入到自定義的容器中。
-
使用:通過自定義的獲取Bean的類進行統(tǒng)一獲取。
現(xiàn)在我們就以上面兩個步驟進行詳細點的分析
數(shù)據(jù)準備階段
首先初始化階段我們要先建立兩個注解類用于類的發(fā)現(xiàn)(@MyIoc類似于@Service)。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public?@interface?MyIoc?{}然后要初始化信息進自定義容器的話用什么類型的容器去存儲這些信息呢?這里可以想到是用Map來存,用key為類名,value用什么呢?value就是要放在容器中進行管理的類的信息了,那么一個類有什么信息呢即類是由什么組成呢?有以下幾個信息
-
類名
-
構造函數(shù)
-
屬性值
-
父類
所以根據(jù)上面的分析我們可以建立一個實體類來存儲這些信息,此時我們就不考慮復雜的構造函數(shù)了,就都是初始化的無參構造函數(shù)。然后父類的屬性就不進行分析注入了。所以此時類實體類就簡單了。
@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;} }此時得說一下自定義的統(tǒng)一容器管理的類MyBeanFactory此類用作統(tǒng)一獲得類的途徑
public?interface?MyBeanFactory?{Object?getBeanByName(String?name)?throws?Exception; }此時還有其實現(xiàn)類
@Log4j public?class?MyBeanFactoryImpl?implements?MyBeanFactory{//存儲對象名稱和已經(jīng)實例化的對象映射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?{//看有沒有已經(jīng)實例化的對象,有的話就直接返回Object?object?=?beanMap.get(name);if?(object?!=?null){return?object;}//沒有的話就實例化一個對象object?=?getObject(beanDefineMap.get(name));if?(object?!=?null){//對實例化中對象的注入需要的參數(shù)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;}}此時初始化的階段已經(jīng)完成了,即已經(jīng)將所有被@MyIoc標記的類已經(jīng)被全部存放在了自定義的容器中了。其實在這里我們已經(jīng)能使用自己的自定義的容器進行獲得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);發(fā)現(xiàn)控制臺中輸出的對象都是同一個對象,并且在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的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 教你 7 招,迅速提高服务器并发能力!
- 下一篇: 高并发下的接口幂等性解决方案!