手寫Spring Ioc框架
一、Spring體系結構
1、Spring是一個輕量級一站式企業開發解決方案。
Spring成員 | 描述 |
Spring framework | spring的基礎,包括ioc、aop及spring mvc、spring template等 |
Spring boot | 儘量減少框架本身的學習成本以及開發成本,讓開發人員更多的關注和開發業務程式碼;主要特點就是 純註解、零配置(無Spring配置檔案)、自動裝配、起步依賴 |
Spring Cloud | 分散式/微服務一站式開發框架 |
Spring data | 解決的是資料互動(mysql、redis、elasticsearch、mongodb、orm等) |
Spring security | 主要是針對登陸安全、許可權認證等 |
Spring oauth2 | 解決單點登入、使用者鑑權等問題 |
2、Spring framework的體系結構入下圖所示。
對每一個模組的描述如下表格:
模組 | 子模組 | 描述 |
核心容器 | Spring-Beans、Spring-Core |
1、這兩個模組是Spring的核心模組,包含了控制反轉了依賴注入; 2、其中BeanFactory介面是Spring框架中的核心介面,他是工廠模式的具體實現 3、BeanFactory使用控制反轉和依賴注入解耦了配置、依賴性規範與業務程式碼 4、BeanFactory容器例項化後並不會自動例項化Bean,而是等到Bean被使用時,BeanFactory才會對Bean進行例項化和依賴關係裝配 |
Spring-Context |
1、SpringContext模組架構於核心模組之上,他擴充套件了BeanFactory,為他添加了Bean生命週期控制、框架事件體系以及資源載入透明化等功能 2、ApplicationContext是該模組的核心介面,他是BeanFactory的超類 3、ApplicationContext容器例項化後會對所有的單例Bean進行例項化和依賴關係的裝配,使其處於可用狀態 |
|
Expression | Expression是統一表達式語言的擴充套件模組,可以查詢和管理執行中的物件 | |
AOP和裝置支援 | Spring-AOP | Spring-AOP是Spring的另一個核心模組,是AOP主要的實現模組 |
Spring-Aspects | Spring-Aspects模組整合自AspectJ,主要是為Spring-AOP提供多種AOP實現方案 | |
Spring-Instrument | Spring-Instrument模組應該是AOP的支援模組,主要作用是在JVM啟動時,生成一個代理類,開發人員可以通過代理類在執行時改變類的位元組,從而改變一個類的功能,從而實現AOP功能 | |
資料訪問與整合 | Spring-JDBC |
1、Spring-JDBC模組是Spring提供的JDBC抽象框架的主要實現模組,用於簡化SpringJDBC 2、Spring-JDBC主要提供模板方式、關係資料庫物件化方式、SimpleJdbc方式、事務管理來簡化JDBC程式設計 3、Spring-JDBC的主要實現類是JdbcTemplate、SimpleJdbcTemplate、NamedParameterJdbcTemplater |
資料訪問及整合 | Spring-TX | Spring-TX是SpringJDBC的事務控制實現模組。 |
Spring-ORM | Spring-ORM模組是ORM框架支援模組,主要整合Hibernate、JPA等用於資源管理、資料訪問的實現和事務策略 | |
Spring-JMS | Spring-JMS能夠傳送和介面資訊,從Spring4開始,還提供了Spring-Messaging模組的支撐 | |
Spring-OXM5 |
Spring-OXM模組主要提供一個抽象層以支撐OXM:將JAVA物件對映成XML資料,或者將XML資料對映成JAVA物件 |
|
Web | Spring-Web | Spring-Web為Spring提供了最基礎的Web支援,主要建立在核心容器之上,通過Servlet或者Listeners來初始化IOC容器,也包含一些與Web相關的支援 |
Spring-WebMVC | Spring-WebMVC是一個Web-Servlet模組,實現了SpringMVC的Web應用 | |
Spring-WebSocket | Spring-WebSocket主要是與Web前端的全雙工通訊的協議 | |
Spring-WebFlux | Spring-WebFlux是一個新的非阻塞函式式ReactiveWeb框架,可以用來建立非同步的,非阻塞,事件驅動的服務,並且擴充套件性非常好 | |
報文傳送 | Spring-Messaging | Spring-Messaging是從Spring4開始加入的新模組,主要職責是為Spring框架整合一些基礎的報文傳送應用 |
Test | Spring-Test | Spring-Test主要為測試提供支援。 |
二、手寫Spring IOC框架
手寫框架可以由簡入繁,一步步深入。
以 apis--->service--->dao為例,進行資料查詢,dao中需要注入datasource,service需要注入dao,apis需要注入service。
V1為直接手動賦值寫法,V2為使用了配置檔案載入的寫法,V3是借鑑了Spring的類體系後的寫法
1、V1版本(手動注入)
IOC主要是使用了建構函式或者是setter方法進行了依賴注入,那麼V1版本就先模擬依賴注入部分。
dao:
@Slf4j public class UserDaoImpl implements UserDao { private BasicDataSource dataSource; public UserDaoImpl(BasicDataSource dataSource){ this.dataSource = dataSource; } @Override public UserDo findUserById(String id) throws Exception { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; UserDo userDo = new UserDo(); try { //載入資料庫驅動 Class.forName(dataSource.getDriverClassName()); //獲取資料庫連線 connection = (Connection) DriverManager.getConnection(dataSource.getUrl(),dataSource.getUsername(),dataSource.getPassword()); //sql預處理 String sql = "select * from user where id = ?"; preparedStatement = (PreparedStatement) connection.prepareStatement(sql); //引數設定 preparedStatement.setString(1,id); //執行sql resultSet = preparedStatement.executeQuery(); //迴圈結果集 while (resultSet.next()){ userDo.setId(resultSet.getInt("id")); userDo.setUsername(resultSet.getString("username")); userDo.setAddress(resultSet.getString("address")); userDo.setSex(resultSet.getString("sex")); userDo.setBirthday(resultSet.getDate("birthday")); log.info("查詢到使用者資訊,id=【{}】,username=【{}】", resultSet.getString("id"), resultSet.getString("username")); } }catch (Exception e){ e.printStackTrace(); }finally { //釋放資源 if (resultSet != null){ try { resultSet.close(); } catch (SQLException e) { e.printStackTrace(); } } if (preparedStatement != null){ try { preparedStatement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null){ try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } return userDo; } }
service:
public class UserServiceImpl implements UserService { private UserDao userDao; @Override public UserDo findUserById(String id) throws Exception{ return userDao.findUserById(id); } public UserServiceImpl(UserDao userDao){ this.userDao = userDao; } }
apis:
@Slf4j public class UserApis { private UserService userService; public UserApis(UserService userService){ this.userService = userService; } public void findUserById(String id) throws Exception{ UserDo userDo = userService.findUserById(id); log.info("=================={}=================", userDo); } }
V1版本測試類:
@Test public void writeFrameV1() throws Exception { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://*********:3306/ins_buying_0"); dataSource.setUsername("*******"); dataSource.setPassword("********"); UserApis userApis = new UserApis(new UserServiceImpl(new UserDaoImpl(dataSource))); userApis.findUserById("2"); }
可以看到,直接將datasource注入到了UserDao,然後將UserDao注入到了UserService,將Userservice注入到了Userapis,最終完成呼叫,並在UserDao中使用datasource做了資料庫查詢操作。
2、V2版本(載入配置檔案)
在寫之前,先梳理一下思路及應該有哪些物件:
(1)應該有個spring的配置檔案,配置檔案中有bean標籤,標籤內有id、name、class、initMethod等屬性,同時可能存在屬性對應的property標籤(有可能多個),那麼這裡就使用一個BeanDefinition物件來封裝每一個Bean的配置資訊
(2)property標籤中存在name、ref、value屬性,其中value屬性表示的是簡單型別,ref表示的是引用型別,這裡我們建立一個PropertyValue物件,來封裝property標籤,同時使用TypeStringValue來封裝簡單型別,使用RuntimeBeanRefeerance來封裝引用型別
(3)對於載入流程,就是載入spring配置檔案進行解析,將所有的配置資訊放入一個map集合中
(4)對於執行流程,我們使用一個單例map儲存單例的bean,保證單例bean只有一個,因此使用bean時,從單例map中獲取,如果獲取不到,就建立bean,如果建立成功,且是單例bean,則將bean放入單例map中
(5)最重要的就是建立bean的步驟了,首先根據beanName從BeanDefinition中獲取配置資訊,然後使用反射獲取類物件,然後迴圈property集合,對集合中的每一個屬性進行賦值操作。
總體流程如上所示,那麼接下來就一步步實現:
(1)建立配置檔案及BeanDefinition物件
<beans> <bean id="userService" class="com.lcl.galaxy.spring.frame.service.UserServiceImpl"> <property name="userDao" ref="userDao"/> </bean> <bean id="userDao" class="com.lcl.galaxy.spring.frame.dao.UserDaoImpl"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://******:3306/ins_buying_0"/> <property name="username" value="*******"/> <property name="password" value="******"/> </bean> </beans>
package com.lcl.galaxy.spring.frame.domain; import lombok.Data; import java.util.ArrayList; import java.util.List; @Data public class MyBeanDefinition { private String clazzName; private String scope; private String beanName; private String initMethod; private List<MyPropertyValue> propertyValueList = new ArrayList<>(); private final String SCOPE_SINGLETON = "singleton"; private final String SCOPE_PROTOTYPE = "prototype"; public MyBeanDefinition(String clazzName, String beanName) { this.clazzName = clazzName; this.beanName = beanName; } public boolean isSingleton(){ if(SCOPE_SINGLETON.equals(scope)){ return true; } return false; } public boolean isPrototype(){ if(SCOPE_PROTOTYPE.equals(scope)){ return true; } return false; } public void addPropertyValue(MyPropertyValue pv) { propertyValueList.add(pv); } }
(2)property標籤中存在name、ref、value屬性,其中value屬性表示的是簡單型別,ref表示的是引用型別,這裡我們建立一個PropertyValue物件,來封裝property標籤,同時使用TypeStringValue來封裝簡單型別,使用RuntimeBeanRefeerance來封裝引用型別
package com.lcl.galaxy.spring.frame.domain; import lombok.Data; @Data public class MyPropertyValue { private String name; private Object typedStringValue; public MyPropertyValue(String name, Object typedStringValue) { this.name = name; this.typedStringValue = typedStringValue; } }
package com.lcl.galaxy.spring.frame.domain; import lombok.Data; @Data public class MyTypedStringValue { private String value; private Class<?> targetType; public MyTypedStringValue(String value) { this.value = value; } }
package com.lcl.galaxy.spring.frame.domain; import lombok.Data; @Data public class MyRuntimeBeanReference { private String ref; public MyRuntimeBeanReference(String ref) { this.ref = ref; } }
(3)對於載入流程,就是載入spring配置檔案進行解析,將所有的配置資訊放入一個map集合中
/** * 載入配置檔案 */ public void init() { String location = "write-frame-beans.xml"; InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(location); Document document = createDocument(inputStream); registerBeanDefinition(document.getRootElement()); } /** * 輸入流傳喚為Document * @param inputStream * @return */ private Document createDocument(InputStream inputStream) { Document document = null; SAXReader saxReader = new SAXReader(); try { document = saxReader.read(inputStream); } catch (Exception e) { e.printStackTrace(); } return document; } /** * 記載配置檔案 * @param rootElement */ private void registerBeanDefinition(Element rootElement) { List<Element> elements = rootElement.elements(); for (Element element : elements) { String name = element.getName(); if ("bean".equals(name)) { parseDefaultElement(element); } else { parseCustomElement(element); } } } /** * 解析自定義標籤 * @param element */ private void parseCustomElement(Element element) { } /** * 解析bean標籤,封裝為Bedefinition,並將BeanDefinition放入map中 * @param beanElement */ private void parseDefaultElement(Element beanElement) { if (beanElement == null) { return; } String id = beanElement.attributeValue("id"); String name = beanElement.attributeValue("name"); String clazzName = beanElement.attributeValue("class"); if (clazzName == null || "".equals(clazzName)) { return; } String initMethod = beanElement.attributeValue("initMethod"); String scope = beanElement.attributeValue("scope"); scope = scope != null ? scope : "singleton"; String beanName = id == null ? name : id; Class<?> clazzType = null; try { clazzType = Class.forName(clazzName); beanName = beanName == null ? clazzType.getName() : beanName; MyBeanDefinition beanDefinition = new MyBeanDefinition(clazzName, beanName); beanDefinition.setInitMethod(initMethod); beanDefinition.setScope(scope); List<Element> propertyElements = beanElement.elements(); for (Element propertyElement : propertyElements) { parsePropertyElement(beanDefinition, propertyElement); } beanDefinitions.put(beanName, beanDefinition); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
對property集合賦值
/** * 為BeanDefinition的property集合賦值 * @param beanDefinition * @param propertyElement */ private void parsePropertyElement(MyBeanDefinition beanDefinition, Element propertyElement) { String name = propertyElement.attributeValue("name"); String value = propertyElement.attributeValue("value"); String ref = propertyElement.attributeValue("ref"); if(value != null && !"".equals(value) && ref != null && !"".equals(ref)){ return; } MyPropertyValue pv = null; if(value != null && !"".equals(value)){ MyTypedStringValue typedStringValue = new MyTypedStringValue(value); Class<?> targetType = getTypeFieldName(beanDefinition.getClazzName(), name); typedStringValue.setTargetType(targetType); pv = new MyPropertyValue(name, typedStringValue); beanDefinition.addPropertyValue(pv); }else{ MyRuntimeBeanReference runtimeBeanReference = new MyRuntimeBeanReference(ref); pv = new MyPropertyValue(name, runtimeBeanReference); beanDefinition.addPropertyValue(pv); } } /** * 獲取簡單型別 * @param clazzName * @param name * @return */ private Class<?> getTypeFieldName(String clazzName, String name) { try { Class<?> clazz = Class.forName(clazzName); Field field = clazz.getDeclaredField(name); return field.getType(); } catch (Exception e) { log.info("================== clazzName,name ====================,{}==========,{}",clazzName, name); e.printStackTrace(); } return null; }
(4)對於執行流程,我們使用一個單例map儲存單例的bean,保證單例bean只有一個,因此使用bean時,從單例map中獲取,如果獲取不到,就建立bean,如果建立成功,且是單例bean,則將bean放入單例map中
public void findUserById(String id) throws Exception { UserService userService = (UserService) getBean("userService"); UserDo userDo = userService.findUserById(id); log.info("=================={}=================", userDo); } /** * 根據bean名稱獲取bean物件 * @param beanName * @return */ private Object getBean(String beanName) { Object object = singletonObjects.get(beanName); if (object != null) { return object; } MyBeanDefinition beanDefinition = beanDefinitions.get(beanName); if (beanDefinition == null || beanDefinition.getClazzName() == null) { return null; } if (beanDefinition.isSingleton()) { object = createBean(beanDefinition); this.singletonObjects.put(beanName, object); } else if (beanDefinition.isPrototype()) { object = createBean(beanDefinition); } return object; }
(5)最重要的就是建立bean的步驟了,首先根據beanName從BeanDefinition中獲取配置資訊,然後使用反射獲取類物件,然後迴圈property集合,對集合中的每一個屬性進行賦值操作。
/** * 建立bean * @param beanDefinition * @return */ private Object createBean(MyBeanDefinition beanDefinition) { String clazzName = beanDefinition.getClazzName(); try { Class<?> clazz = Class.forName(clazzName); if(clazz == null){ return null; } Constructor<?> constructor = clazz.getConstructor(); Object object = constructor.newInstance(); populateBean(object, beanDefinition); initMethod(); return object; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 處理初始化方法 */ private void initMethod() { } /** * 為物件賦值 * @param object * @param beanDefinition */ private void populateBean(Object object, MyBeanDefinition beanDefinition) { List<MyPropertyValue> propertyValueList = beanDefinition.getPropertyValueList(); for (MyPropertyValue propertyValue: propertyValueList) { String name = propertyValue.getName(); Object value = propertyValue.getTypedStringValue(); Object valueToUse = null; if(value instanceof MyTypedStringValue){ MyTypedStringValue myTypedStringValue = (MyTypedStringValue) value; String stringValue = myTypedStringValue.getValue(); Class<?> targetType = myTypedStringValue.getTargetType(); if(targetType == Integer.class){ valueToUse = Integer.parseInt(stringValue); }else if(targetType == String.class){ valueToUse = String.valueOf(stringValue); }else{ } }else if(value instanceof MyRuntimeBeanReference){ MyRuntimeBeanReference myRuntimeBeanReference = (MyRuntimeBeanReference) value; valueToUse = getBean(myRuntimeBeanReference.getRef()); } Class<?> clazz = object.getClass(); try { Field declaredField = clazz.getDeclaredField(name); declaredField.setAccessible(true); declaredField.set(object, valueToUse); } catch (Exception e) { e.printStackTrace(); } } }
可以看到,對於簡單型別的屬性,直接進行了賦值操作,對於引用型別,則使用了遞迴呼叫建立Bean物件,然後進行賦值操作
3、V3版本(仿照Spring原始碼類體系結構)
由於V3版本是借鑑SpringIOC原始碼編寫,那先來看一下Spring IOC中的一些核心概念及類的繼承體系。
基礎概念及對比
Spring容器:Spring容器就是IOC容器,底層是一個BeanFactory(使用簡單工廠實現),spring容器分為基礎容器(BeanFactory)和高階容器(ApplicationContext)
BeanFactory和ApplicationContext的區別:(1)ApplicationContext是繼承自BeanFactory介面;(2)BeanFactory建立Bean例項是在第一次獲取該Bean的例項時建立;(3)ApplicationContext建立Bean例項是在初始化的時候,一次性建立所有的單例bean例項。
BeanPostProcessor和BeanFactoryPostProcessor的區別:(1)BeanFactoryPostProcessor是在建立Bean物件前,對Bean的封裝資訊Beandefinition物件做處理;例如context:property標籤,可以載入指定路徑的properties檔案,同時將檔案中的key-value放入容器;也可將BeanDefinition中的${}佔位符進行替換(2)BeanPostProcessor是在物件例項化後,對Bean物件做後置處理。例如:AOP
BeanFactory和FactoryBean的區別:(1)BeanFactory是Spring的基礎容器,管理著spring中需要管理的所有Bean。(2)FactoryBean只是spring容器管理的bean的一員,只不過該bean物件比較特殊,可以產生一些指定型別的bean。
繼承體系
如上圖所示,是BeanFactory的類體系結構(這裡沒有包含AppllicationContext體系)可以將其描述為四級介面繼承體系。
針對以上介面的大致內容描述如下:
介面級別 | 描述 | BeanFactory介面 | 作用 |
一級介面 | Beanfactory作為主介面,不繼承任何介面,可以稱為一級介面 | BeanFactory | 是Spring Bean的根介面,提供獲取bean、是否包含bean、是否單例、是否原型、獲取bean型別獲取bean等功能介面 |
二級介面 | AutowireCapableBeanFactory、HierarchicalBeanFactory、ListableBeanFactory三個子介面繼承了BeanFactory,可以稱為二級介面 | AutowireCapableBeanFactory | 提供工廠的裝配功能 |
ListableBeanFactory | 提供容器內bean例項的列舉功能,這邊不會考慮父容器內的例項 | ||
HierarchicalBeanFactory | 提供父容器的訪問功能 | ||
三級介面 | ConfigurableBeanFactory繼承了二級介面HierarchicalBeanFactory,同時和繼承了SingletonBeanRegistry介面 | ConfigurableBeanFactory | 提供factory的配置功能 |
四級介面 | ConfigurableListableBeanFactory是一個更強大的介面,繼承了以上所有的介面。 | ConfigurableListAbleBeanFactory | 集大成者,提供解析bean、修改bean定義、初始化單例等功能 |
根據上述內容,可以暫定一下手寫Ioc框架的類劃分
1、載入主入口&呼叫主入口
2、BeanFactory體系:用於載入Bendefinition資訊和建立Bean物件
3、reader:用於讀取xml檔案或Document檔案
4、registory:用於註冊BeanDefinition資訊和單例Bean資訊
5、utils:用於處理Dodument物件或通過反射操作一些內容
6、resource:載入配置檔案
7、aware
其中utils和resource屬於工具類範疇,在最後會附上程式碼。
其實邏輯程式碼跟V2是一樣的,主要是對介面、類、方法進行了封裝,最主要的就是BeanFactory體系,仿照Spring原始碼中的BeanFactory體系,我也建立了一些體系,在說BeanFactory之前,先說一下registory的介面和類:
registory
我在程式碼中提供了兩個registory介面MyBeanDefinitonRegistory和MySingletonRegistory,分別用來註冊BeanDefinition和單例Bean物件
其中MyBeanDefinitionRegistory介面提供了BeanDefinition註冊、根據beanName獲取BeanDefinition物件兩個方法
public interface MyBeanDefinitionRegisty { void registry(String beanName, MyBeanDefinition beanDefinition); MyBeanDefinition getBeanDefinition(String beanName); }
MySingletonRegistory提供了根據beanName獲取Bean例項物件和新增Bean例項物件兩個方法
package com.lcl.galaxy.spring.frame.V3.register; public interface MySingletonRegistry { Object getSingletonBean(String beanName); void addSingleton(String beanName, Object object); }
為MySingletonRegistor提供了一個預設實現類MyDefaultSingletonBeanFactory,用來向單例Bean的map集合中新增單例Bean或獲取單例Bean
public class MyDefaultSingletonBeanRegistory implements MySingletonRegistry { private Map<String, Object> singletonObjects = new HashMap<>(); @Override public Object getSingletonBean(String beanName) { return singletonObjects.get(beanName); } @Override public void addSingleton(String beanName, Object object) { singletonObjects.put(beanName, object); } }
BeanFactory
說完registry,然後重點說一下BeanFactory類,仿照Spring原始碼中的BeanFactory體系建立的BeanFactory體系如下:
首先,建立了一個根介面MyBeanFactory,該介面只提供根據BeanName獲取Bean物件
public interface MyBeanFactory { Object getBean(String beanName); }
建立了兩個二級介面MyAutowireCapableBeanFactory和MyListableBeanFactory,其中MyAutowireCapableBeanFactory提供了建立bean物件的方法,而MyListableBeanFactory則提供了根據指定型別獲取所有bean名稱集合方法和根據型別所有Bean物件集合方法
public interface MyAutowireCapableBeanFactory extends MyBeanFactory { Object createBean(String beanName, MyBeanDefinition myBeanDefinition); }
public interface MyListableBeanFactory extends MyBeanFactory { List<String> getBeanNamesByType(Class<?> type); <T> List<T> getBeansByType(Class<?> type); }
然後建立介面的實現類,首先建立一個頂層的抽象類MyAbstructBeanFactory,其實現MyListableBeanFactory介面,同時整合上述的MyDefualtSingletonRegistory類,在該類中去實現getBean方法。
在該類的getBean方法中,主要做了四步:單例bean是否已存在、獲取bean對應的BeanDefinition物件、根據BeanDefinition建立Bean例項、將Bean例項放入單例Bean的map中
由於該類繼承了MyDefualtSingletonRegistory,因此也擁有了單例Bean的註冊和獲取方法,因此對於上面說的四步中,第一步和第四步直接可以呼叫父類的方法完成操作。
那麼在該方法中,還有第二步獲取Bendefiniton物件和建立Bean例項兩個步驟,具體如何建立,交由子類處理,此處只使用抽象的方法做了封裝。(從這裡可以看到,具體每個類做哪些事情,還是劃分的比較清楚的)
public abstract class MyAbstractBeanFactory extends MyDefaultSingletonBeanRegistory implements MyListableBeanFactory { @Override public Object getBean(String beanName) { Object bean = getSingletonBean(beanName); if(bean != null){ return bean; } MyBeanDefinition beanDefinition = getBeanDefinition(beanName); if(beanDefinition == null){ return null; } if(beanDefinition.isSingleton()){ bean = createBean(beanName, beanDefinition); addSingleton(beanName, beanDefinition); }else if(beanDefinition.isPrototype()){ bean = createBean(beanName, beanDefinition); } return bean; } public abstract Object createBean(String beanName, MyBeanDefinition beanDefinition); public abstract MyBeanDefinition getBeanDefinition(String beanName); }
然後建立負責建立物件的BeanFactory:MyAbstructAutowireCapableBeanFactory,該類實現MyAutowireCapableBeanFactory介面,同時整合上一步的MyAbstructBeanFactory類,由於MyAbstructBeanFactory中有抽象的建立bean例項的方法,而MyAutowireCapableBeanFactory介面中也存在該方法,因此在MyAbstructAutowireCapableBeanFactory中實現該方法,具體實現內容其實和V2版本一致,具體就不再描述了,直接上程式碼:
public abstract class MyAbstructAutowireCapableBeanFactory extends MyAbstractBeanFactory implements MyAutowireCapableBeanFactory { @Override public Object createBean(String beanName, MyBeanDefinition beanDefinition) { Class<?> clazz = getResolvedClass(beanDefinition); Object object = createInstance(clazz); populateBean(object, beanDefinition); initalBean(object, beanDefinition); return object; } /** * bean的初始化 * @param object * @param beanDefinition */ private void initalBean(Object object, MyBeanDefinition beanDefinition) { //aware介面處理 if(object instanceof MyAware){ if(object instanceof MyBeanFactoryAware){ ((MyBeanFactoryAware)object).setBeanFactory(this); } } //對實現了IniallizingBean介面的類,呼叫他的afterProperties方法 //如果Bean中init-method屬性有值,則呼叫指定的方法 initMethod(object, beanDefinition); } private void initMethod(Object object, MyBeanDefinition beanDefinition) { String method = beanDefinition.getInitMethod(); if(method != null && !"".equals(method)){ ReflectUtils.invokeMethod(object, method); } } private Object createInstance(Class<?> clazz){ //如果有例項工廠,則通過例項工廠建立Bean例項 //如果有工廠方法,則通過工廠方法建立Bean例項 //如果都沒有,則使用建構函式建立Bean例項 return ReflectUtils.createObject(clazz); } private Class<?> getResolvedClass(MyBeanDefinition beanDefinition){ String clazzName = beanDefinition.getClazzName(); try { Class<?> clazz = Class.forName(clazzName); return clazz; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 為物件賦值 * @param object * @param beanDefinition */ private void populateBean(Object object, MyBeanDefinition beanDefinition) { List<MyPropertyValue> propertyValueList = beanDefinition.getPropertyValueList(); for (MyPropertyValue propertyValue: propertyValueList) { String name = propertyValue.getName(); Object value = propertyValue.getTypedStringValue(); Object valueToUse = null; if(value instanceof MyTypedStringValue){ MyTypedStringValue myTypedStringValue = (MyTypedStringValue) value; String stringValue = myTypedStringValue.getValue(); Class<?> targetType = myTypedStringValue.getTargetType(); if(targetType == Integer.class){ valueToUse = Integer.parseInt(stringValue); }else if(targetType == String.class){ valueToUse = String.valueOf(stringValue); }else{ } }else if(value instanceof MyRuntimeBeanReference){ MyRuntimeBeanReference myRuntimeBeanReference = (MyRuntimeBeanReference) value; valueToUse = getBean(myRuntimeBeanReference.getRef()); } ReflectUtils.setProperty(object, name, valueToUse); } } }
最後一個建立的就是功能的集大成者:MyDefaultListableBeanFactory,其實現MyBeanDefinitionRegistory介面,同時整合上一個類MyAbstructAutowireCapableBeanFactory,由於繼承了MyBeanDefinitionRegistory介面,因此就需要實現對應的Beandefinition的註冊和獲取方法。
public class MyDefaultListableBeanFactory extends MyAbstructAutowireCapableBeanFactory implements MyBeanDefinitionRegisty { private Map<String, MyBeanDefinition> beanDefinitions = new HashedMap(); @Override public void registry(String beanName, MyBeanDefinition beanDefinition) { beanDefinitions.put(beanName, beanDefinition); } @Override public MyBeanDefinition getBeanDefinition(String beanName) { return beanDefinitions.get(beanName); } @Override public List<String> getBeanNamesByType(Class<?> type) { return null; } @Override public <T> List<T> getBeansByType(Class<?> type) { return null; } }
整體碼完。。。。。。。
附錄、工具類程式碼附錄
接下來,附上對應的工具類內容
1、主入口:
@Slf4j @Data public class SpringIocFrame { public MyDefaultListableBeanFactory init(){ String location = "write-frame-beans.xml"; //載入配置檔案 MyResources resources = new MyClassPathResource(location); //建立BeanFactory MyDefaultListableBeanFactory beanFactory = new MyDefaultListableBeanFactory(); // MyXmlBeanDefinitionReader reader = new MyXmlBeanDefinitionReader(beanFactory); reader.loadBeanDefinitions(resources); return beanFactory; } public void findUserById(String id, MyDefaultListableBeanFactory beanFactory) throws Exception { UserService userService = (UserService) beanFactory.getBean("userService"); UserDo userDo = userService.findUserById(id); log.info("=================={}=================", userDo); } }
2、reader:
package com.lcl.galaxy.spring.frame.V3.reader; import com.lcl.galaxy.spring.frame.V2.domain.MyBeanDefinition; import com.lcl.galaxy.spring.frame.V2.domain.MyPropertyValue; import com.lcl.galaxy.spring.frame.V2.domain.MyRuntimeBeanReference; import com.lcl.galaxy.spring.frame.V2.domain.MyTypedStringValue; import com.lcl.galaxy.spring.frame.V3.register.MyBeanDefinitionRegisty; import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; import java.lang.reflect.Field; import java.util.List; @Slf4j public class MyXmlBeanDefinitionDocumentReader { private MyBeanDefinitionRegisty registy; public MyXmlBeanDefinitionDocumentReader(MyBeanDefinitionRegisty registy) { super(); this.registy = registy; } /** * 按照spring標籤語義進行解析 * @param rootElement */ public void loadBeanDefinitions(Element rootElement) { List<Element> elements = rootElement.elements(); for (Element element : elements) { String name = element.getName(); if ("bean".equals(name)) { parseDefaultElement(element); } else { parseCustomElement(element); } } } /** * 解析自定義標籤 * @param element */ private void parseCustomElement(Element element) { } /** * 解析bean標籤,封裝為Bedefinition,並將BeanDefinition放入map中 * @param beanElement */ private void parseDefaultElement(Element beanElement) { if (beanElement == null) { return; } String id = beanElement.attributeValue("id"); String name = beanElement.attributeValue("name"); String clazzName = beanElement.attributeValue("class"); if (clazzName == null || "".equals(clazzName)) { return; } String initMethod = beanElement.attributeValue("initMethod"); String scope = beanElement.attributeValue("scope"); scope = scope != null ? scope : "singleton"; String beanName = id == null ? name : id; Class<?> clazzType = null; try { clazzType = Class.forName(clazzName); beanName = beanName == null ? clazzType.getName() : beanName; MyBeanDefinition beanDefinition = new MyBeanDefinition(clazzName, beanName); beanDefinition.setInitMethod(initMethod); beanDefinition.setScope(scope); List<Element> propertyElements = beanElement.elements(); for (Element propertyElement : propertyElements) { parsePropertyElement(beanDefinition, propertyElement); } this.registy.registry(beanName, beanDefinition); } catch (ClassNotFoundException e) { e.printStackTrace(); } } /** * 為BeanDefinition的property集合賦值 * @param beanDefinition * @param propertyElement */ private void parsePropertyElement(MyBeanDefinition beanDefinition, Element propertyElement) { String name = propertyElement.attributeValue("name"); String value = propertyElement.attributeValue("value"); String ref = propertyElement.attributeValue("ref"); if(value != null && !"".equals(value) && ref != null && !"".equals(ref)){ return; } MyPropertyValue pv = null; if(value != null && !"".equals(value)){ MyTypedStringValue typedStringValue = new MyTypedStringValue(value); Class<?> targetType = getTypeFieldName(beanDefinition.getClazzName(), name); typedStringValue.setTargetType(targetType); pv = new MyPropertyValue(name, typedStringValue); beanDefinition.addPropertyValue(pv); }else{ MyRuntimeBeanReference runtimeBeanReference = new MyRuntimeBeanReference(ref); pv = new MyPropertyValue(name, runtimeBeanReference); beanDefinition.addPropertyValue(pv); } } /** * 獲取簡單型別 * @param clazzName * @param name * @return */ private Class<?> getTypeFieldName(String clazzName, String name) { try { Class<?> clazz = Class.forName(clazzName); Field field = clazz.getDeclaredField(name); return field.getType(); } catch (Exception e) { log.info("================== clazzName,name ====================,{}==========,{}",clazzName, name); e.printStackTrace(); } return null; } }
package com.lcl.galaxy.spring.frame.V3.reader; import com.lcl.galaxy.spring.frame.V3.register.MyBeanDefinitionRegisty; import com.lcl.galaxy.spring.frame.V3.resource.MyResources; import com.lcl.galaxy.spring.frame.V3.utils.DocumentUtils; import org.dom4j.Document; import java.io.InputStream; public class MyXmlBeanDefinitionReader { private MyBeanDefinitionRegisty registy; public MyXmlBeanDefinitionReader(MyBeanDefinitionRegisty registy) { this.registy = registy; } public void loadBeanDefinitions(MyResources resources) { InputStream inputStream = resources.getResourceAsStream(); Document document = DocumentUtils.readInputStream(inputStream); MyXmlBeanDefinitionDocumentReader xmlBeanDefinitionDocumentReader = new MyXmlBeanDefinitionDocumentReader(registy); xmlBeanDefinitionDocumentReader.loadBeanDefinitions(document.getRootElement()); } }
3、resource
public class MyClassPathResource implements MyResources { private String location; public MyClassPathResource(String location){ this.location = location; } @Override public InputStream getResourceAsStream() { return MyResources.class.getClassLoader().getResourceAsStream(location); } }
public interface MyResources { InputStream getResourceAsStream(); }
4、utils
public class DocumentUtils { public static Document readInputStream(InputStream inputStream) { SAXReader saxReader = new SAXReader(); try { Document document = saxReader.read(inputStream); return document; } catch (DocumentException e) { e.printStackTrace(); } return null; } }
public class ReflectUtils { /** * 通過反射獲取物件 * @param args * @return */ public static Object createObject(Class<?> clazz, Object... args){ try { Constructor<?> constructor = clazz.getConstructor(); return constructor.newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 通過反射設定屬性值 * @param object * @param name * @param valueToUse */ public static void setProperty(Object object, String name, Object valueToUse){ Class<?> clazz = object.getClass(); try { Field field = clazz.getDeclaredField(name); field.setAccessible(true); field.set(object, valueToUse); } catch (Exception e) { e.printStackTrace(); } } /** * 通過反射獲取欄位的型別 * @param beanClassName * @param name * @return */ public static Class<?> getTypeByFieldName(String beanClassName, String name){ try { Class<?> clazz = Class.forName(beanClassName); Field field = clazz.getDeclaredField(name); return field.getType(); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 通過反射執行init方法 * @param object * @param initMethod */ public static void invokeMethod(Object object, String initMethod){ Class<?> clazz = object.getClass(); try { Method declaredMethod = clazz.getDeclaredMethod(initMethod); declaredMethod.setAccessible(true); declaredMethod.invoke(initMethod); } catch (Exception e) { e.printStackTrace(); } } }
三、總結
對於IoC框架的手寫,其實V2版本已經實現了功能,V3版本只是借鑑Spring原始碼對V2版本進行了梳理,程式碼邏輯沒有變化,只是對類的整合體系、設計模式等做了相應的調整,其實手寫完V3版本,就已經大致瞭解Spring原始碼中相關類的繼承體系,對於啃Spring Ioc的原始碼有很大的幫助,