IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> spring源码之模拟扫描器 -> 正文阅读

[Java知识库]spring源码之模拟扫描器

之前写Junit测试类的时候,我们都会给spring容器手动注入一个配置类,里面会用@ComponentScan来告诉spring需要扫描的路径。如下图:
在这里插入图片描述
那么spring就是通过这个配置类的注解,拿到我们定义的路径,然后从电脑中的绝对路径读取到.class文件进行解析。
大概流程如下:

  1. ConfigrarionClassPostProcessor
  2. 获取包名
  3. 得到路径下的所有文件
  4. 通过ASM的方式读取字节码信息,生成类
  5. 验证是否符合规则,生成beanDefinition
  6. 放入map中

我们可以通过代码模拟一下这个流程

public class MyScaner {

	File f = new File(this.getClass().getResource("/").getPath());
	public List<String> listName = new ArrayList<>();
	public Map<String, AbstractBeanDefinition> map = new HashMap<>();


	//这个方法一旦完成,就是完成了spring的扫描
	//listName中存了左右符合规则的bean名字
	//map中存了所有beanName以及对应的beanDefinition
	public void scan(String packageName) throws ClassNotFoundException {

		System.out.println("packageName = " + packageName);
		String scanPath = "";
		//获取当前类的存放绝对路径
		//  /Users/xxxx/Downloads/idea/spring-framework-5.2.x/spring-example/src/main/java/com/spring/scan
		//  /Users/shang/Downloads/idea/spring-framework-5.2.x/spring-example/out/test/classes/com/spring/scan/bean/A.class
		//  /Users/shang/Downloads/idea/spring-framework-5.2.x/spring-example/out/test/classes/com/spring/scan/bean
		String rootPath = f.getPath();
		System.out.println("rootPath = " + rootPath);

		//传入的包路径将. 替换为\
		//com.spring.scan --- com\spring\scan
		scanPath = packageName.replaceAll("\\.", "/");
		System.out.println("scanPath = " + scanPath);

		//拼接绝对路径
		rootPath = rootPath+ "/"+ scanPath;
		System.out.println("rootPath = " + rootPath);

		//取出文件夹下的文件,这里只是简单取出了,没有处理文件夹中的文件夹
		File rootDir = new File(rootPath);
		String[] list = rootDir.list();
		if (list == null || list.length == 0) {
			System.out.println("bean null");
			return;
		}
		for (String s : list) {
			s = s.replaceAll(".class", "");
			System.out.println("s = " + s);

			String beanName = s.toLowerCase();
			System.out.println("beanName = " + beanName);

			s = packageName + "." + s;
			System.out.println("s = " + s);


			//通过反射的方式获取bean
			//spring中是通过ASM字节码的方式获取,优点是不会提前执行构造方法
			Class<?> clazz = Class.forName(s);

			//如果有Component注解的话才处理
			if (clazz.isAnnotationPresent(Component.class)) {
				GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
				beanDefinition.setBeanClass(clazz);
				
				//如果自定义了bean的作用域的话就放入这个组件中
				if(clazz.isAnnotationPresent(Scope.class)){
					beanDefinition.setScope(clazz.getAnnotation(Scope.class).value());
				}

				map.put(beanName, beanDefinition);
				listName.add(beanName);
			}
		}

	}

}



public class ScanBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {


	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		try{
			MyScaner myScaner = new MyScaner();
			myScaner.scan("com.spring.scan.bean");
			//

			List<String> listName = myScaner.listName;
			Map<String, AbstractBeanDefinition> map = myScaner.map;
			for (String s : listName) {
				registry.registerBeanDefinition(s, map.get(s));

			}
		}catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

	}
}

//注意这三个类,因为是用Junit Test运行的,所以这三个类也要跟测试类在一块
@Component
public class A {

	@PostConstruct
	public void init() {
		System.out.println("A init");
	}
}

public class B {

	@PostConstruct
	public void init() {
		System.out.println("B init");
	}
}

@Component
public class C {

	@PostConstruct
	public void init() {
		System.out.println("C init");
	}
}

public class ScanTest {

	@Test
	public void defaultScanTest() {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.addBeanFactoryPostProcessor(new ScanBeanDefinitionRegistryPostProcessor());
		context.refresh();
	}
}

这个测试的方法与spring不一样的是第4步。

测试方法是通过反射获取类对象;而spring是通过ASM方式获取获取的,这个我也不懂,是一个跟高深的学问。
只是知道他的优势在于,不会提前执行static代码块。
因为如果用反射的方式获取的话,就直接把类加载到JVM了,如果类中有static代码块并且还关联了其他类,那不就要报错了吗。而通过ASM获取的话就不会,只是获取这个文件而已。
这就是spring优秀的地方

PS:
spring的扫描注解什么情况不会生效。
在正常的扫描过程中,如果扫描到的类中,有ComponentScan注解的话,也会读取,然后按照配置继续扫描
如下:

@ComponentScan("com.spring.other")
public class A {

	@PostConstruct
	public void init() {
		System.out.println("A init");
	}
}

但是,这个类如果是通过BeanDefinitionPostProcessor类注册进来的话,ComponentScan就不会生效了,因为扫描的工作在执行BeanDefinitionPostProcessor回调方法之前就已经完成了。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-24 17:59:01  更:2022-05-24 18:00:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:47:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码