自己實現一個SpringIOC——(1)
阿新 • • 發佈:2018-12-09
Spring框架中最重要也是最廣為人知的就是AOP和IOC了吧,AOP我已經講過了,今天我們就講講IOC,對於一些基本概念我就不贅述了,而且講了也很難深刻的理解,今天我們就自己編寫一個簡易的框架來實現IOC,至於我是怎麼知道的,嘿嘿,我是看了這裡的視訊:https://segmentfault.com/l/1500000013061317 , 所以如果有些小夥伴覺得我下面的內容比較看不懂的話,你們就看看這裡的視訊吧!
首先我們先看看程式碼的框架:
我們這就講講DispatcherServlet部分,不懂的可以看我github程式碼和視訊。
我們這個框架主要分為三個部分,流程也是按這三個部分走的:
- 找到bean
- 載入並註冊bean
- 注入bean
1 找到bean
找到bean在什麼地方,是對BeanDefinition的資源定位,是由ResourceLoader通過統一的Resource介面來完成,這個介面對各中形式的Resource都提供了統一介面,比如Xml,比如annotation。而這些都是由ResourceLoader來完成的
/** * * 找到bean * * */ private void scanBase(String basePackages) { //"com.yy" //file:/D:/IdeaProject/springIoc/springIoc/springioc/target/springioc/WEB-INF/classes/com/yy/ URL url = this.getClass().getClassLoader().getResource("/" + replacePath(basePackages));//getResource全部是帶斜槓的,所以我們要把點換成斜槓 String path = url.getFile(); ///D:/IdeaProject/springIoc/springIoc/springioc/target/springioc/WEB-INF/classes/com/yy/ File file = new File(path); //生成一個檔案 String [] strFiles = file.list(); //得到檔案底下的所有的檔名 for(String strFile: strFiles) { File eachFile = new File(path + strFile); if(eachFile.isDirectory()) { //如果是目錄,就遞迴繼續向裡面查詢 scanBase(basePackages +"."+eachFile.getName()); }else { System.out.println("class name" + eachFile.getName()); classNames.add(basePackages +"." + eachFile.getName()); } } } //將字串中的.換成/ String replacePath(String path) { return path.replaceAll("\\.","/"); }
2 載入並註冊bean
找到bean後,將bean註冊到我們的IOC容器中。Spring是通過一些ApplicationContext來完成的,比如FileSystemXmlApplicationContext, ClassPathXmlApplicationContext以及我們最常見的XmlWebApplicationContext,讀取之後將bean註冊到IOC容器中,簡單來說,就是把讀取的bean都放到一個map中。
/** * * 載入並註冊bean * * */ //把classNames迴圈一遍,看裡面有哪些類,然後把它生成出來載入進去 private void filterAndInstance() throws Exception { if( classNames.size() == 0) { return; } //迴圈獲取類名 for(String className : classNames) { Class clazz = Class.forName(className.replace(".class", "")); if(clazz.isAnnotationPresent(Controller.class)) { //如果clazz位元組碼上面帶了一個Annotation,並且Annotion是controller,就例項化一個controller物件出來 //獲取bean例項 Object instance = clazz.newInstance(); //獲取註解的value---將controller上的名字取出來(fish) String key = ((Controller)clazz.getAnnotation(Controller.class)).value(); //將bean交付給IOC instanceMap.put(key, instance); //eg:(fish , FishController) }else if(clazz.isAnnotationPresent(Service.class)) { //如果clazz位元組碼上面帶了一個Annotation,並且Annotion是Service //獲取bean例項 Object instance = clazz.newInstance(); //獲取註解的value String key = ((Service)clazz.getAnnotation(Service.class)).value(); //將bean交付給IOC instanceMap.put(key, instance); }else { continue; } } }
3 注入bean
當我們要用bean時,由IOC容器自動的注入進去。
/**
*
* 注入bean
*
* */
//把ioc容器裡的bean注入到指定地方(instanceMap中的值注入到@qualifier)p--DI
//把instanceMap迴圈一遍,把它每一個物件位元組碼中的file取出來,看看裡面有沒有Qualifier的註解,如果有的話,把qualifier中的value取出來,然後把value對應的物件注入到這裡
private void springDi() throws IllegalArgumentException, IllegalAccessException {
if(instanceMap.size() == 0) {
return;
}
/**
* 迴圈獲取例項
* */
for(Map.Entry<String, Object> entry: instanceMap.entrySet()) {
//獲取所有的類變數
Field[] fields = entry.getValue().getClass().getDeclaredFields();
//檢視上面有沒有qualifer的標識,如果有qualifer的標識,把它的value取出來,通過這個值我們就可以拿到它的例項
for(Field field: fields) {
//包含Qualifer註解
if(field.isAnnotationPresent(Qualifier.class)){
String key = ((Qualifier)field.getAnnotation(Qualifier.class)).value();
field.setAccessible(true);
field.set(entry.getValue(), instanceMap.get(key)); //注入qualifier----即把fishService注入進去了
}//autowired
}
}
}