当前位置: 首页 > news >正文

【spring如何扫描一个路径下被注解修饰的类】

文章目录

  • 为什么需要用到扫描被注解修饰的类
    • demo1
    • demo2:
  • PathMatchingResourcePatternResolver流程分析

为什么需要用到扫描被注解修饰的类

我们利用Spring框架或者SpringBoot框架经常会自己写一些注解,来方便自己的业务使用,但是我们如何获取到被注解修饰的类或方法,是一个值得探讨的问题,下面我们会具体分析一些demo来给出路径下某些被注解修饰的类的方案。

demo1

一、核心方案:使用 ClassPathScanningCandidateComponentProvider
这是 Spring 官方提供的扫描类,用来:
1.按包扫描 class;
2.按注解过滤;
3.支持 classpath 路径;
4.返回每个匹配类的 BeanDefinition。
下面的case是我们获取org.apache.dubbo.springboot.demo下的被@Service修饰的类

 public static void main(String[] args) {// 1️⃣ 创建扫描器ClassPathScanningCandidateComponentProvider scanner =new ClassPathScanningCandidateComponentProvider(false); // false 表示不使用默认过滤器// 2️⃣ 添加注解过滤器(比如扫描 @Service 的类)scanner.addIncludeFilter(new AnnotationTypeFilter(org.springframework.stereotype.Service.class));// 可选:也可以加类型过滤器,比如所有实现 MyInterface 的类// scanner.addIncludeFilter(new AssignableTypeFilter(MyInterface.class));String name = SayController.class.getPackage().getName();System.out.println("name = " + name);// 3️⃣ 扫描指定包路径String basePackage = "org.apache.dubbo.springboot.demo";Set<BeanDefinition> candidates = scanner.findCandidateComponents(basePackage);// 4️⃣ 遍历结果for (BeanDefinition bd : candidates) {String className = bd.getBeanClassName();System.out.println("发现被 @Service 修饰的类: " + className);}}

输出的结果
在这里插入图片描述
ClassPathScanningCandidateComponentProvider
ClassPathScanningCandidateComponentProvider 底层执行的流程:
1.扫描PathMatchingResourcePatternResolver#getResources(“classpath*:org/apache/dubbo/springboot/demo/**/*.class”)
2.扫描出所有 .class 文件;
3.读取字节码(不加载类)→ MetadataReader;
4.判断类上是否包含指定注解;
5.符合条件则返回 ScannedGenericBeanDefinition。

demo2:

使用PathMatchingResourcePatternResolver.class来进行获取路径org.apache.dubbo.springboot.demo下被@RestController注解修饰的类

public static void main(String[] args) throws IOException {String basePackage = "org.apache.dubbo.springboot.demo";String packageSearchPath = "classpath*:" + basePackage.replace('.', '/') + "/**/*.class";PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();Resource[] resources = resolver.getResources(packageSearchPath);SimpleMetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory();for (Resource resource : resources) {MetadataReader metadataReader = readerFactory.getMetadataReader(resource);AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();if (annotationMetadata.hasAnnotation(RestController.class.getName())) {Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(RestController.class.getName());System.out.println("发现 @RestController 类:" + metadataReader.getClassMetadata().getClassName());System.out.println("注解@RestController 类的属性:" + annotationAttributes);}}}

执行结果
在这里插入图片描述

PathMatchingResourcePatternResolver流程分析

在这里插入图片描述
核心PathMatchingResourcePatternResolver.getResource()

   public Resource[] getResources(String locationPattern) throws IOException {//校验路径不为nullAssert.notNull(locationPattern, "Location pattern must not be null");//判断路径的开头是否是classpath*:if (locationPattern.startsWith("classpath*:")) {//AntPathMatcher.isPattern()方法是校验路径中是否含有*等字符,true执行findPathMatchingResources()方法,false执行findAllClassPathResources()方法return this.getPathMatcher().isPattern(locationPattern.substring("classpath*:".length())) ? this.findPathMatchingResources(locationPattern) : this.findAllClassPathResources(locationPattern.substring("classpath*:".length()));} else {int prefixEnd = locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 : locationPattern.indexOf(58) + 1;return this.getPathMatcher().isPattern(locationPattern.substring(prefixEnd)) ? this.findPathMatchingResources(locationPattern) : new Resource[]{this.getResourceLoader().getResource(locationPattern)};}}

findPathMatchingResources方法

protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {//获取我们的根路径String rootDirPath = this.determineRootDir(locationPattern);//将*后面的字段去掉String subPattern = locationPattern.substring(rootDirPath.length());//获取目录下的所有的文件 递归走getResource()Resource[] rootDirResources = this.getResources(rootDirPath);Set<Resource> result = new LinkedHashSet(16);for(Resource rootDirResource : rootDirResources) {rootDirResource = this.resolveRootDirResource(rootDirResource);URL rootDirUrl = rootDirResource.getURL();if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {URL resolvedUrl = (URL)ReflectionUtils.invokeMethod(equinoxResolveMethod, (Object)null, new Object[]{rootDirUrl});if (resolvedUrl != null) {rootDirUrl = resolvedUrl;}rootDirResource = new UrlResource(rootDirUrl);}if (rootDirUrl.getProtocol().startsWith("vfs")) {result.addAll(PathMatchingResourcePatternResolver.VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, this.getPathMatcher()));} else if (!ResourceUtils.isJarURL(rootDirUrl) && !this.isJarResource(rootDirResource)) {result.addAll(this.doFindPathMatchingFileResources(rootDirResource, subPattern));} else {result.addAll(this.doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));}}if (logger.isTraceEnabled()) {logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result);}return (Resource[])result.toArray(new Resource[0]);}

findAllClassPathResources()方法

    protected Resource[] findAllClassPathResources(String location) throws IOException {String path = location;if (location.startsWith("/")) {path = location.substring(1);}//核心点doFindAllClassPathResources()Set<Resource> result = this.doFindAllClassPathResources(path);if (logger.isTraceEnabled()) {logger.trace("Resolved classpath location [" + location + "] to resources " + result);}return (Resource[])result.toArray(new Resource[0]);}protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {//创建一个结果集合Set<Resource> result = new LinkedHashSet(16);//DefaultResourceLoade创建,为什么考虑用DefaultResourceLoader?//JVM 会在以下路径中查找:当前 classpath 目录;所有依赖的 JAR 包;父类加载器的路径(如 AppClassLoader → ExtClassLoader)。这也是为什么即使 com/example/MyService.class 在不同 JAR 中都有,Spring 也能找到多个匹配。ClassLoader cl = this.getClassLoader();//获取路径下的所有的类Enumeration<URL> resourceUrls = cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path);//循环遍历获取到的resourceUrlswhile(resourceUrls.hasMoreElements()) {URL url = (URL)resourceUrls.nextElement();result.add(this.convertClassLoaderURL(url));}if (!StringUtils.hasLength(path)) {this.addAllClassLoaderJarRoots(cl, result);}return result;
}

PathMatchingResourcePatternResolver#doFindPathMatchingFileResources()方法

    protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern) throws IOException {File rootDir;try {//获取根路径rootDir = rootDirResource.getFile().getAbsoluteFile();} catch (FileNotFoundException ex) {if (logger.isDebugEnabled()) {logger.debug("Cannot search for matching files underneath " + rootDirResource + " in the file system: " + ex.getMessage());}return Collections.emptySet();} catch (Exception ex) {if (logger.isInfoEnabled()) {logger.info("Failed to resolve " + rootDirResource + " in the file system: " + ex);}return Collections.emptySet();}//执行核心方法return this.doFindMatchingFileSystemResources(rootDir, subPattern);}

PathMatchingResourcePatternResource#rerrieveMatchingFiles()方法

 protected Set<File> retrieveMatchingFiles(File rootDir, String pattern) throws IOException {//校验路径是否存在if (!rootDir.exists()) {if (logger.isDebugEnabled()) {logger.debug("Skipping [" + rootDir.getAbsolutePath() + "] because it does not exist");}//	不存在直接返回return Collections.emptySet();//判断是否是目录} else if (!rootDir.isDirectory()) {if (logger.isInfoEnabled()) {logger.info("Skipping [" + rootDir.getAbsolutePath() + "] because it does not denote a directory");}//不是直接返回return Collections.emptySet();//判断目录是否可读} else if (!rootDir.canRead()) {if (logger.isInfoEnabled()) {logger.info("Skipping search for matching files underneath directory [" + rootDir.getAbsolutePath() + "] because the application is not allowed to read the directory");}//不可读直接返回return Collections.emptySet();} else {//重新定义路径String fullPattern = StringUtils.replace(rootDir.getAbsolutePath(), File.separator, "/");if (!pattern.startsWith("/")) {fullPattern = fullPattern + "/";}fullPattern = fullPattern + StringUtils.replace(pattern, File.separator, "/");Set<File> result = new LinkedHashSet(8);this.doRetrieveMatchingFiles(fullPattern, rootDir, result);return result;}}

PathMatchingResourcePatternResource#doRetriveMatchingFiles()方法

 protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set<File> result) throws IOException {if (logger.isTraceEnabled()) {logger.trace("Searching directory [" + dir.getAbsolutePath() + "] for files matching pattern [" + fullPattern + "]");}//获取路径下的所有的文件,并进行循环遍历,将匹配到的文件放入集合中,如果是文件夹,继续循环遍历。for(File content : this.listDirectory(dir)) {String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");if (content.isDirectory() && this.getPathMatcher().matchStart(fullPattern, currPath + "/")) {if (!content.canRead()) {if (logger.isDebugEnabled()) {logger.debug("Skipping subdirectory [" + dir.getAbsolutePath() + "] because the application is not allowed to read the directory");}} else {//递归继续循环遍历this.doRetrieveMatchingFiles(fullPattern, content, result);}}//判断是否匹配if (this.getPathMatcher().match(fullPattern, currPath)) {result.add(content);}}}
http://www.dtcms.com/a/508840.html

相关文章:

  • Cuda reduce算子实现与优化
  • 计网4.2 IPV4
  • 做网站需要备案网站建设会计科目
  • 网站开发技术什么软件可以制作图片
  • 解码Linux文件IO之BMP 图像原理与应用
  • 串口转以太网模块在电梯控制柜中的透明改造
  • Git 检出到HEAD 再修改提交commit 会消失解决方案
  • 徐州网站建设方案推广4399电脑版网页链接
  • 利用 Trie 树对仅由小写字母构成的多个字符串按字典序排序
  • 曲沃网站建设网站流量怎么提升
  • 从 KaTeX 到智能渲染:构建 Vue + LLM 的公式可视化体系
  • 大数据做网站流量分析seo推广的特点
  • 网站虚拟交易技术怎么做建站设计公司
  • 龙虎榜——20251020
  • 网站改版原因东莞专业网站推广策划
  • 扎根中亚十三年,科伦药业打造现代化综合性药厂
  • 基于C语言和Ncurses的俄罗斯方块游戏实现
  • 企业网站脚本语言网站代备案公司
  • 网站建设托管预算清单展厅设计培训
  • PCIe协议之 Equalization篇 之 关于 TxSwing 的理解
  • 海康域名网站有做门窗找活的网站吗
  • 福建省龙岩市建设培训中心网站网站内容一样影响收录
  • 流行网站类型大学网站建设宣传方案
  • 久久网站建设巴中市平昌县建设局网站
  • idea整合Git
  • 如何选择性价比高的中药饮片才能确保品质与效果?
  • 设计师网站图片重庆市建设工程信息网官网工程押证
  • 私人程序定制:纳什欺诈谈判
  • 呼和浩特市网站建设什么叫宣传类网站
  • 建设银行网站-个人业务泰州网站建设设计