若是你有幸能看到java
tiny-spring
是爲了學習Spring的而開發的,能夠認爲是一個Spring的精簡版。Spring的代碼不少,層次複雜,閱讀起來費勁。我嘗試從使用功能的角度出發,參考Spring的實現,一步一步構建,最終完成一個精簡版的Spring。 有人把程序員與畫家作比較,畫家有門基本功叫臨摹,tiny-spring能夠算是一個程序的臨摹版本-從本身的需求出發,進行程序設計,同時對著名項目進行參考。node
早看到也不浪費時間了,唉。git
一、時序圖-普通bean的加載過程程序員
二、加載資源的主要相關類github
三、從xml中加載bean定義的主要相關類spring
四、裝配bean的主要相關類編程
五、實現ApplicationContext接口的相關類app
IoC最基本的角色有兩個:容器(BeanFactory)和Bean自己。這裏使用BeanDefinition來封裝了bean對象,這樣能夠保存一些額外的元信息。測試代碼:框架
// 1.初始化beanfactory
BeanFactory beanFactory = new BeanFactory();
// 2.注入bean
BeanDefinition beanDefinition = new BeanDefinition(new HelloWorldService());
beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);
// 3.獲取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
複製代碼
一、首先咱們來看下總體代碼結構,這裏的要點是BeanFactory接口。咱們應該面向接口編程。ide
/** * Created by guo on 3/1/2018. * bean的容器,工廠 */
public interface BeanFactory {
Object getBean(String name) throws Exception;
}
/** * Created by guo on 3/1/2018. * 抽象bean工廠 */
public abstract class AbstractBeanFactory implements BeanFactory {
}
``
2、你們想知道AbstractBeanFactory抽象類有什麼做用嗎?和接口有什麼區別嗎?何時用接口何時用抽象類?
- 1、若是你擁有一些方法想讓他們中的一些默認實現,那麼使用抽象類。
- 2、若是你想實現多重繼承,那麼你必須使用接口。因爲java不支多繼承,子類不可以繼承多個類,但能夠實現多個接口
- 3、若是基本功能在不斷改變,那麼就須要使用抽象類。若是不斷改變基本功能而且使用接口 ,那麼就須要改變全部實現了該接口的類。
3、有了bean工廠你總得bean的定義吧。`BeanDefinition`來了。bean的定義
```java
/** * Created by guo on 3/1/2018. * bean的內容及元數據,保存在beanFactory中,包裝bean的實體。 */
public class BeanDefinition {
private Object bean;
//類的class信息
private Class beanClass;
//類名
private String beanClassName;
//保存全部的屬性,
private PropertyValues propertyValues = new PropertyValues();
public BeanDefinition() {
}
public BeanDefinition() {}
//setters and gettes 略
public void setBeanClassName(String beanClassName) {
this.beanClassName = beanClassName;
try {
//加載類,並返回class對象
//這裏已經有類的實例來,可是沒有引用,怎麼能夠獲取這個引用呢?
this.beanClass = Class.forName(beanClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
複製代碼
四、有了bean的定義你總得建立啊,初始化那,註冊啊,驗證啊,否則一點卵用都沒有。來瞅瞅。這時候咱們來看下AbstractBeanFactory抽象類具體的實現。
/** * Created by guo on 3/1/2018. * 抽象bean工廠 */
public abstract class AbstractBeanFactory implements BeanFactory {
//bean工程裏維護類的字典,類名+class對象
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
private final List<String> beanDefinitionNames = new ArrayList<String>();
private List<BeanPostProcessor> beanPostProcessors = new ArrayList<BeanPostProcessor>();
/** * 獲取bean的時候,才建立類的實例對象,原來只是保存類名和類的Class對象 。 * 到這一步會根據Class對象建立類的實例 * * @param name * @return * @throws Exception */
@Override
public Object getBean(String name) throws Exception {
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
if (beanDefinition == null) {
throw new IllegalArgumentException("no bean named " + name + "is defined");
}
Object bean = beanDefinition.getBean();
if (bean == null) {
//一、剛建立的對象,其餘什麼都沒作
bean = doCreateBean(beanDefinition);
//二、初始化bean對象
bean = initializeBean(bean,name);
//三、這裏的bean是初始化以後的bean,與剛開始建立的bean不同。
beanDefinition.setBean(bean);
}
return bean;
}
}
複製代碼
五、咱們在來看看具體的doCreateBean
、initializeBean
、registerBeanDefinition
/** * 初始化bean,BeanPostProcessor初始化先後處理器。 */
protected Object initializeBean(Object bean, String name) throws Exception {
for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
bean = beanPostProcessor.postProcessBeforeInitialization(bean, name);
}
for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
bean = beanPostProcessor.postProcessAfterInitialization(bean, name);
}
return bean;
}
/** * 建立bean的實例 */
protected Object createBeanInstance(BeanDefinition beanDefinition) throws Exception {
return beanDefinition.getBeanClass().newInstance();
}
/** * 註冊bean,即將類名和定義保存到內存中(map對象)中 */
public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {
beanDefinitionMap.put(name, beanDefinition);
//保存一份作準備
beanDefinitionNames.add(name);
}
/** * 建立bean並設置bean的引用 */
protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception {
//這裏會建立bean的實例對象
Object bean = createBeanInstance(beanDefinition);
//將bean的實例對象設置到beandefinition中去
beanDefinition.setBean(bean);
//設置bean的引用的實例對象
applyPropertyValues(bean, beanDefinition);
return bean;
}
複製代碼
六、可有可無的方法暫時就不貼出來了,描述個大概過程。帶着這些類和接口在源碼中找。請忽略第二步和第三步。直接跳到第四步。記得是在Spring源碼,這個簡單多了,可是基本功能有
step1中的bean是初始化好以後再set進去的,實際使用中,咱們但願容器來管理bean的建立。因而咱們將bean的初始化放入BeanFactory中。爲了保證擴展性,咱們使用Extract Interface的方法,將BeanFactory替換成接口,而使用AbstractBeanFactory和AutowireCapableBeanFactory做爲其實現。"AutowireCapable"的意思是「可自動裝配的」,爲咱們後面注入屬性作準備。
// 1.初始化beanfactory
BeanFactory beanFactory = new AutowireCapableBeanFactory();
// 2.注入bean
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClassName("us.codecraft.tinyioc.HelloWorldService");
beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);
// 3.獲取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
複製代碼
這一步,咱們想要爲bean注入屬性。咱們選擇將屬性注入信息保存成PropertyValue對象,而且保存到BeanDefinition中。這樣在初始化bean的時候,咱們就能夠根據PropertyValue來進行bean屬性的注入。Spring自己使用了setter來進行注入,這裏爲了代碼簡潔,咱們使用Field的形式來注入。
// 1.初始化beanfactory
BeanFactory beanFactory = new AutowireCapableBeanFactory();
// 2.bean定義
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanClassName("us.codecraft.tinyioc.HelloWorldService");
// 3.設置屬性
PropertyValues propertyValues = new PropertyValues();
propertyValues.addPropertyValue(new PropertyValue("text", "Hello World!"));
beanDefinition.setPropertyValues(propertyValues);
// 4.生成bean
beanFactory.registerBeanDefinition("helloWorldService", beanDefinition);
// 5.獲取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
複製代碼
一、看到讀取你總得有IO流吧 ,還有有資源啊(Xxx.xml),還有一個讀取器。讓咱們看下重要的接口和實現類。
/** * Resource是Spring內部定位資源接口 */
public interface Resource {
InputStream getInputStream() throws Exception;
}
-----------------加載資源------------------------------
public class ResourceLoader {
//獲取資源
public Resource getResource(String location){
URL resource = this.getClass().getClassLoader().getResource(location);
return new UrlResource(resource);
}
}
--------------------------------------------------
*/
public class UrlResource implements Resource {
private final URL url;
public UrlResource(URL url) {
this.url = url;
}
@Override
//根據URL載入輸入流
public InputStream getInputStream() throws IOException{
URLConnection urlConnection = url.openConnection();
urlConnection.connect();
return urlConnection.getInputStream();
}
}
--------------------測試--------------------------------
public class ResourceLoaderTest {
@Test
public void test() throws IOException {
ResourceLoader resourceLoader = new ResourceLoader();
Resource resource = resourceLoader.getResource("tinyioc.xml");
InputStream inputStream = resource.getInputStream();
Assert.assertNotNull(inputStream);
}
}
複製代碼
二、接下來就讓咱們看看更爲重要的接口和實現類
public interface BeanDefinitionReader {
void loadBeanDefinitions(String location) throws Exception;
}
----------------------重要實現----------------------------------
/** * Created by guo on 3/1/2018. * 從配置文件中讀取BeanDifinition 抽象類 */
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {
//bean集合
private Map<String,BeanDefinition> registry;
//資源加載器
private ResourceLoader resourceLoader;
protected AbstractBeanDefinitionReader(ResourceLoader resourceLoader) {
this.registry = new HashMap<String, BeanDefinition>();
this.resourceLoader = resourceLoader;
}
//setter。getter
}
複製代碼
三、最終的實現來了
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {
super(resourceLoader);
}
@Override
public void loadBeanDefinitions(String location) throws Exception {
InputStream inputStream = getResourceLoader().getResource(location).getInputStream();
doLoadBeanDefinitions(inputStream);
}
複製代碼
四、爲了便於理解 ,我把方法抽出來了。這裏主要是解析和註冊
protected void doLoadBeanDefinitions(InputStream inputStream) throws Exception {
//xml解析
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = factory.newDocumentBuilder();
Document doc = docBuilder.parse(inputStream);
// 解析bean
registerBeanDefinitions(doc);
inputStream.close();
}
public void registerBeanDefinitions(Document doc) {
Element root = doc.getDocumentElement();
parseBeanDefinitions(root);
}
複製代碼
五、真正的解析在這裏。
protected void parseBeanDefinitions(Element root) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
processBeanDefinition(ele);
}
}
}
protected void processBeanDefinition(Element ele) {
//獲取id和classname
String name = ele.getAttribute("id");
String className = ele.getAttribute("class");
BeanDefinition beanDefinition = new BeanDefinition();
//處理屬性
processProperty(ele, beanDefinition);
//註冊Class
beanDefinition.setBeanClassName(className);
getRegistry().put(name, beanDefinition);
}
//添加bean的屬性,和ref引用
private void processProperty(Element ele, BeanDefinition beanDefinition) {
NodeList propertyNode = ele.getElementsByTagName("property");
for (int i = 0; i < propertyNode.getLength(); i++) {
Node node = propertyNode.item(i);
if (node instanceof Element) {
Element propertyEle = (Element) node;
String name = propertyEle.getAttribute("name");
String value = propertyEle.getAttribute("value");
if (value != null && value.length() > 0) {
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));
} else {
String ref = propertyEle.getAttribute("ref");
if (ref == null || ref.length() == 0) {
throw new IllegalArgumentException("Configuration problem: <property> element for property '"
+ name + "' must specify a ref or value");
}
//bean對其餘對象的引用,直接放到本身的屬性裏面
BeanReference beanReference = new BeanReference(ref);
beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanReference));
}
}
}
}
}
複製代碼
六、這裏是測試代碼
@Test
public void test() throws Exception {
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
Map<String, BeanDefinition> registry = xmlBeanDefinitionReader.getRegistry();
Assert.assertTrue(registry.size() > 0);
}
複製代碼
這麼大一坨初始化代碼讓人心煩。這裏的BeanDefinition只是一些配置,咱們仍是用xml來初始化吧。咱們定義了BeanDefinitionReader初始化bean,它有一個實現是XmlBeanDefinitionReader。
// 1.讀取配置
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
// 2.初始化BeanFactory並註冊bean
BeanFactory beanFactory = new AutowireCapableBeanFactory();
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
// 3.獲取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
複製代碼
使用xml配置以後,彷佛裏咱們熟知的Spring更近了一步!可是如今有一個大問題沒有解決:咱們沒法處理bean之間的依賴,沒法將bean注入到bean中,因此它沒法稱之爲完整的IoC容器!如何實現呢?咱們定義一個BeanReference,來表示這個屬性是對另外一個bean的引用。這個在讀取xml的時候初始化,並在初始化bean的時候,進行解析和真實bean的注入。
for (PropertyValue propertyValue : mbd.getPropertyValues().getPropertyValues()) {
Field declaredField = bean.getClass().getDeclaredField(propertyValue.getName());
declaredField.setAccessible(true);
Object value = propertyValue.getValue();
if (value instanceof BeanReference) {
BeanReference beanReference = (BeanReference) value;
value = getBean(beanReference.getName());
}
declaredField.set(bean, value);
}
複製代碼
同時爲了解決循環依賴的問題,咱們使用lazy-init的方式,將createBean的事情放到getBean的時候才執行,是否是一會兒方便不少?這樣在注入bean的時候,若是該屬性對應的bean找不到,那麼就先建立!由於老是先建立後注入,因此不會存在兩個循環依賴的bean建立死鎖的問題。
// 1.讀取配置
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions("tinyioc.xml");
// 2.初始化BeanFactory並註冊bean
AbstractBeanFactory beanFactory = new AutowireCapableBeanFactory();
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
// 3.初始化bean
beanFactory.preInstantiateSingletons();
// 4.獲取bean
HelloWorldService helloWorldService = (HelloWorldService) beanFactory.getBean("helloWorldService");
helloWorldService.helloWorld();
複製代碼
無論三七二十一,咱們先看重要的接口以及重要實現
/** * 繼承beanFactory,繼承了factory全部的遺產 */
public interface ApplicationContext extends BeanFactory {
}
-----------------------------------------------------------------
public abstract class AbstractApplicationContext implements ApplicationContext {
protected AbstractBeanFactory beanFactory;
public AbstractApplicationContext(AbstractBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void refresh() throws Exception {
//加載bean
loadBeanDefinitions(beanFactory);
//註冊以前,乾點什麼事情
registerBeanPostProcessors(beanFactory);
onRefresh();
}
//調用beanfactory工廠獲取bean的實例對象
@Override
public Object getBean(String name) throws Exception {
return beanFactory.getBean(name);
}
}
複製代碼
二、爲了方便,方法放這裏
protected abstract void loadBeanDefinitions(AbstractBeanFactory beanFactory) throws Exception;
protected void registerBeanPostProcessors(AbstractBeanFactory beanFactory) throws Exception {
List beanPostProcessors = beanFactory.getBeansForType(BeanPostProcessor.class);
for (Object beanPostProcessor : beanPostProcessors) {
beanFactory.addBeanPostProcessor((BeanPostProcessor) beanPostProcessor);
}
}
protected void onRefresh() throws Exception{
beanFactory.preInstantiateSingletons();
}
複製代碼
三、熟悉的東東出場了
public class ClassPathXmlApplicationContext extends AbstractApplicationContext {
private String configLocation;
public ClassPathXmlApplicationContext(String configLocation) throws Exception {
this(configLocation, new AutowireCapableBeanFactory()); //可自動裝配內容的BeanFactory
}
public ClassPathXmlApplicationContext(String configLocation, AbstractBeanFactory beanFactory) throws Exception {
super(beanFactory);
this.configLocation = configLocation;
//直接所有初始化
refresh();
}
@Override
protected void loadBeanDefinitions(AbstractBeanFactory beanFactory) throws Exception {
//定位bean,而後加載bean
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(new ResourceLoader());
xmlBeanDefinitionReader.loadBeanDefinitions(configLocation);
//註冊bean,這裏bean已經加載到虛擬機中,但尚未實例化對象,先不急嘛。
for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : xmlBeanDefinitionReader.getRegistry().entrySet()) {
beanFactory.registerBeanDefinition(beanDefinitionEntry.getKey(), beanDefinitionEntry.getValue());
}
}
複製代碼
三、測試代碼
@Test
public void test() throws Exception {
//就是把beanfactory封裝一下,使調用更加方便。註冊,所有初始化。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
HelloWorldService helloWorldService = (com.guo.codecraft.tinyioc.HelloWorldService) applicationContext.getBean("helloWorldService");
helloWorldService.helloWorld();
}
@Test
public void testPostBeanProcessor() throws Exception {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc-postbeanprocessor.xml");
HelloWorldService helloWorldService = (com.guo.codecraft.tinyioc.HelloWorldService) applicationContext.getBean("helloWorldService");
helloWorldService.helloWorld();
}
複製代碼
如今BeanFactory的功能齊全了,可是使用起來有點麻煩。因而咱們引入熟悉的ApplicationContext接口,並在AbstractApplicationContext的refresh()方法中進行bean的初始化工做。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("tinyioc.xml");
HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService");
helloWorldService.helloWorld();
複製代碼
是否是很是熟悉?至此爲止,咱們的tiny-spring的IoC部分可說完工了。這部分的類、方法命名和做用,都是對應Spring中相應的組件。雖然代碼量只有400多行,可是已經有了基本的IoC功能!