一個模擬Spring初始化Ioc容器的demo
在李剛老師的《JAVA瘋狂講義》的最後一節中,講到一個 ObjectPoolFactory
-對象池工廠,說是就是Spring框架的核心,用於創建對象以及獲取對象,以此爲基礎寫了這個demo。因爲我還沒看過spring的源碼,所以可能會有不同,請各位斧正!
- 涉及的知識
Annotation
-註解類的自定義reflect
- 反射的瞭解,如Class.forName(String className)
、class.getDeclareFields()
- 對
classpath
的理解
- 這個demo實現的功能
- 模擬Spring:只加載具有指定
Annotation
類的類到IoC
容器中 - 模擬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的全部內容,歡迎各位斧正!