文章目录
前言
今天开启一个新的章节,手写一个简易版的spring
,帮助大家对spring
能有一个更深层次的理解.
我将分为以下几个章节进行学习:
今天先开启我们的第一个章节:容器启动.
applicationContext
初始化的前置操作
我们在日常使用spring
的时候,一般会向容器传入一个配置类或者配置文件 ,在配置类或者配置文件中,存放扫描路径 .在这里我们使用配置类的方式进行设计.
-
自定义启动类
BaiLuApplicationContext
:public BaiLuApplicationContext(Class configClass) {
//....
} -
自定义配置类
@ComponentScan("com.fbl.service")
public class AppConfig {
//.....
}
在配置类中需要进行扫描路径的确定,我们采用注解(@ComponentScan
)的方式设置一个扫描路径
-
自定义
@ComponentScan
@Retention(RetentionPolicy.RUNTIME)//注解生效时机
@Target(ElementType.TYPE) //写在类上面的注解
public @interface ComponentScan {
String value() default "";//存放扫描路径
} -
自定义
Component
@Retention(RetentionPolicy.RUNTIME)//生效时间
@Target(ElementType.TYPE) //写在类上面的注解
public @interface Component {
String value() default ""; //这里是beanName
}
获取扫描路径
容器启动的第一步,先要通过配置类,获取到扫描路径 .也就是获取到配置类上的注解 @ComponentScan
的value()
public BaiLuApplicationContext(Class configClass) {
this.configClass = configClass;
//1.扫描
if (configClass.isAnnotationPresent(ComponentScan.class)) {
ComponentScan annotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
String path = annotation.value();//扫描路径:com.fbl.service
//...
}
isAnnotationPresent
:类中是否存在某个注解getAnnotation
:获取到类的某个注解类
这里,我们获取到路径
com.fbl.service
,该路径就是我们真正需要的路径吗?答案是否定 的.因为我们真正想要扫描的是字节码文件 ,所以我们需要编译之后的字节码文件的路径,那么我们应该如何获取到这个路径呢?
String path = annotation.value(); //扫描路径:com.fbl.service
path=path.replace(".","/"); //com/fbl/service
ClassLoader classLoader = BaiLuApplicationContext.class.getClassLoader();//获取到类加载器
URL resource = classLoader.getResource(path); //通过相对路径(com/fbl/service)获取资源
File file = new File(resource.getFile()); //转化成文件
System.out.println(file); //D:\develop\MyGit\BaiLuSpring\out\production\BaiLuSpring\com\fbl\service
- 通过配置类的注解
@@ComponentScan("com.fbl.service")
获取到扫描的包路径;- 将扫描的包路径转换成扫描路径:
com.fbl.service
->com/fbl/service
- 获取到类加载器,通过相对路径获取到该资源
D:\develop\MyGit\BaiLuSpring\out\production\BaiLuSpring\com\fbl\service
,这里获取到的就是存放.class
文件的文件夹
判断是否是bean
对象
我们刚才已经获取到了字节码文件存放的文件夹 ,接下来,我们要开始判断,该文件夹下,有哪些是需要交给spring
来管理的bean
对象.
这里采用的方法是判断是否存在@Component
注解:
if(file.isDirectory()){
File[] files = file.listFiles();
for (File f : files) {
String fileName = f.getAbsolutePath();
System.out.println(fileName);
//判断是不是一个类
if(fileName.endsWith(".class")){
String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
className=className.replace("\\",".");
//获取对象
try {
Class<?> clazz = classLoader.loadClass(className);//传入全限定类名
//说明当前存在@Component注解,是一个需要交给spring管理的bean对象
if (clazz.isAnnotationPresent(Component.class)) {
}
}
}
- 遍历 获取到的资源文件夹,判断是否是
.class
文件;- 如果是
.class
文件(是一个类),接下来通过反射的方式获取到当前的类- 再来判断是否存在
@Component
注解.
这样,整个容器的启动大致就完成了,其实我们主要完成了扫描和判断是否是bean
对象的工作 .那么bean
对象是在什么时候被创建的?什么是单例?什么是多例?这些问题将会在接下来的博客文章里进行总结.