上一篇文章裏咱們已經對 Spring 的源碼有了一個大概的認識,對於 Spring 中的一些組件,好比 Aware,PostProcessor,ApplicationContext 有了必定的認識,下面咱們就來手動實現一個 IOC/DI 容器。java
項目總體用 maven 構建,裏面有兩個模塊,MySpring 爲 IOC/DI 的核心,Demo 爲測試項目。git
先來看看總體的項目結構,目前爲第一個版本,好多須要完善的地方。最近好忙。
github
首先咱們把幾個重要的註解定義出來。
@Autowired,自動注入註解,用來實現 DI 功能。
@Controller,控制層註解,暫時不實現 SpringMVC 相關的功能。後續分析完 SpringMVC 源碼後會實現。
@Service,服務層註解,與 Spring 中的 @Component 做用相同,就是將這個 Bean 交給 Spring 來管理。
@Repository,持久層註解,將這個 Bean 交給 Spring 來管理。
暫時先定義這幾個註解。spring
這幾個註解的定義與代碼都是同樣的,就是將註解定義出來。緩存
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Service { String value() default ""; }
首先咱們也定義一個 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); }
定義一個 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(); } }
在看容器的最終實現類以前,咱們先把 xmlUtil 和 配置文件的結構給你們看一下。
xmlUtil 的做用就是解析配置文件得到類的所描路徑。app
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>
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(); } }
在 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(); } }
測試結果:maven
// 容器中的 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)。
項目源碼