從零開始實現spring(一)最簡單的IOC
本文原始碼可至碼雲查閱(https://gitee.com/wondersWX/myspring),我也還在一步步構建中,歡迎fork,歡迎star!
自開啟始JavaWeb程式設計以來,從最初的jsp+servelt,到SSH框架,更行技術框架SSM框架,實現分散式架構,進行前後分離,接觸到的框架技術可以說數不勝數,但從沒能離開的就是spring,最開始的時候只是將spring用作物件容器,做框架整合,事務管理,然後慢慢從struts過渡到springmvc,使用spring進行view層的控制,再後來,使用spring-boot,有直接打包好的檢視層、和jpa、mybatise實體層,到現在使用spring-clouder實現微服務,通過路由加nigix實現前後分離架構。最近在一個微信群中,看到群友分享的一個面試題,如果不能使用spring,你會怎麼程式設計?發現自己看到這個問題著實愣了一下,的確使用spring已成了習慣,沒想過缺少了這個框架該怎麼辦。其實使用了spring這麼久,spring兩大基礎特性ioc(控制反轉),aop(面向切面)兩大特性可以說是十分熟悉了,自己對該如何實現這兩大特性也有一定的思路,那還等什麼,自己來嘗試實現一下吧!
那麼就從大家最熟悉的開始,對於applicationContext.xml檔案中bean的配置可以說用過spring的人沒有不熟悉的,那麼其本質是什麼呢?簡單說,就是從xml檔案中讀取到bean的定義,spring的bean工廠基於此定義進行bean的建立和裝配,然後你就能輕鬆通過getBean()方法獲取到例項化的物件,再進一步抽象,就是將一段字串轉化為一個物件例項,熟悉反射的人可能就能想到了,這不挺像類的反射嘛,給出類路徑獲取Class,再使用newInstance獲取物件。沒錯spring的物件建立就是大量使用了反射的方式。好了說了這麼多我們就來嘗試自己實現一個最簡單的IOC吧!
spring的BeanFactory在建立物件時並不是直接讀取xml檔案中的配置,而是對bean的定義進行了封裝也就是BeanDefinition
只是通過反射建立物件還不夠,我們當然想要給物件進行屬性注入,spring對屬性配置也進行了封裝package com.fxx.bean; public class BeanDefiniton { private String beanClassName; private Class beanClass; //bean的建立與裝載應由工廠完成,beanDefinition只維護此屬性,不提供建立bean的方法 private Object bean; private PropertyValues pvs; public String getBeanClassName() { return beanClassName; } public void setBeanClassName(String beanClassName) { this.beanClassName = beanClassName; //一般通過class反射建立例項,但ioc目的是通過修改字串達到調整例項的目標故在設定classname的同時,修改beanClass屬性 try { this.beanClass = Class.forName(beanClassName); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public Class getBeanClass() { return beanClass; } public void setBeanClass(Class beanClass) { this.beanClass = beanClass; } public Object getBean() { return bean; } public void setBean(Object bean) { this.bean = bean; } public PropertyValues getPvs() { return pvs; } public void setPvs(PropertyValues pvs) { this.pvs = pvs; } }
package com.fxx.bean;
public class PropertyValue {
private final String name;
private final Object value;
//物件屬性屬於不可改變的時候,可將其設定為final,在構造器中賦值,並不提供set方法(提供也沒用)
public PropertyValue(String name,Object value){
this.name=name;
this.value=value;
}
public String getName() {
return name;
}
public Object getValue() {
return value;
}
}
存在多個屬性注入,首相想到的是應該是使用list將propertyvalue管理起來,事實上spring也是這麼做的,但是為了更靈活的屬性注入,提供屬性注入前後的操作,那麼我們最熟悉的代理設計模式就可以用上了package com.fxx.bean;
import java.util.ArrayList;
import java.util.List;
/**
* 用於載入屬性前判斷
*/
public class PropertyValues {
private final List<PropertyValue> propertyValueList = new ArrayList<PropertyValue>();
public void addValue(PropertyValue pv){
//此處進行注入前操作
propertyValueList.add(pv);
}
public List<PropertyValue> getPropertyValueList() {
return propertyValueList;
}
}
好了有了beanDfiniton,現在只要將其註冊到工廠中,就可以獲取到物件了,我們來建立一個最簡單的物件工廠,使用map來管理beanDfiniton,通過物件name獲取物件,程式碼如下
package com.fxx.bean.factory;
import com.fxx.bean.BeanDefiniton;
public interface BeanFactory {
Object getBean(String name) throws Exception;
void registBeanDefinition(String name, BeanDefiniton beanDefiniton);
}
package com.fxx.bean.factory;
import com.fxx.bean.BeanDefiniton;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public abstract class AbstractBeanFactory implements BeanFactory{
private final Map<String,BeanDefiniton> beanDefinitonMap = new ConcurrentHashMap<String, BeanDefiniton>();
public Object getBean(String name) throws Exception {
if(!beanDefinitonMap.containsKey(name)||beanDefinitonMap.get(name)==null){
throw new Exception("物件定義不存在!");
}
return beanDefinitonMap.get(name).getBean();
}
/**
* 註冊同時建立bean並裝載,將bean的建立與裝載相分離,建立的方式由子類實現(bean的建立有多種型別的需求)
* @param name
* @param beanDefiniton
*/
public void registBeanDefinition(String name, BeanDefiniton beanDefiniton){
if(beanDefiniton==null){
return;
}
Object bean = null;
try {
bean = creatBean(beanDefiniton);
} catch (Exception e) {
e.printStackTrace();
}
beanDefiniton.setBean(bean);
beanDefinitonMap.put(name,beanDefiniton);
}
protected abstract Object creatBean(BeanDefiniton beanDefiniton) throws Exception;
}
package com.fxx.bean.factory;
import com.fxx.bean.BeanDefiniton;
import com.fxx.bean.PropertyValue;
import com.fxx.bean.PropertyValues;
import java.lang.reflect.Field;
public class AutoWiredBeanFactory extends AbstractBeanFactory{
/**
* 實現例項建立以及屬性裝載
* @param beanDefiniton
* @return
*/
@Override
protected Object creatBean(BeanDefiniton beanDefiniton) throws Exception{
return doCreatBean(beanDefiniton);
}
private Object doCreatBean(BeanDefiniton beanDefiniton) throws Exception{
Object bean = beanDefiniton.getBeanClass().newInstance();
setPropertyValues(bean,beanDefiniton.getPvs());
return bean;
}
private void setPropertyValues(Object bean,PropertyValues pvs) throws Exception {
if(pvs.getPropertyValueList().size()==0){
return;
}
for (PropertyValue pv : pvs.getPropertyValueList()){
Field declaredField = bean.getClass().getDeclaredField(pv.getName());
declaredField.setAccessible(true);
declaredField.set(bean,pv.getValue());
}
}
}