Spring 源碼解析(二)

  • 第一章爲源碼解析。
  • 第二章爲實現一個簡單的 IOC 容器。
  • 第三章進階 Spring 插件開發。

手動實現一個 IOC/DI 容器

上一篇文章裏咱們已經對 Spring 的源碼有了一個大概的認識,對於 Spring 中的一些組件,好比 Aware,PostProcessor,ApplicationContext 有了必定的認識,下面咱們就來手動實現一個 IOC/DI 容器。php

項目總體用 maven 構建,裏面有兩個模塊,MySpring 爲 IOC/DI 的核心,Demo 爲測試項目。java

  1. 先來看看總體的項目結構,目前爲第一個版本,好多須要完善的地方。最近好忙。 git

  2. 首先咱們把幾個重要的註解定義出來。 @Autowired,自動注入註解,用來實現 DI 功能。 @Controller,控制層註解,暫時不實現 SpringMVC 相關的功能。後續分析完 SpringMVC 源碼後會實現。 @Service,服務層註解,與 Spring 中的 @Component 做用相同,就是將這個 Bean 交給 Spring 來管理。 @Repository,持久層註解,將這個 Bean 交給 Spring 來管理。 暫時先定義這幾個註解。github

    這幾個註解的定義與代碼都是同樣的,就是將註解定義出來。spring

    @Target(ElementType.TYPE)
     @Retention(RetentionPolicy.RUNTIME)
     public @interface Service {
     
         String value() default "";
     }
    複製代碼
  3. 首先咱們也定義一個 BeanFactory 工廠方法做爲最上層的容器。裏面主要有一個 getBean 方法用來從容器中獲取 Bean,固然這裏面已經包含了 Bean
    的實例化過程。getBean 方法調用抽象的 doGetBean 方法,最後交給子類實現。緩存

    package org.springframework.ioc.factory;
     
     /** * 容器對象的工廠類,生產容器對象 * */
     public abstract class BeanFactory {
     
         public Object getBean(String beanName){
             return doGetBean(beanName);
         }
     
         protected abstract Object doGetBean(String beanName);
     
     }    
    複製代碼
  4. 定義一個 ApplicationContext 繼承 BeanFactory,在裏面添加 xml
    處理工具類和包的掃描路徑。這個類也是一個抽象類。裏面包含兩個實例參數,配置文件路徑和 xml 處理工具。安全

    package org.paul.springframework.ioc.bean;
     
     import org.paul.springframework.ioc.factory.BeanFactory;
     import org.paul.springframework.ioc.xml.XmlUtil;
     
     public abstract class ApplicationContext extends BeanFactory {
     
         protected String configLocation;
         protected XmlUtil xmlUtil = null;
     
         public ApplicationContext(){
         }
     
         public ApplicationContext(String configLocations){
             this.configLocation = configLocations;
             xmlUtil = new XmlUtil();
         }
     
     }
    複製代碼
  5. 在看容器的最終實現類以前,咱們先把 xmlUtil 和 配置文件的結構給你們看一下。 xmlUtil 的做用就是解析配置文件得到類的所描路徑。架構

    package org.springframework.ioc.xml;
     
     import org.dom4j.Document;
     import org.dom4j.DocumentException;
     import org.dom4j.Element;
     import org.dom4j.io.SAXReader;
     import java.io.InputStream;
     
     /** * 解析容器的配置文件中掃描包的路徑 * */
     public class XmlUtil {
         public String handlerXMLForScanPackage(String configuration){
             InputStream ins = this.getClass().getClassLoader().getResourceAsStream(configuration);
             SAXReader reader = new SAXReader();
             try{
                 Document document = reader.read(ins);
                 Element root = document.getRootElement();
                 Element element = root.element("package-scan");
                 String res = element.attributeValue("component-scan");
                 return res;
             }catch (DocumentException e){
                 e.printStackTrace();
             }
             return null;
         }
     }
    
    複製代碼
    <?xml version="1.0" encoding="UTF-8" ?>
     <beans>
         <package-scan component-scan="com.spring.demo" />
     </beans>
    複製代碼
  6. 容器的最終實現類,基於註解的 xml 掃描容器配置類。app

    • 首先定義兩個線程安全的 List 和 一個 ConcurrentHashMap,分別用來保存掃描 類的路徑,類和實例對象。
    • 在 AnnotationApplicationContext 的構造函數裏,分別實現瞭如下的功能。 調用父類的初始化方法,將 xml 工具實例化。 使用 xmlUtil 和 配置文件路徑獲取到掃描的包路徑。 獲取到包路徑後,執行包的掃描操做。 將裏面有對應註解的 Bean 注入到容器中。 將對象建立出來,先忽略依賴關係。 執行容器實例管理和對象運行期間的依賴裝配。
    package org.springframework.ioc.bean;
     
     import java.io.File;
     import java.io.FileFilter;
     import java.lang.reflect.Field;
     import java.net.URISyntaxException;
     import java.net.URL;
     import java.util.ArrayList;
     import java.util.Collections;
     import java.util.List;
     import java.util.Map;
     import java.util.Map.Entry;
     import java.util.concurrent.ConcurrentHashMap;
     
     import org.springframework.ioc.annotation.Autowired;
     import org.springframework.ioc.annotation.Component;
     import org.springframework.ioc.annotation.Controller;
     import org.springframework.ioc.annotation.Repository;
     import org.springframework.ioc.annotation.Service;
     
     public class AnnotationApplicationContext extends ApplicationContext {
     	
     	//保存類路徑的緩存
     	private static List<String> classCache = Collections.synchronizedList(new ArrayList<String>());
     	
     	//保存須要注入的類的緩存
     	private static List<Class<?>> beanDefinition = Collections.synchronizedList(new ArrayList<Class<?>>());
     
     	//保存類實例的容器
     	private static Map<String,Object> beanFactory = new ConcurrentHashMap<>();
     	
     	public AnnotationApplicationContext(String configuration) {
     		super(configuration);
     		String path  = xmlUtil.handlerXMLForScanPackage(configuration);
             System.out.println(path);
             
             //執行包的掃描操做
             scanPackage(path);
             //註冊bean
             registerBean();
             //把對象建立出來,忽略依賴關係
             doCreateBean();
             //執行容器管理實例對象運行期間的依賴裝配
             diBean();
     	}
     
     
     
     	@Override
     	protected Object doGetBean(String beanName) {
     		return beanFactory.get(beanName);
     	}
     	
     	/** * 掃描包下面全部的 .class 文件的類路徑到上面的List中 * */
     	private void scanPackage(final String path) {
     		URL url = this.getClass().getClassLoader().getResource(path.replaceAll("\\.", "/"));
     		try {
     			File file = new File(url.toURI());
     			
     			file.listFiles(new FileFilter(){
     				//pathname 表示當前目錄下的全部文件
     				@Override
     				public boolean accept(File pathname) {
     					//遞歸查找文件
     					if(pathname.isDirectory()){
     						scanPackage(path+"."+pathname.getName());
     					}else{
     						if(pathname.getName().endsWith(".class")){
                                 String classPath = path + "." + pathname.getName().replace(".class","");
                                 classCache.add(classPath);
     						}
     					}
     					return true;
     				}
     				
     			});
     		} catch (URISyntaxException e) {
     			e.printStackTrace();
     		}
     		
     		
     	}
     	
     
     	/** * 根據類路徑得到 class 對象 */
     	private void registerBean() {
     		if(classCache.isEmpty()){
     			return;
     		}
     		
     		
     		for(String path:classCache){
     			try {
     				//使用反射,經過類路徑獲取class 對象
     				Class<?> clazz = Class.forName(path);
     				//找出須要被容器管理的類,好比,@Component,@Controller,@Service,@Repository
     				if(clazz.isAnnotationPresent(Repository.class)||clazz.isAnnotationPresent(Service.class)
     						||clazz.isAnnotationPresent(Controller.class)|| clazz.isAnnotationPresent(Component.class)){
     					beanDefinition.add(clazz);
     				}
     			} catch (ClassNotFoundException e) {
     				// TODO Auto-generated catch block
     				e.printStackTrace();
     			}
     		}
     		
     	}
     
     	
     	/** * * 根據類對象,建立實例 */
     	private void doCreateBean() {
     		if(beanDefinition.isEmpty()){
     			return;
     		}
     		
     
     		for(Class clazz:beanDefinition){
     			try {
     				Object instance = clazz.newInstance();
     				//將首字母小寫的類名做爲默認的 bean 的名字
     				String aliasName = lowerClass(clazz.getSimpleName());
     				//先判斷@ 註解裏面是否給了 Bean 名字,有的話,這個就做爲 Bean 的名字
     				if(clazz.isAnnotationPresent(Repository.class)){
     					Repository repository = (org.springframework.ioc.annotation.Repository) clazz.getAnnotation(Repository.class);
     					if(!"".equals(repository.value())){
     						aliasName = repository.value();
     					}
     				}	
     				if(clazz.isAnnotationPresent(Service.class)){
     					Service service = (org.springframework.ioc.annotation.Service) clazz.getAnnotation(Service.class);
     					if(!"".equals(service.value())){
     						aliasName = service.value();
     					}
     				}
     				if(clazz.isAnnotationPresent(Controller.class)){
     					Controller controller = (org.springframework.ioc.annotation.Controller) clazz.getAnnotation(Controller.class);
     					if(!"".equals(controller.value())){
     						aliasName = controller.value();
     					}
     				}
     				if(clazz.isAnnotationPresent(Component.class)){
     					Component component = (org.springframework.ioc.annotation.Component) clazz.getAnnotation(Component.class);
     					if(!"".equals(component.value())){
     						aliasName = component.value();
     					}
     				}
     				if(beanFactory.get(aliasName)== null){
     					beanFactory.put(aliasName, instance);
     				}
     				
     				//判斷當前類是否實現了接口
     				Class<?>[] interfaces = clazz.getInterfaces();
     				if(interfaces == null){
     					continue;
     				}
     				//把當前接口的路徑做爲key存儲到容器中
     				for(Class<?> interf:interfaces){
     					beanFactory.put(interf.getName(), instance);
     				}
     				
     
     				
     			} catch (InstantiationException e) {
     				e.printStackTrace();
     			} catch (IllegalAccessException e) {
     				e.printStackTrace();
     			}
     		}
     		
     		
     		for (Entry<String, Object> entry : beanFactory.entrySet()) { 
     			  System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); 
     			}
     		
     		
     	}
     	
     	/** * 對建立好的對象進行依賴注入 */
     	private void diBean() {
     		if(beanFactory.isEmpty()){
     			return;
     		}
     		
     		for(Class<?> clazz:beanDefinition){
     			String aliasName = lowerClass(clazz.getSimpleName());
     			//先判斷@ 註解裏面是否給了 Bean 名字,有的話,這個就做爲 Bean 的名字
     			if(clazz.isAnnotationPresent(Repository.class)){
     				Repository repository = (org.springframework.ioc.annotation.Repository) clazz.getAnnotation(Repository.class);
     				if(!"".equals(repository.value())){
     					aliasName = repository.value();
     				}
     			}	
     			if(clazz.isAnnotationPresent(Service.class)){
     				Service service = (org.springframework.ioc.annotation.Service) clazz.getAnnotation(Service.class);
     				if(!"".equals(service.value())){
     					aliasName = service.value();
     				}
     			}
     			if(clazz.isAnnotationPresent(Controller.class)){
     				Controller controller = (org.springframework.ioc.annotation.Controller) clazz.getAnnotation(Controller.class);
     				if(!"".equals(controller.value())){
     					aliasName = controller.value();
     				}
     			}
     			if(clazz.isAnnotationPresent(Component.class)){
     				Component component = (org.springframework.ioc.annotation.Component) clazz.getAnnotation(Component.class);
     				if(!"".equals(component.value())){
     					aliasName = component.value();
     				}
     			}
     			
     			//根據別名獲取到被裝配的 bean 的實例
     			Object instance = beanFactory.get(aliasName);
     			try{
     				//從類中獲取參數,判斷是否有 @Autowired 註解
     				Field[] fields = clazz.getDeclaredFields();
     				for(Field f:fields){
     					if(f.isAnnotationPresent(Autowired.class)){
     						System.out.println("12312312312123");
     						//開啓字段的訪問權限
     						f.setAccessible(true);
     						Autowired autoWired = f.getAnnotation(Autowired.class);
     						if(!"".equals(autoWired.value())){
     							System.out.println("111111111111111111111");
     							//註解裏寫了別名
     							f.set(instance, beanFactory.get(autoWired.value()));
     							
     						}else{
     							//按類型名稱
     							String fieldName = f.getType().getName();
     							f.set(instance, beanFactory.get(fieldName));
     						}
     					}
     				}
     			}catch(Exception e){
     				e.printStackTrace();
     			}
     			
     		}
     		
     	}
     
     
     	private String lowerClass(String simpleName) {
             char[] chars = simpleName.toCharArray();
             chars[0] += 32;
             return chars.toString();
     	}
     
     }
    
    
    
    複製代碼
  7. 在 Demo 模塊中定義相似與 SpringMVC 的三層架構,並在 Service 層注入 Dao,在 Dao 層咱們只打印了一句話,爲了驗證 DI 成功。dom

    package org.Demo;
    
     import static org.junit.Assert.assertTrue;
     
     import org.junit.Test;
     import org.springframework.ioc.bean.AnnotationApplicationContext;
     import org.springframework.ioc.bean.ApplicationContext;
     
     import com.spring.demo.Service.BookService;
     
     /** * Unit test for simple App. */
     public class AppTest {
     	public static void main(String[] args){
     
     		ApplicationContext ctx = new AnnotationApplicationContext("applicationContext.xml");
     		BookService bookService = (BookService) ctx.getBean("bookService");
     		bookService.action();
     	}
     }
    
    複製代碼
  8. 測試結果:

    // 容器中的 Bean
     Key = bookDao, Value = com.spring.demo.Dao.BookDaoImpl@3d494fbf
     Key = [C@4fca772d, Value = com.spring.demo.controller.BookController@1ddc4ec2
     Key = bookService, Value = com.spring.demo.Service.BookServiceImpl@133314b
     Key = com.spring.demo.Dao.BookDao, Value = com.spring.demo.Dao.BookDaoImpl@3d494fbf
     Key = com.spring.demo.Service.BookService, Value = com.spring.demo.Service.BookServiceImpl@133314b
     
     // service 調用 dao 層的方法,成功打印。
     我在讀書
    複製代碼

    這樣一個 IOC/DI 容器就構建成功了,整個項目源碼在 github,但願你們 star 一下,一塊兒改進(多提 pull request)。 項目源碼

相關文章
相關標籤/搜索