在Spring中写下这样的一段代码,Spring是怎样找到这个类的?
ClassLoader可以加载类,但是却没有运行时API取出某个package下面的所有类。既然没有运行时API可以供调用,那么,Spring是怎样@Aspect @Component public class LoggerAspect { }
找到这样的一个类,并读取它的注解?
前提:这里调查的问题是怎样遍历某一个包下的所有类,即一个包下有哪些类。
下面的代码就是Spring的找到classpath下的类的方法:
通过源码跟踪,发现Spring使用ClassLoader取出URL的枚举值,如下:PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); resolver.getResources("classpath*:org/springframework/core/io/support/**/*.class");
其中的path是org/springframework/core/io/support/Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
这个URL的格式如下:
jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework/spring-core/4.2.6.RELEASE/a1c6ef01f18888f51fc5054c65ef4787b7cf0a1e/spring-core-4.2.6.RELEASE.jar!/org/springframework/core/io/support/ 这是一个jar文件,然后Spring会读取这个jar文件,然后遍历jar文件里的内容,进行路径的匹配,如下:
<pre name="code" class="html">...
if (con instanceof JarURLConnection) { // Should usually be the case for traditional JAR files. JarURLConnection jarCon = (JarURLConnection) con; ResourceUtils.useCachesIfNecessary(jarCon); jarFile = jarCon.getJarFile(); jarFileUrl = jarCon.getJarFileURL().toExternalForm(); JarEntry jarEntry = jarCon.getJarEntry(); rootEntryPath = (jarEntry != null ? jarEntry.getName() : ""); }
...
Set<Resource> result = new LinkedHashSet<Resource>(8); for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) { JarEntry entry = entries.nextElement(); String entryPath = entry.getName(); if (entryPath.startsWith(rootEntryPath)) { String relativePath = entryPath.substring(rootEntryPath.length()); if (getPathMatcher().match(subPattern, relativePath)) { result.add(rootDirResource.createRelative(relativePath)); } } }
...
再重复上面的过程,
1.首先使用ClassLoader取出哪些目录或jar文件包含参数路径,如下例:
目录:
0 = {UrlResource@7967} "URL [file:/Users/lvtu/buzhidaolvtu/boxfish-pay/pay-nami/build/classes/main/cn/boxfish/pay/]" 1 = {UrlResource@7968} "URL [file:/Users/lvtu/buzhidaolvtu/boxfish-pay/pay-common/build/classes/main/cn/boxfish/pay/]" 2 = {UrlResource@7969} "URL [file:/Users/lvtu/buzhidaolvtu/boxfish-pay/pay-api/build/classes/main/cn/boxfish/pay/]"
2.遍历目录或jar文件,取出目录内的所有文件或jar文件内的所有文件jar文件: 0 = {UrlResource@7552} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-devtools/1.3.5.RELEASE/ab035bdc1e6297ff4c8676edd93ed307b11353c2/spring-boot-devtools-1.3.5.RELEASE.jar!/org/springframework/]" 1 = {UrlResource@7553} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework/spring-aspects/4.2.6.RELEASE/7ed7b15e95eedd551ac979bd8c295256d96cf2ff/spring-aspects-4.2.6.RELEASE.jar!/org/springframework/]" 2 = {UrlResource@7554} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework.security/spring-security-web/4.1.0.RELEASE/b0996431b0ebaa36794d1f2c81ecceaeb6ddece3/spring-security-web-4.1.0.RELEASE.jar!/org/springframework/]" 3 = {UrlResource@7555} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework.security/spring-security-config/4.1.0.RELEASE/98b549f97d9544c590ba656fb8a2d5ab369b410a/spring-security-config-4.1.0.RELEASE.jar!/org/springframework/]" 4 = {UrlResource@7556} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot/1.3.5.RELEASE/b218ba5f3bd01e657fbde9b085722da1fafa4f8a/spring-boot-1.3.5.RELEASE.jar!/org/springframework/]" 5 = {UrlResource@7557} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-autoconfigure/1.3.5.RELEASE/2bcfa86bb3afd95eff5252db6d78f2693b706997/spring-boot-autoconfigure-1.3.5.RELEASE.jar!/org/springframework/]" 6 = {UrlResource@7558} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.thymeleaf/thymeleaf-spring4/2.1.4.RELEASE/e5677e7daedfd05e001945abf4295305ac23826b/thymeleaf-spring4-2.1.4.RELEASE.jar!/org/springframework/]" 7 = {UrlResource@7559} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-actuator/1.3.5.RELEASE/97b25693835ca5bf436dea760fe337833d6aa51b/spring-boot-actuator-1.3.5.RELEASE.jar!/org/springframework/]" 8 = {UrlResource@7560} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework/spring-web/4.2.6.RELEASE/d5ce949da3f3266f118ed899a153413613b503ad/spring-web-4.2.6.RELEASE.jar!/org/springframework/]" 9 = {UrlResource@7561} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework/spring-webmvc/4.2.6.RELEASE/7c7ea475d33287e0e3a92e98ccbe0ad6a0dbb9ca/spring-webmvc-4.2.6.RELEASE.jar!/org/springframework/]" 10 = {UrlResource@7562} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework.security/spring-security-core/4.0.4.RELEASE/67e6eccc73a9887a7ca262c7cac20f9b36ce5a5d/spring-security-core-4.0.4.RELEASE.jar!/org/springframework/]" 11 = {UrlResource@7563} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework/spring-beans/4.2.6.RELEASE/d4a319fb4d949fb6313f45c929947b9b4e26283e/spring-beans-4.2.6.RELEASE.jar!/org/springframework/]" 12 = {UrlResource@7564} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework/spring-context/4.2.6.RELEASE/bbf3c8526fe37bb341507f28db17882d4348dbca/spring-context-4.2.6.RELEASE.jar!/org/springframework/]" 13 = {UrlResource@7565} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework/spring-core/4.2.6.RELEASE/a1c6ef01f18888f51fc5054c65ef4787b7cf0a1e/spring-core-4.2.6.RELEASE.jar!/org/springframework/]" 14 = {UrlResource@7566} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework/spring-expression/4.2.6.RELEASE/c0182d73f348ab11d51d45cbe29f3820c32d0ccc/spring-expression-4.2.6.RELEASE.jar!/org/springframework/]" 15 = {UrlResource@7567} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework/spring-aop/4.2.6.RELEASE/5efbfccb19efda2956b8977561bf4da6b15b0d0e/spring-aop-4.2.6.RELEASE.jar!/org/springframework/]" 16 = {UrlResource@7568} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework/spring-jdbc/4.2.6.RELEASE/16075bfb0901a5b89c5be975365ff09b2df04abb/spring-jdbc-4.2.6.RELEASE.jar!/org/springframework/]" 17 = {UrlResource@7569} "URL [jar:file:/Users/lvtu/.gradle/caches/modules-2/files-2.1/org.springframework/spring-tx/4.2.6.RELEASE/ba7502c0644414748b1eeb65b4193b05d335a110/spring-tx-4.2.6.RELEASE.jar!/org/springframework/]"
3.根据package筛选匹配的文件
4.找到了所有的class文件,也就是找到了所有的class类的全限定名。
5.Spring就可以根据这些全限定名加载类或使用类,通过容器管理这些类。
说明:有些步骤是猜测的,并没有追踪源代码。只是认为这样能实现就结束调查了。
以上是Spring的搜索类的方法。
下面的链接是JVM搜索类的方法:
https://docs.oracle.com/javase/8/docs/technotes/tools/findingclasses.html
jvm会根据classpath搜索class文件,然后加载该类。如果该文件存在多处,那么只有先被搜索到的文件才会被加载。