一個模擬Spring初始化Ioc容器的demo

一個模擬Spring初始化Ioc容器的demo

在李剛老師的《JAVA瘋狂講義》的最後一節中,講到一個 ObjectPoolFactory-對象池工廠,說是就是Spring框架的核心,用於創建對象以及獲取對象,以此爲基礎寫了這個demo。因爲我還沒看過spring的源碼,所以可能會有不同,請各位斧正!

  • 涉及的知識
  1. Annotation -註解類的自定義
  2. reflect- 反射的瞭解,如 Class.forName(String className)class.getDeclareFields()
  3. classpath的理解
  • 這個demo實現的功能
  1. 模擬Spring:只加載具有指定Annotation類的類到IoC容器中
  2. 模擬Spring:只掃描指定包下的相關類,其他包的不管
  • IoC容器-對象池工廠ObjectPoolFactory的主要代碼
package springbootCopy;

import springbootCopy.annotation.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * Create by Lingo
 */

public class ObjectPoolFactory {
   private static Map<String,Object> pool = new HashMap<>();  //初始化對象池
	//這是加載類時調用的方法,這是靜態方法
    public static void createObject(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
            Class<?> clazz = Class.forName(className); //記住,這個方法的參數的格式是 `com.xx.xx`這種形式
            //如果這個類沒有 Component 註解,則不加載到對象池中
            if (clazz.getDeclaredAnnotation(Component.class) == null){
                return;
            }
            pool.put(className,clazz.newInstance());
    }
	//這是獲取類的時候調用的,如果找不到則拋出 ClassNotFoundException  異常
    public static Object getObject(String className) throws ClassNotFoundException {
        Object object = pool.get(className);
        if (null == object){
            throw new ClassNotFoundException(className);
        }
        return object;
    }
}
  • 創建註解類
package springbootCopy.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Create by Lingo
 * 主要功能是指明這個類需要被加載到 ioc 容器中
 */
 
//指明註解在運行時仍然存在
@Retention(RetentionPolicy.RUNTIME)
//指明這個註解只能用在 類 上
@Target(ElementType.TYPE)
public @interface Component {
}
package springbootCopy.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Create by Lingo
 * 指明掃描哪個包下的類
 */
 
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
    String value();
}
  • 創建2個類,用於測試是否加載成功
package springbootCopy.classes;

import springbootCopy.annotation.Component;

/**
 * Create by Lingo
 */

@Component
public class Animal {
	private String type;
    private Integer age;
    //初始化塊
    {
        type = "我是 Animal";
        age = 10;
    }
}
package springbootCopy.classes;

/**
 * Create by Lingo
 */

public class Person {
    String name;
    Integer age;
}
  • 啓動主程序
package springbootCopy;
import springbootCopy.annotation.ComponentScan;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;

/**
 * Create by Lingo
 */


@ComponentScan("springbootCopy.classes")  /// Ⅰ號代碼
public class Main {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException, IOException {
    	//還記得 @SpringBootApplication 其實這個註解就可以標誌這個是啓動程序,springBoot主程序只需要掃描一下就知道哪個是主程序的入口了
    	//也就獲得以下的主程序 Class,這裏爲了方便並沒有演示出來,如果這段話看不懂的同學,可以在評論區進行評論,我再豐富內容
        Class<?> clazz = Main.class;
        //如果這個沒有指定掃描包,則直接返回。(理應有默認值其實)
        Annotation annotation = clazz.getDeclaredAnnotation(ComponentScan.class);
        if (null == annotation){
            return;
        }
         // 通過反射獲取 指定包的掃描值,本文中 classPath = "springCopy.classes" ,由Ⅰ號代碼 處獲取
        String aimPath = ((ComponentScan)annotation).value();
        // 獲取 classpath,或者理解成編譯後的 class文件存在的地方
        String url =ClassLoader.getSystemClassLoader().getResource("")+aimPath;
       	// 要將 aimPath 的 . 替換成 / ,才能拼湊成編譯後class的路徑
        url = url.replace(".","/");
        url = url.replace("file:/",""); // 去掉classpath 中的 file:/
        File file = new File(url);
        File[] subFiles = file.listFiles();
        //遍歷這個文件下的所有 class 文件,並將符合條件的類加載到對象池裏
        for (File subFile : subFiles) {
        	// 提取 className , 例如 springCopy.classes.Animal
            String className =  aimPath+"."+subFile.getName().substring(0,subFile.getName().indexOf("."));
            ObjectPoolFactory.createObject(className);
        }
        //獲取相關的加載類,如果對象池沒有這個類的話,則會有 classNotFound 的錯
        for (File subFile : subFiles) {        	
            String path =  aimPath+"."+subFile.getName().substring(0,subFile.getName().indexOf("."));
            Object object = ObjectPoolFactory.getObject(path);
            System.out.println(object);
             for (Field declaredField : object.getClass().getDeclaredFields()) {
                declaredField.setAccessible(true); //關閉 私有 Field 的權限檢查,即所有的Field 無論權限控制符是啥,都可以訪問
                System.out.println(declaredField.get(object));
            }          
        }
    }
}
  • 輸出結果 :因爲 Person 類沒有 @Component的註解,所以沒有加載到容器中,因此報錯
springbootCopy.classes.Animal@266474c2
我是 Animal
10
Exception in thread "main" java.lang.ClassNotFoundException
	at springbootCopy.ObjectPoolFactory.getObject(ObjectPoolFactory.java:26)
	at springbootCopy.Main.main(Main.java:34)

Process finished with exit code 1

如果再加上 AOP 則可以實現很多功能。
以上則是這個demo的全部內容,歡迎各位斧正!

END

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章