版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
一个模拟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的全部内容,欢迎各位斧正!