spring IOC容器自自實現
阿新 • • 發佈:2018-12-19
流程
元件一、配置檔案載入器
public final class PropsUtil { /** * 獲取檔案流,轉成properti map記憶體 */ public static Properties loadProps(String fileName){ InputStream inputStream=Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName); Properties properties=new Properties(); properties.load(inputStream); } /** * 通過key 獲取對應value */ public static String getPropertie(Properties properties,String key){ String value=""; if (properties.contains(key)){ value=properties.getProperty(key); } return value; } }
元件二、類載入器
/** * @author zhongailing * @version V1.0 * @Description: 通過url載入對應類 如同scan配置 傳入最基礎的包名,通過包名裝配初始化所有bean, * @date Created in 2018-11-5 15:51 * 1.根據包名獲取(檔案路徑),get類.class檔案 * 2.傳入loadClass方法,返回類 */ public class ClassLoadHandler { /** * 獲取類載入期 * @return */ public static ClassLoader getClassLoader(){ return Thread.currentThread().getContextClassLoader(); } /** * 通過className載入類 * @param className * @param isInitialized * @return */ public static Class<?> loadClass(String className,Boolean isInitialized){ Class<?> cls = null; try { //isInitialized標識是否初始化(執行靜態程式碼塊,new物件) cls=Class.forName(className,isInitialized,getClassLoader()); } catch (ClassNotFoundException e) { e.printStackTrace(); } return cls; } /** * 獲取該包名下的所有類 * @param packageName * @return */ public static Set<Class<?>> getClassSet(String packageName){ /** * 1. 獲取指定包名下的的所有類 * 2. 根據包名將其轉換為檔案路徑 * 3. 讀取class檔案或jar包 * 4. 獲取指定的類名去載入類 */ Set<Class<?>> classSet = new HashSet<>(); try { Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/")); while (urls.hasMoreElements()){ URL url = urls.nextElement(); String protocol = url.getProtocol(); //獲取此 URL 的協議名稱。 if(protocol.equals("file")){ // %20 表示file協議? String packagePath = url.getPath().replaceAll("%20", ""); addClass(classSet,packagePath,packageName); } } } catch (IOException e) { e.printStackTrace(); } return classSet; } /** * 如果是檔案,就根據包名 和 檔名 組成類的全限定名稱,然後 載入類 * @param classSet * @param packagePath 檔案(夾)的絕對路徑 * @param packageName 和當前檔案(夾) 對應的包名 */ public static void addClass(Set<Class<?>> classSet,String packagePath,String packageName){ File[] files = new File(packagePath).listFiles(file -> { // 只需要 檔案並且是.class的檔案,或則是目錄 都返回true return file.isFile() && file.getName().endsWith(".class") || file.isDirectory(); }); for (File file : files) { String fileName = file.getName(); if(file.isFile()){ // 是指定的檔案 就獲取到全限定類名 然後裝載它 String className = fileName.substring(0, fileName.lastIndexOf(".")); // 把.class後最擷取掉 if(StringUtils.isNotBlank(packageName)){ className = packageName + "." + className; // 根據包名 + 檔名 得到這個類的全限定名稱, } Class<?> cls = loadClass(className, false); classSet.add(cls); }else { // 是檔案 就遞迴自己. 獲取 資料夾的絕對路徑,和 當前資料夾對應的 限定包名.方便 檔案裡面直接使用 String subPackagePath= fileName; if(StringUtils.isNotBlank(subPackagePath)){ subPackagePath = packagePath + "/" + subPackagePath; // 第一次:由基礎包名 得到絕對路徑,再加上當前資料夾名稱 = 當前資料夾的絕對路徑 } subPackagePath = file.getAbsolutePath(); // 該方法獲得檔案的絕對路徑.和上面的程式碼效果是一致的 String subPackageName = fileName; if(StringUtils.isNotBlank(subPackageName)){ subPackageName = packageName + "." + subPackageName; // 第一次: 基礎包名 加資料夾名稱 組合成 當前包名 + } addClass(classSet,subPackagePath, subPackageName); } } } public static void main(String[] args) { getClassSet("java/ioc"); }
增加按照註解區分@[email protected] 不同bean的kv(class-obj)結構
/** * @author zhongailing * @version V1.0 * @Description: 在裝配的class中區分出@[email protected]對應的bean * @date Created in 2018-11-5 16:15 */ public final class AnnotationMapHandler { private static final Logger LOGGER = LoggerFactory.getLogger(PropsUtil.class); private static final Set<Class<?>> CLASS_SET; static{ LOGGER.debug("是否被載入了兩次"); CLASS_SET = ClassLoadHandler.getClassSet(PropsUtil.getPropertie(null,"scan-package-name")); } public static Set<Class<?>> getClassSet() { return CLASS_SET; } /** * 獲取所有Services型別的註解類 * @return */ public static Set<Class<?>> getServiceClassSet(){ Set<Class<?>> serviceClassSet = new HashSet<>(); for (Class<?> c : CLASS_SET) { if(c.isAnnotationPresent(Services.class)){ // 是否存在 services註解 serviceClassSet.add(c); }; } return serviceClassSet; } /** * 獲取所有Controller型別的註解類 * @return */ public static Set<Class<?>> getControllerClassSet(){ Set<Class<?>> controllerClassSet = new HashSet<>(); for (Class<?> c : CLASS_SET) { if(c.isAnnotationPresent(Controller.class)){ controllerClassSet.add(c); }; } return controllerClassSet; } /** * 獲取所有的 bean : controller 和 Services 註解的類 * 類似 component =所有controller、service、repository bean * @return */ public static Set<Class<?>> getBeanClassSet(){ Set<Class<?>> beanClassSet = new HashSet<>(); beanClassSet.addAll(getControllerClassSet()); beanClassSet.addAll(getServiceClassSet()); return beanClassSet; }
元件三、bean容器
/**
* 維護靜態map<class型別,Object物件> 通過類名獲取bean物件
* bean初始化 即載入包下所有class,add到map中
*/
public class BeanContainer {
private static final Map<Class<?>, Object> BEAN_MAP = new HashMap<>();
static {
Set<Class<?>> beanClassSet = AnnotationMapHandler.getClassSet();
for (Class<?> c : beanClassSet) {
BEAN_MAP.put(c, ReflectionHandler.newInstance(c)); //建立所有class的例項(自己定義的註解類)
}
}
/**
* bean 容器
**/
public static Map<Class<?>, Object> getBeanMap() {
return BEAN_MAP;
}
/**
* 獲取bean
**/
public static <T> T getBean(Class<T> cls) {
if (!BEAN_MAP.containsKey(cls)) {
throw new RuntimeException("can not get bean by class : " + cls);
}
return (T) BEAN_MAP.get(cls);
}
/**
* 新增bean 例項
**/
public static void setBean(Class<?> cls, Object obj) {
BEAN_MAP.put(cls, obj);
}
}
IOC 反射將bean 通過setField 設定到使用bean的成員變數中
/**
* @author zhongailing
* @version V1.0
* @Description: 通過遍歷BeanContainer裡的所有cls,初始化對應bean例項,然後通過反射setField把注入的bean set到類中成為其成員變數。
* @date Created in 2018-11-5 16:37
*/
public class IocHandler {
static {
Map<Class<?>, Object> beanMap = BeanContainer.getBeanMap(); //框架需要管理的bean對映
if (MapUtils.isNotEmpty(beanMap)) {
for (Map.Entry<Class<?>, Object> ent : beanMap.entrySet()) {
Class<?> beanCls = ent.getKey();
Object beanInstance = ent.getValue();
// 獲取該class所有的成員屬性,包括公共、保護、預設(包)訪問和私有欄位,但不包括繼承的欄位
Field[] beanFields = beanCls.getDeclaredFields();
for (Field beanField : beanFields) {
//判斷 該欄位是否 包含 inject註解
if (beanField.isAnnotationPresent(Inject.class)) {
Class<?> beanFieldType = beanField.getType(); //宣告型別 成員變數的Class
Object beanFieldInstance = beanMap.get(beanFieldType); // 獲取該型別的例項
if (beanFieldInstance != null) {
ReflectionHandler.setField(beanInstance, beanField, beanFieldInstance); // 把對應的成員變數屬性 賦值
}
}
}
}
}
}
}
反射處理類
/**
* @author zhongailing
* @version V1.0
* @Description: 通過ClassLoaderHandler 載入的class類來初始化物件
* java反射 clazz.newInstance
* @date Created in 2018-11-5 16:23
*/
public class ReflectionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionUtil.class);
/** 建立例項 **/
public static Object newInstance(Class<?> cls){
Object ins = null;
try {
ins = cls.newInstance();
} catch (Exception e) {
LOGGER.error("new instance failure");
throw new RuntimeException(e);
}
return ins;
}
public static Object newInstance(String className){
Object ins = null;
try {
Class<?> cls = Class.forName(className);
ins = cls.newInstance();
} catch (Exception e) {
LOGGER.error("new instance failure");
throw new RuntimeException(e);
}
return ins;
}
/**
* 呼叫方法
* @param obj 例項
* @param method 方法
* @param args 引數
* @return
*/
public static Object invokeMethod(Object obj, Method method, Object...args){
method.setAccessible(true); //取消訪問許可權檢查
Object result = null;
try {
result = method.invoke(obj, args);
} catch (Exception e) {
LOGGER.error("invoe method fail",e);
throw new RuntimeException(e);
}
return result;
}
/**
* 呼叫不帶引數的方法
* @param obj
* @param method
* @return
*/
public static Object invokeMethod(Object obj,Method method){
method.setAccessible(true); //取消訪問許可權檢查
Object result = null;
try {
result = method.invoke(obj);
} catch (Exception e) {
LOGGER.error("invoe method fail",e);
throw new RuntimeException(e);
}
return result;
}
/**
* 設定成員變數
* @param obj
* @param field
* @param value
*/
public static void setField(Object obj, Field field, Object value){
try {
field.setAccessible(true); //取消訪問許可權檢查
field.set(obj,value);
} catch (Exception e) {
LOGGER.error("set Field fail",e);
throw new RuntimeException(e);
}
}
}
最後流程總結,別人家的圖一個意思