前言
手动实现 Spring 底层机制的第三篇实现了编写自己 Spring 容器,实现扫描包, 得到 bean 的 class 对象 实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象
个人主页:尘觉主页
个人简介:大家好,我是尘觉,希望我的文章可以帮助到大家,您的满意是我的动力
在csdn获奖荣誉: csdn城市之星2名
Java全栈群星计划top前5
端午大礼包获得者
欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博客,感谢大家的观看
如果文章有什么需要改进的地方还请大佬不吝赐教 先在次感谢啦
编写自己 Spring 容器,实现扫描包, 得到 bean 的 class 对象
● java 的类加载器 3 种
Bootstrap 类加载器--------------对应路径 jre/lib
Ext 类加载器--------------------对应路径 jre/lib/ext
App 类加载器-------------------对应路径 classpath
● classpath 类路径,就是 java.exe 执行时,指定的路径,比如
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
//通过value可以指定要扫描的包
String value() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
//通过value可以给注入的bean/对象指定名字
String value() default "";
}
@ComponentScan(value = "com.wyxdu.spring.component")
public class WyxSpringConfig {
}
@Component("monsterService")
public class MonsterService {
}
@Component("monsterDao")
public class MonsterDao {
}
\1. 解析配置类
\2. 获取到配置类的 @ComponentScan(“com.Wyxedu.spring.component”)
\3. 获取扫描路径下所有的类文件
3.1 先得到类加载器, 使用 App 方式来加载.
ClassLoader classLoader=WyxSpringApplicationContext.class.getClassLoader();
3.2 将 path 转成 形式为 com/Wyxedu/spring/component
通过类加载器获取来类文件的 Clazz 对象
先 得 到 类 的 完 整 类 路 径 形 式 为com.Wyxedu.spring.component.MonsterService
public class WyxSpringApplicationContext {
private Class configClass;
public WyxSpringApplicationContext(Class configClass) {
this.configClass = configClass;
//1. 解析配置类
//2. 获取到配置类的 @ComponentScan("com.Wyxedu.spring.component")
ComponentScan componentScan = (ComponentScan)
this.configClass.getDeclaredAnnotation(ComponentScan.class);
String path = componentScan.value();
System.out.println("扫描路径 = " + path);
//3. 获取扫描路径下所有的类文件
//(1) 先得到类加载器, 使用 App 方式来加载. ClassLoader classLoader = WyxSpringApplicationContext.class.getClassLoader();
//在获取某个包的 d 对应的 URL 时,要求是 com/Wyxedu/spring/component
//URL resource = classLoader.getResource("com/Wyxedu/spring/component");
//(2) 将 path 转成 形式为 com/Wyxedu/spring/component
path = path.replace(".", "/");
URL resource = classLoader.getResource(path);
File file = new File(resource.getFile());
if(file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
String fileAbsolutePath = f.getAbsolutePath();
System.out.println("=======================================");
System.out.println("文件绝对路径 = " + fileAbsolutePath);
if(fileAbsolutePath.endsWith(".class")) {//说明是类文件
//通过类加载器获取来类文件的 Clazz 对象
// 先 得 到 类 的 完 整 类 路 径 形 式 为com.Wyxedu.spring.component.MonsterService
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\")
+ 1, fileAbsolutePath.indexOf(".class"));
String classFullPath = path.replace("/", ".") + "." + className;
System.out.println("类名 = " + className);
System.out.println("类的全路径 = " + classFullPath);
try {
//获取到扫描包下的类的 clazz 对象
Class<?> clazz = classLoader.loadClass(classFullPath);
if(clazz.isAnnotationPresent(Component.class)) {
//如果这个类有@Commponent, 说明是一个 spring bean
System.out.println("是一个 bean = " + clazz);
} else {
//如果这个类没有@Commponent, 说明不是一个 spring bean
System.out.println("不是一个 bean = " + clazz);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("=======================================");
}
}
}
}
public Object getBean(String name) {
return null;
}
}
public class AppMain {
public static void main(String[] args) {
//创建我们的 spring 容器对象
WyxSpringApplicationContext wyxSpringApplicationContext =
new WyxSpringApplicationContext(WyxSpringConfig.class);
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
//通过value可以指定singleton,prototype
String value() default "";
}
@Component("monsterService")
@Scope("prototype")
public class MonsterService{}
/**
* BeanDefinition 用于封装/记录Bean的信息[
1. scope
2 Bean对应的Class对象, 反射可以生对应的对象]
*/
public class BeanDefinition {
private String scope;
private Class clazz;
//可以根据需求,进行扩展
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
@Override
public String toString() {
return "BeanDefinition{" +
"scope='" + scope + '\'' +
", clazz=" + clazz +
'}';
}
}
将扫描到的 Bean 信息封装到 BeanDefinition 对象中,并保存到map中
public class WyxSpringApplicationContext {
private Class configClass;
//如果 bean 是单例的,就直接放在这个 单例 bean 对象池
private ConcurrentHashMap<String,Object> singletonObjects =
new ConcurrentHashMap<>();
//将 bean 的定义,放在这个 beanDefinitionMap 集合
private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =
new ConcurrentHashMap<>();
public WyxSpringApplicationContext(Class configClass) {
//通过扫描,得到 beanDefinition 的 map
beanDefinitionsByscan(configClass);
System.out.println(beanDefinitionMap);
}
private void beanDefinitionsByscan(Class configClass) {
this.configClass = configClass;
//1. 解析配置类
//2. 获取到配置类的 @ComponentScan("com.Wyxedu.spring.component")
ComponentScan componentScan = (ComponentScan)
this.configClass.getDeclaredAnnotation(ComponentScan.class);
String path = componentScan.value();
System.out.println("扫描路径 = " + path);
//3. 获取扫描路径下所有的类文件
//(1) 先得到类加载器, 使用 App 方式来加载. ClassLoader classLoader = WyxSpringApplicationContext.class.getClassLoader();
//在获取某个包的 d 对应的 URL 时,要求是 com/Wyxedu/spring/component
//URL resource = classLoader.getResource("com/Wyxedu/spring/component");
//(2) 将 path 转成 形式为 com/Wyxedu/spring/component
path = path.replace(".", "/");
URL resource = classLoader.getResource(path);
File file = new File(resource.getFile());
if(file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
String fileAbsolutePath = f.getAbsolutePath();
System.out.println("=======================================");
System.out.println("文件绝对路径 = " + fileAbsolutePath);
if(fileAbsolutePath.endsWith(".class")) {//说明是类文件
//通过类加载器获取来类文件的 Clazz 对象
// 先 得 到 类 的 完 整 类 路 径 形 式 为
com.wyxedu.spring.component.MonsterService
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\")
+ 1, fileAbsolutePath.indexOf(".class"));
String classFullPath = path.replace("/", ".") + "." + className;
System.out.println("类名 = " + className);
System.out.println("类的全路径 = " + classFullPath);
try {
//获取到扫描包下的类的 clazz 对象
Class<?> clazz = classLoader.loadClass(classFullPath);
if(clazz.isAnnotationPresent(Component.class)) {
//如果这个类有@Commponent, 说明是一个 spring bean
System.out.println("是一个 bean = " + clazz);
//解读
//1. 因为这里不能直接将 bean 实例放入 singletonObjects
//2. 原因是如果 bean 是prototype是需要每次创建新的 bean对象
//3. 所以,Spring 底层是这样设计的: 将 bean 信息封装到
BeanDefinition 对象中, 便于 getBean 的操作
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
//获取 bean 的 name
Component componentAnnotation =
clazz.getDeclaredAnnotation(Component.class);
String beanName = componentAnnotation.value();
//获取 bean 的 scope
if(clazz.isAnnotationPresent(Scope.class)) { // 如 果 有
@Scope
Scope scopeAnnotation =
clazz.getDeclaredAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value());
} else { //如果没有@Scope, 默认是 singleton
beanDefinition.setScope("singleton");
}
//放入到 beanDefinitionMap
beanDefinitionMap.put(beanName, beanDefinition);
} else {
//如果这个类没有@Commponent, 说明不是一个 spring bean
System.out.println("不是一个 bean = " + clazz);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("=======================================");
}
}
}
}
public Object getBean(String name) {
return null;
}
}
本文完成了实现任务阶段 一 编写自己 Spring 容器,实现扫描包, 得到 bean 的 class 对象和实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象, 并放入到 Map下一阶段为 初始化 bean
手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】系列
手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器-准备篇【1】
手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器-准备篇【2】
热门专栏推荐
想学习vue的可以看看这个
java基础合集
数据库合集
redis合集
nginx合集
linux合集
等等等还有许多优秀的合集在主页等着大家的光顾感谢大家的支持
欢迎大家加入我的社区 尘觉社区
文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起来评论区一起讨论
希望能和诸佬们一起努力,今后我们一起观看感谢您的阅读
如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力