Spring源码学习,进行简单模拟实现,一步步搭建。
当前完成:
ComponentScan
扫描指定路径包(不递归,且认为都是class文件,直接类加载方式)Scope
区分单例与多例Bean(单例Bean创建放入单例池)Autowired
依赖注入(不考虑循环依赖)BeanDefinition
只考虑了type
,scope
,lazy
(未实现)InitializingBean
IOC注入完成后执行afterPropertiesSet
BeanPostProcessor
初始化前后执行前置后置方法Aware
普通对象依赖注入后,初始化前,注入Spring
属性ApplicationContext
对象, 通过该对象读取一个 Config
配置类信息。ApplicationContext
对象的 getBean()
方法获取 bean
对象, 并调用 bean
对象方法。KunApplicationContext
package com.spring;
/**
* @author guokun
* @date 2022/9/5 17:03
*/
public class KunApplicationContext {
private Class<?> configClass;
public KunApplicationContext(Class<?> configClass) {
this.configClass = configClass;
// To Do: 根据Config中指定的扫描路径扫描
}
public Object getBean(String beanName) {
return null;
}
}
测试类 Test
import com.kun.config.KunAppConfig;
import com.kun.service.KunService;
import com.spring.KunApplicationContext;
/**
* @author guokun
* @date 2022/9/5 17:05
*/
public class Test {
public static void main(String[] args) {
KunApplicationContext kunApplicationContext = new KunApplicationContext(KunAppConfig.class);
KunService kunService = (KunService) kunApplicationContext.getBean("kunService");
kunService.test();
}
}
ComponentScan
注解
package com.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author guokun
* @date 2022/9/5 17:17
*
* 运行时生效
* 类注解
* 扫描指定路径下得所有Component
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {
String value() default "";
}
Component
注解
package com.spring.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author guokun
* @date 2022/9/5 17:17
* 运行时生效
* 类注解
* 声明为需要创建的bean类,可以指定bean的名字
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {
String value() default "";
}
KunService
服务类(Bean类)
package com.kun.service;
import com.spring.annotation.Component;
/**
* @author guokun
* @date 2022/9/5 17:15
*/
@Component("kunService")
public class KunService {
public void test() {
System.out.println("kunService test()");
}
}
ApplicationContext
对象创建时进行。(构造方法中调用)public KunApplicationContext(Class<?> configClass) {
this.configClass = configClass;
componentScan();
}
private void componentScan(Class<?> configClass) {
}
}
Config
类中有没有 ComponentScan
注解,才可以扫描指定的包
- 通过
isAnnotationPresent()
查看某个类是否含有某个注解- 通过
getAnnotation()
获取某个类的某个注解对象
private void componentScan() {
// 扫描包
if (configClass.isAnnotationPresent(ComponentScan.class)) {
ComponentScan componentScanAnnotation = configClass.getAnnotation(ComponentScan.class);
// 获取包路径 xx.xx.xx
String packagePath = componentScanAnnotation.value();
}
}
JVM
是读取class
文件的,因此我们可以通过AppClassLoader
来获取指定包的相对文件路径(注意需要将包格式转换为文件路径格式) private void componentScan() {
// 扫描包
if (configClass.isAnnotationPresent(ComponentScan.class)) {
ComponentScan componentScanAnnotation = configClass.getAnnotation(ComponentScan.class);
// 获取包路径 xx.xx.xx
String packagePath = componentScanAnnotation.value();
// 格式转换为 xx/xx/xx (因为我们要去加载的是class文件,通过文件系统去读取)
String filePath = packagePath.replace(".", "/");
// 获取类加载器(AppClassLoader)
ClassLoader classLoader = KunApplicationContext.class.getClassLoader();
// 使用AppClassLoader去获取指定包下的class文件夹路径
URL targetClassUrl = classLoader.getResource(filePath);
}
}
Class
,然后检查是否含有Component
注解
- 在Spring中使用ASM技术扫描,无需加载类,此处直接通过将所有类加载后判断是否有
Component
注解实现- 此处先只考虑当前包,不考虑子包
- 假设该目录下所有文件都是
class
文件,不去考虑其他类型文件
// 获取对应的文件目录
assert targetClassUrl != null;
File targetClassDir = new File(targetClassUrl.getFile());
// 检查是否是一个文件目录
if (targetClassDir.isDirectory()) {
// 获取所有子文件 (先只考虑当前包,不考虑子包,假设子文件都是class文件)
File[] classFiles = targetClassDir.listFiles();
assert classFiles != null;
List<Class<?>> classList = new ArrayList<>(classFiles.length);
for (File classFile : classFiles) {
// 获取class文件名称 xx.class
String classFileName = classFile.getName();
// 获取类名(去除.class)
String className = classFileName.substring(0, classFileName.length() - 6);
String classFullName = packagePath + "." + className;
try {
// 使用类加载器(AppClassLoader)加载类
Class<?> loadClass = classLoader.loadClass(classFullName);
// 检查是否含有Component注解
if (loadClass.isAnnotationPresent(Component.class)) {
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
beanName
,如果Component
注解上为空串则使用类名的小驼峰命名作为