001/** 002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.jboot.utils; 017 018import io.jboot.app.config.JbootConfigManager; 019 020import java.io.File; 021import java.io.IOException; 022import java.lang.annotation.Annotation; 023import java.lang.reflect.Modifier; 024import java.net.URL; 025import java.net.URLClassLoader; 026import java.net.URLDecoder; 027import java.util.*; 028import java.util.function.Predicate; 029import java.util.jar.JarEntry; 030import java.util.jar.JarFile; 031import java.util.jar.JarInputStream; 032import java.util.stream.Collectors; 033 034public class ClassScanner { 035 036 private static final Set<Class<?>> appClassesCache = new HashSet<>(); 037 038 public static final Set<String> scanJars = new HashSet<>(); 039 public static final Set<String> excludeJars = new HashSet<>(); 040 041 public static final Set<String> scanClasses = new HashSet<>(); 042 public static final Set<String> excludeClasses = new HashSet<>(); 043 044 // dev模式打开扫描信息打印 045 private static boolean printScannerInfoEnable = false; 046 047 public static boolean isPrintScannerInfoEnable() { 048 return printScannerInfoEnable; 049 } 050 051 public static void setPrintScannerInfoEnable(boolean printScannerInfoEnable) { 052 ClassScanner.printScannerInfoEnable = printScannerInfoEnable; 053 } 054 055 056 public static void addScanJarPrefix(String prefix) { 057 scanJars.add(prefix.toLowerCase().trim()); 058 } 059 060 static { 061 scanJars.add("jboot"); 062 } 063 064 065 public static void addUnscanJarPrefix(String prefix) { 066 excludeJars.add(prefix.toLowerCase().trim()); 067 } 068 069 static { 070 excludeJars.add("jfinal-"); 071 excludeJars.add("cos-"); 072 excludeJars.add("cglib-"); 073 excludeJars.add("undertow-"); 074 excludeJars.add("xnio-"); 075 excludeJars.add("javax."); 076 excludeJars.add("hikaricp-"); 077 excludeJars.add("druid-"); 078 excludeJars.add("mysql-"); 079 excludeJars.add("db2jcc-"); 080 excludeJars.add("db2jcc4-"); 081 excludeJars.add("ojdbc"); 082 excludeJars.add("junit-"); 083 excludeJars.add("junit5-"); 084 excludeJars.add("org.junit"); 085 excludeJars.add("hamcrest-"); 086 excludeJars.add("jboss-"); 087 excludeJars.add("motan-"); 088 excludeJars.add("commons-pool"); 089 excludeJars.add("commons-beanutils"); 090 excludeJars.add("commons-codec"); 091 excludeJars.add("commons-collections"); 092 excludeJars.add("commons-configuration"); 093 excludeJars.add("commons-lang"); 094 excludeJars.add("commons-logging"); 095 excludeJars.add("commons-io"); 096 excludeJars.add("commons-httpclient"); 097 excludeJars.add("commons-fileupload"); 098 excludeJars.add("commons-validator"); 099 excludeJars.add("commons-email"); 100 excludeJars.add("commons-text"); 101 excludeJars.add("commons-cli"); 102 excludeJars.add("commons-math"); 103 excludeJars.add("commons-jxpath"); 104 excludeJars.add("commons-compress"); 105 excludeJars.add("audience-"); 106 excludeJars.add("hessian-"); 107 excludeJars.add("metrics-"); 108 excludeJars.add("javapoet-"); 109 excludeJars.add("netty-"); 110 excludeJars.add("consul-"); 111 excludeJars.add("gson-"); 112 excludeJars.add("zookeeper-"); 113 excludeJars.add("slf4j-"); 114 excludeJars.add("fastjson-"); 115 excludeJars.add("guava-"); 116 excludeJars.add("failureaccess-"); 117 excludeJars.add("listenablefuture-"); 118 excludeJars.add("jsr305-"); 119 excludeJars.add("checker-qual-"); 120 excludeJars.add("error_prone_annotations-"); 121 excludeJars.add("j2objc-"); 122 excludeJars.add("animal-sniffer-"); 123 excludeJars.add("cron4j-"); 124 excludeJars.add("jedis-"); 125 excludeJars.add("lettuce-"); 126 excludeJars.add("reactor-"); 127 excludeJars.add("fst-"); 128 excludeJars.add("kryo-"); 129 excludeJars.add("jackson-"); 130 excludeJars.add("javassist-"); 131 excludeJars.add("objenesis-"); 132 excludeJars.add("reflectasm-"); 133 excludeJars.add("asm-"); 134 excludeJars.add("minlog-"); 135 excludeJars.add("jsoup-"); 136 excludeJars.add("ons-client-"); 137 excludeJars.add("amqp-client-"); 138 excludeJars.add("ehcache-"); 139 excludeJars.add("sharding-"); 140 excludeJars.add("snakeyaml-"); 141 excludeJars.add("groovy-"); 142 excludeJars.add("profiler-"); 143 excludeJars.add("joda-time-"); 144 excludeJars.add("shiro-"); 145 excludeJars.add("dubbo-"); 146 excludeJars.add("curator-"); 147 excludeJars.add("resteasy-"); 148 excludeJars.add("reactive-"); 149 excludeJars.add("validation-"); 150 excludeJars.add("httpclient-"); 151 excludeJars.add("httpcore-"); 152 excludeJars.add("httpmime-"); 153 excludeJars.add("jcip-"); 154 excludeJars.add("jcl-"); 155 excludeJars.add("microprofile-"); 156 excludeJars.add("org.osgi"); 157 excludeJars.add("zkclient-"); 158 excludeJars.add("jjwt-"); 159 excludeJars.add("okhttp-"); 160 excludeJars.add("okio-"); 161 excludeJars.add("zbus-"); 162 excludeJars.add("swagger-"); 163 excludeJars.add("j2cache-"); 164 excludeJars.add("caffeine-"); 165 excludeJars.add("jline-"); 166 excludeJars.add("qpid-"); 167 excludeJars.add("geronimo-"); 168 excludeJars.add("activation-"); 169 excludeJars.add("org.abego"); 170 excludeJars.add("antlr-"); 171 excludeJars.add("antlr4-"); 172 excludeJars.add("st4-"); 173 excludeJars.add("icu4j-"); 174 excludeJars.add("idea_rt"); 175 excludeJars.add("mrjtoolkit"); 176 excludeJars.add("logback-"); 177 excludeJars.add("log4j-"); 178 excludeJars.add("log4j2-"); 179 excludeJars.add("aliyun-java-sdk-"); 180 excludeJars.add("aliyun-sdk-"); 181 excludeJars.add("archaius-"); 182 excludeJars.add("aopalliance-"); 183 excludeJars.add("hdrhistogram-"); 184 excludeJars.add("jdom-"); 185 excludeJars.add("rxjava-"); 186 excludeJars.add("jersey-"); 187 excludeJars.add("stax-"); 188 excludeJars.add("stax2-"); 189 excludeJars.add("jettison-"); 190 excludeJars.add("commonmark-"); 191 excludeJars.add("jaxb-"); 192 excludeJars.add("json-20"); 193 excludeJars.add("jcseg-"); 194 excludeJars.add("lucene-"); 195 excludeJars.add("elasticsearch-"); 196 excludeJars.add("jopt-"); 197 excludeJars.add("httpasyncclient-"); 198 excludeJars.add("jna-"); 199 excludeJars.add("lang-mustache-client-"); 200 excludeJars.add("parent-join-client-"); 201 excludeJars.add("rank-eval-client-"); 202 excludeJars.add("aggs-matrix-stats-client-"); 203 excludeJars.add("t-digest-"); 204 excludeJars.add("compiler-"); 205 excludeJars.add("hppc-"); 206 excludeJars.add("libthrift-"); 207 excludeJars.add("seata-"); 208 excludeJars.add("eureka-"); 209 excludeJars.add("netflix-"); 210 excludeJars.add("nacos-"); 211 excludeJars.add("apollo-"); 212 excludeJars.add("guice-"); 213 excludeJars.add("servlet-"); 214 excludeJars.add("debugger-agent.jar"); 215 excludeJars.add("xpp3_min-"); 216 excludeJars.add("latency"); 217 excludeJars.add("micrometer-"); 218 excludeJars.add("xstream-"); 219 excludeJars.add("jsr311-"); 220 excludeJars.add("servo-"); 221 excludeJars.add("compactmap-"); 222 excludeJars.add("dexx-"); 223 excludeJars.add("spotbugs-"); 224 excludeJars.add("xmlpull-"); 225 excludeJars.add("shardingsphere-"); 226 excludeJars.add("sentinel-"); 227 excludeJars.add("spring-"); 228 excludeJars.add("simpleclient-"); 229 excludeJars.add("breeze-"); 230 excludeJars.add("config-"); 231 excludeJars.add("encrypt-core-"); 232 excludeJars.add("lombok-"); 233 excludeJars.add("hutool-"); 234 excludeJars.add("jakarta."); 235 excludeJars.add("protostuff-"); 236 excludeJars.add("poi-"); 237 excludeJars.add("easypoi-"); 238 excludeJars.add("ognl-"); 239 excludeJars.add("xmlbeans-"); 240 excludeJars.add("master-slave-core-"); 241 excludeJars.add("shadow-core-rewrite-"); 242 excludeJars.add("apiguardian-api-"); 243 excludeJars.add("opentest4j-"); 244 excludeJars.add("opentracing-"); 245 excludeJars.add("freemarker-"); 246 excludeJars.add("protobuf-"); 247 excludeJars.add("jdom2-"); 248 excludeJars.add("useragentutils-"); 249 excludeJars.add("common-io-"); 250 excludeJars.add("common-image-"); 251 excludeJars.add("common-lang-"); 252 excludeJars.add("imageio-"); 253 excludeJars.add("curvesapi-"); 254 excludeJars.add("myexcel-"); 255 excludeJars.add("oshi-"); 256 excludeJars.add("classmate-"); 257 excludeJars.add("hibernate-"); 258 excludeJars.add("aspectjweaver-"); 259 excludeJars.add("aspectjrt-"); 260 excludeJars.add("simpleclient_"); 261 excludeJars.add("rocketmq-"); 262 excludeJars.add("clickhouse-"); 263 excludeJars.add("lz4-"); 264 excludeJars.add("commons-digester-"); 265 excludeJars.add("opencc4j-"); 266 excludeJars.add("heaven-"); 267 excludeJars.add("tinypinyin-"); 268 excludeJars.add("jieba-"); 269 excludeJars.add("ahocorasick-"); 270 excludeJars.add("kotlin-"); 271 excludeJars.add("xml-apis-"); 272 excludeJars.add("dom4j-"); 273 excludeJars.add("ini4j-"); 274 excludeJars.add("cache-api-"); 275 excludeJars.add("byte-buddy-"); 276 excludeJars.add("jodd-"); 277 excludeJars.add("redisson-"); 278 excludeJars.add("bcprov-"); 279 excludeJars.add("pay-java-"); 280 excludeJars.add("alipay-sdk-"); 281 excludeJars.add("mapper-extras-"); 282 excludeJars.add("org.jacoco"); 283 excludeJars.add("jxl-"); 284 excludeJars.add("jxls-"); 285 excludeJars.add("jstl-"); 286 excludeJars.add("batik-"); 287 excludeJars.add("xmlsec-"); 288 excludeJars.add("pdfbox-"); 289 excludeJars.add("fontbox-"); 290 excludeJars.add("xk-time-"); 291 excludeJars.add("geohash-"); 292 excludeJars.add("ezmorph-"); 293 excludeJars.add("async-http-"); 294 excludeJars.add("jsr-"); 295 excludeJars.add("jsr250"); 296 excludeJars.add("pinyin4j"); 297 excludeJars.add("ijpay-"); 298 excludeJars.add("wildfly-"); 299 excludeJars.add("liquibase-"); 300 excludeJars.add("flowable-"); 301 excludeJars.add("mybatis-"); 302 excludeJars.add("ip2region-"); 303 excludeJars.add("java-uuid-generator-"); 304 excludeJars.add("quartz-"); 305 excludeJars.add("elasticjob-"); 306 excludeJars.add("reflections-"); 307 excludeJars.add("jts-"); 308 excludeJars.add("json-"); 309 excludeJars.add("httpclient5-"); 310 excludeJars.add("httpcore5-"); 311 excludeJars.add("jul-to-"); 312 excludeJars.add("calcite-"); 313 excludeJars.add("avatica-"); 314 excludeJars.add("encoder-"); 315 excludeJars.add("aggdesigner-"); 316 excludeJars.add("uzaygezen-"); 317 excludeJars.add("memory-"); 318 excludeJars.add("commons-"); 319 excludeJars.add("accessors-"); 320 excludeJars.add("sketches-"); 321 excludeJars.add("h2-"); 322 excludeJars.add("cosid-"); 323 excludeJars.add("mchange-"); 324 excludeJars.add("janino-"); 325 excludeJars.add("jnanoid-"); 326 excludeJars.add("proj4j-"); 327 excludeJars.add("sparsebitset-"); 328 excludeJars.add("captcha-"); 329 excludeJars.add("cryptokit"); 330 excludeJars.add("isec-"); 331 excludeJars.add("jurt-"); 332 excludeJars.add("minio-"); 333 excludeJars.add("logging-"); 334 excludeJars.add("simple-xml-"); 335 excludeJars.add("jodconverter-"); 336 excludeJars.add("credentials-"); 337 excludeJars.add("unoil-"); 338 excludeJars.add("endpoint-"); 339 excludeJars.add("ridl-"); 340 excludeJars.add("tencentcloud-"); 341 excludeJars.add("yauaa-"); 342 excludeJars.add("tea-"); 343 excludeJars.add("fr."); 344 excludeJars.add("vod20"); 345 excludeJars.add("juh-"); 346 excludeJars.add("prefixmap-"); 347 excludeJars.add("dmjdbcdriver"); 348 } 349 350 351 public static void addUnscanClassPrefix(String prefix) { 352 excludeClasses.add(prefix.trim()); 353 } 354 355 static { 356 excludeClasses.add("java."); 357 excludeClasses.add("javax."); 358 excludeClasses.add("junit."); 359 excludeClasses.add("jline."); 360 excludeClasses.add("redis."); 361 excludeClasses.add("lombok."); 362 excludeClasses.add("oshi."); 363 excludeClasses.add("jodd."); 364 excludeClasses.add("javassist."); 365 excludeClasses.add("google."); 366 excludeClasses.add("com.jfinal."); 367 excludeClasses.add("com.aliyuncs."); 368 excludeClasses.add("com.carrotsearch."); 369 excludeClasses.add("org.aopalliance."); 370 excludeClasses.add("org.apache."); 371 excludeClasses.add("org.nustaq."); 372 excludeClasses.add("net.sf."); 373 excludeClasses.add("org.slf4j."); 374 excludeClasses.add("org.antlr."); 375 excludeClasses.add("org.jboss."); 376 excludeClasses.add("org.checkerframework."); 377 excludeClasses.add("org.jsoup."); 378 excludeClasses.add("org.objenesis."); 379 excludeClasses.add("org.ow2."); 380 excludeClasses.add("org.reactivest."); 381 excludeClasses.add("org.yaml."); 382 excludeClasses.add("org.checker"); 383 excludeClasses.add("org.codehaus"); 384 excludeClasses.add("org.commonmark"); 385 excludeClasses.add("org.jdom2."); 386 excludeClasses.add("org.aspectj."); 387 excludeClasses.add("org.hibernate."); 388 excludeClasses.add("org.ahocorasick."); 389 excludeClasses.add("org.lionsoul.jcseg."); 390 excludeClasses.add("org.ini4j."); 391 excludeClasses.add("org.jetbrains."); 392 excludeClasses.add("org.jacoco."); 393 excludeClasses.add("org.xnio."); 394 excludeClasses.add("org.bouncycastle."); 395 excludeClasses.add("org.elasticsearch."); 396 excludeClasses.add("org.hamcrest."); 397 excludeClasses.add("org.objectweb."); 398 excludeClasses.add("org.joda."); 399 excludeClasses.add("org.wildfly."); 400 excludeClasses.add("org.owasp."); 401 excludeClasses.add("aj.org."); 402 excludeClasses.add("ch.qos."); 403 excludeClasses.add("joptsimple."); 404 excludeClasses.add("com.alibaba.csp."); 405 excludeClasses.add("com.alibaba.nacos."); 406 excludeClasses.add("com.alibaba.druid."); 407 excludeClasses.add("com.alibaba.fastjson."); 408 excludeClasses.add("com.aliyun.open"); 409 excludeClasses.add("com.caucho"); 410 excludeClasses.add("com.codahale"); 411 excludeClasses.add("com.ctrip.framework.apollo"); 412 excludeClasses.add("com.ecwid."); 413 excludeClasses.add("com.esotericsoftware."); 414 excludeClasses.add("com.fasterxml."); 415 excludeClasses.add("com.github."); 416 excludeClasses.add("io.github."); 417 excludeClasses.add("com.google."); 418 excludeClasses.add("metrics_influxdb."); 419 excludeClasses.add("com.rabbitmq."); 420 excludeClasses.add("com.squareup."); 421 excludeClasses.add("com.sun."); 422 excludeClasses.add("com.typesafe."); 423 excludeClasses.add("com.weibo.api.motan."); 424 excludeClasses.add("com.zaxxer."); 425 excludeClasses.add("com.mysql."); 426 excludeClasses.add("com.papertrail."); 427 excludeClasses.add("com.egzosn."); 428 excludeClasses.add("com.alipay.api"); 429 excludeClasses.add("org.gjt."); 430 excludeClasses.add("org.fusesource."); 431 excludeClasses.add("org.redisson."); 432 excludeClasses.add("io.dropwizard"); 433 excludeClasses.add("io.prometheus"); 434 excludeClasses.add("io.jsonwebtoken"); 435 excludeClasses.add("io.lettuce"); 436 excludeClasses.add("reactor.adapter"); 437 excludeClasses.add("io.seata."); 438 excludeClasses.add("io.swagger."); 439 excludeClasses.add("io.undertow."); 440 excludeClasses.add("io.netty."); 441 excludeClasses.add("io.opentracing."); 442 excludeClasses.add("it.sauronsoftware"); 443 excludeClasses.add("net.oschina.j2cache"); 444 excludeClasses.add("net.bytebuddy"); 445 excludeClasses.add("cn.hutool."); 446 excludeClasses.add("com.dyuproject."); 447 excludeClasses.add("io.protostuff."); 448 excludeClasses.add("io.reactivex."); 449 excludeClasses.add("freemarker."); 450 excludeClasses.add("com.twelvemonkeys."); 451 excludeClasses.add("eu.bitwalker."); 452 excludeClasses.add("jstl."); 453 excludeClasses.add("jxl."); 454 excludeClasses.add("org.jxls"); 455 excludeClasses.add("org.kordamp"); 456 excludeClasses.add("org.mybatis"); 457 excludeClasses.add("org.lisonsoul"); 458 excludeClasses.add("org.flowable"); 459 } 460 461 462 public static void addScanClassPrefix(String prefix) { 463 scanClasses.add(prefix.toLowerCase().trim()); 464 } 465 466 static { 467 scanClasses.add("io.jboot.support.shiro.directives"); 468 } 469 470 static { 471 String scanJarPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.scanJarPrefix"); 472 if (scanJarPrefix != null) { 473 String[] prefixes = scanJarPrefix.split(","); 474 for (String prefix : prefixes) { 475 if (prefix != null && prefix.trim().length() > 0) { 476 addScanJarPrefix(prefix.trim()); 477 } 478 } 479 } 480 481 String unScanJarPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.unScanJarPrefix"); 482 if (unScanJarPrefix != null) { 483 String[] prefixes = unScanJarPrefix.split(","); 484 for (String prefix : prefixes) { 485 if (prefix != null && prefix.trim().length() > 0) { 486 addUnscanJarPrefix(prefix.trim()); 487 } 488 } 489 } 490 491 String unScanClassPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.unScanClassPrefix"); 492 if (unScanClassPrefix != null) { 493 String[] prefixes = unScanClassPrefix.split(","); 494 for (String prefix : prefixes) { 495 if (prefix != null && prefix.trim().length() > 0) { 496 addUnscanClassPrefix(prefix.trim()); 497 } 498 } 499 } 500 501 String scanClassPrefix = JbootConfigManager.me().getConfigValue("jboot.app.scanner.scanClassPrefix"); 502 if (scanClassPrefix != null) { 503 String[] prefixes = scanClassPrefix.split(","); 504 for (String prefix : prefixes) { 505 if (prefix != null && prefix.trim().length() > 0) { 506 addScanClassPrefix(prefix.trim()); 507 } 508 } 509 } 510 511 } 512 513 public static <T> List<Class<T>> scanSubClass(Class<T> pclazz) { 514 return scanSubClass(pclazz, false); 515 } 516 517 518 public static <T> List<Class<T>> scanSubClass(Class<T> pclazz, boolean instantiable) { 519 initIfNecessary(); 520 List<Class<T>> classes = new ArrayList<>(); 521 findChildClasses(classes, pclazz, instantiable); 522 return classes; 523 } 524 525 public static List<Class> scanClass() { 526 return scanClass(false); 527 } 528 529 public static List<Class> scanClass(boolean isInstantiable) { 530 531 initIfNecessary(); 532 533 if (!isInstantiable) { 534 return new ArrayList<>(appClassesCache); 535 } 536 537 return scanClass(ClassScanner::isInstantiable); 538 539 } 540 541 public static List<Class> scanClass(Predicate<Class> filter) { 542 543 initIfNecessary(); 544 545 return appClassesCache.stream() 546 .filter(filter) 547 .collect(Collectors.toList()); 548 549 } 550 551 public static void clearAppClassesCache() { 552 appClassesCache.clear(); 553 } 554 555 556 private static boolean isInstantiable(Class clazz) { 557 return !clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()); 558 } 559 560 561 public static List<Class> scanClassByAnnotation(Class annotationClass, boolean instantiable) { 562 initIfNecessary(); 563 564 List<Class> list = new ArrayList<>(); 565 for (Class clazz : appClassesCache) { 566 Annotation annotation = clazz.getAnnotation(annotationClass); 567 if (annotation == null) { 568 continue; 569 } 570 571 if (instantiable && !isInstantiable(clazz)) { 572 continue; 573 } 574 575 list.add(clazz); 576 } 577 578 return list; 579 } 580 581 private static void initIfNecessary() { 582 if (appClassesCache.isEmpty()) { 583 initAppClasses(); 584 } 585 } 586 587 588 private static <T> void findChildClasses(List<Class<T>> classes, Class<T> parent, boolean instantiable) { 589 for (Class clazz : appClassesCache) { 590 591 if (!parent.isAssignableFrom(clazz)) { 592 continue; 593 } 594 595 if (instantiable && !isInstantiable(clazz)) { 596 continue; 597 } 598 599 classes.add(clazz); 600 } 601 } 602 603 604 private static void initAppClasses() { 605 606 Set<String> jarPaths = new HashSet<>(); 607 Set<String> classPaths = new HashSet<>(); 608 609 610 // jdk8 及以下、 611 // tomcat 容器、 612 // jfinal-undertow、 613 // 以上三种加载模式通过 classloader 获取 614 findClassPathsAndJarsByClassloader(jarPaths, classPaths, ClassScanner.class.getClassLoader()); 615 616 //jdk9+ 等其他方式通过 classpath 获取 617 findClassPathsAndJarsByClassPath(jarPaths, classPaths); 618 619 620 String tomcatClassPath = null; 621 622 for (String classPath : classPaths) { 623 //过滤tomcat自身的lib 以及 bin 下的jar 624 File tomcatApiJarFile = new File(classPath, "tomcat-api.jar"); 625 File tomcatJuliJarFile = new File(classPath, "tomcat-juli.jar"); 626 if (tomcatApiJarFile.exists() || tomcatJuliJarFile.exists()) { 627 tomcatClassPath = tomcatApiJarFile 628 .getParentFile() 629 .getParentFile().getAbsolutePath().replace('\\', '/'); 630 continue; 631 } 632 633 if (isPrintScannerInfoEnable()) { 634 System.out.println("Jboot Scan ClassPath: " + classPath); 635 } 636 637 addClassesFromClassPath(classPath); 638 } 639 640 for (String jarPath : jarPaths) { 641 642 //过滤 tomcat 的 jar,但是不能过滤 webapps 目录下的 643 if (tomcatClassPath != null 644 && jarPath.startsWith(tomcatClassPath) 645 && !jarPath.contains("webapps")) { 646 continue; 647 } 648 649 if (!isIncludeJar(jarPath)) { 650 continue; 651 } 652 653 if (isPrintScannerInfoEnable()) { 654 System.out.println("Jboot Scan Jar: " + jarPath); 655 } 656 657 addClassesFromJar(jarPath); 658 } 659 660 661 } 662 663 private static void addClassesFromJar(String jarPath) { 664 JarFile jarFile = null; 665 try { 666 jarFile = new JarFile(jarPath); 667 Enumeration<JarEntry> entries = jarFile.entries(); 668 while (entries.hasMoreElements()) { 669 JarEntry jarEntry = entries.nextElement(); 670 String entryName = jarEntry.getName(); 671 if (jarEntry.isDirectory() && entryName.startsWith("BOOT-INF/classes/")) { 672 673 if (isPrintScannerInfoEnable()) { 674 System.out.println("Jboot Scan entryName: " + entryName); 675 } 676 677 if (entryName.endsWith(".class")) { 678 String className = entryName.replace("/", ".").substring(0, entryName.length() - 6); 679 addClass(classForName(className)); 680 } 681 } else { 682 if (entryName.endsWith(".class")) { 683 String className = entryName.replace("/", ".").substring(0, entryName.length() - 6); 684 addClass(classForName(className)); 685 } else if (entryName.startsWith("BOOT-INF/lib/") && entryName.endsWith(".jar")) { 686 if (!isIncludeJar(entryName)) { 687 continue; 688 } 689 690 if (isPrintScannerInfoEnable()) { 691 System.out.println("Jboot Scan Jar: " + entryName); 692 } 693 694 try (JarInputStream jarStream = new JarInputStream(jarFile 695 .getInputStream(jarEntry));) { 696 JarEntry innerEntry = jarStream.getNextJarEntry(); 697 while (innerEntry != null) { 698 if (!innerEntry.isDirectory()) { 699 String nestedEntryName = innerEntry.getName(); 700 if (nestedEntryName.endsWith(".class")) { 701 String className = nestedEntryName.replace("/", ".").substring(0, nestedEntryName.length() - 6); 702 addClass(classForName(className)); 703 } 704 } 705 innerEntry = jarStream.getNextJarEntry(); 706 } 707 } 708 } 709 } 710 } 711 } catch (IOException e1) { 712 } finally { 713 if (jarFile != null) { 714 try { 715 jarFile.close(); 716 } catch (IOException e) { 717 } 718 } 719 } 720 } 721 722 723 private static void addClassesFromClassPath(String classPath) { 724 725 List<File> classFileList = new ArrayList<>(); 726 scanClassFile(classFileList, classPath); 727 728 for (File file : classFileList) { 729 730 int start = classPath.length(); 731 int end = file.toString().length() - ".class".length(); 732 733 String classFile = file.toString().substring(start + 1, end); 734 String className = classFile.replace(File.separator, "."); 735 736 addClass(classForName(className)); 737 } 738 } 739 740 private static void addClass(Class clazz) { 741 if (clazz != null && isNotExcludeClass(clazz.getName())) { 742 appClassesCache.add(clazz); 743 } 744 } 745 746 //用于在进行 fatjar 打包时,提高性能 747 private static boolean isNotExcludeClass(String clazzName) { 748 for (String prefix : scanClasses) { 749 if (clazzName.startsWith(prefix)) { 750 return true; 751 } 752 } 753 for (String prefix : excludeClasses) { 754 if (clazzName.startsWith(prefix)) { 755 return false; 756 } 757 } 758 return true; 759 } 760 761 762 private static void findClassPathsAndJarsByClassloader(Set<String> jarPaths, Set<String> classPaths, ClassLoader classLoader) { 763 try { 764 URL[] urls = null; 765 if (classLoader instanceof URLClassLoader) { 766 URLClassLoader ucl = (URLClassLoader) classLoader; 767 urls = ucl.getURLs(); 768 } 769 if (urls != null) { 770 for (URL url : urls) { 771 String path = url.getPath(); 772 path = URLDecoder.decode(path, "UTF-8"); 773 774 // path : /d:/xxx 775 if (path.startsWith("/") && path.indexOf(":") == 2) { 776 path = path.substring(1); 777 } 778 779 if (!path.toLowerCase().endsWith(".jar")) { 780 if (path.toLowerCase().endsWith("!/") || path.toLowerCase().endsWith("!")) { 781 } else { 782 classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); 783 } 784 } else { 785 jarPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); 786 } 787 } 788 } 789 } catch (Exception ex) { 790 ex.printStackTrace(); 791 } 792 793 ClassLoader parent = classLoader.getParent(); 794 if (parent != null) { 795 findClassPathsAndJarsByClassloader(jarPaths, classPaths, parent); 796 } 797 } 798 799 private static void findClassPathsAndJarsByClassPath(Set<String> jarPaths, Set<String> classPaths) { 800 String classPath = System.getProperty("java.class.path"); 801 if (classPath == null || classPath.trim().length() == 0) { 802 return; 803 } 804 String[] classPathArray = classPath.split(File.pathSeparator); 805 if (classPathArray == null || classPathArray.length == 0) { 806 return; 807 } 808 for (String path : classPathArray) { 809 path = path.trim(); 810 811 if (path.startsWith("./")) { 812 path = path.substring(2); 813 } 814 815 if (path.startsWith("/") && path.indexOf(":") == 2) { 816 path = path.substring(1); 817 } 818 try { 819 if (!path.toLowerCase().endsWith(".jar") && !jarPaths.contains(path)) { 820 if (path.toLowerCase().endsWith("!/") || path.toLowerCase().endsWith("!")) { 821 } else { 822 classPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); 823 } 824 } else { 825 jarPaths.add(new File(path).getCanonicalPath().replace('\\', '/')); 826 } 827 } catch (IOException e) { 828 } 829 } 830 } 831 832 833 private static boolean isIncludeJar(String path) { 834 835 String jarName = new File(path).getName().toLowerCase(); 836 837 for (String include : scanJars) { 838 if (jarName.startsWith(include)) { 839 return true; 840 } 841 } 842 843 for (String exclude : excludeJars) { 844 if (jarName.startsWith(exclude)) { 845 return false; 846 } 847 } 848 849 //from jre lib 850 if (path.contains("/jre/lib")) { 851 return false; 852 } 853 854 //from java home 855 if (getJavaHome() != null 856 && path.startsWith(getJavaHome())) { 857 return false; 858 } 859 860 return true; 861 } 862 863 864 @SuppressWarnings("unchecked") 865 private static Class classForName(String className) { 866 try { 867 ClassLoader cl = Thread.currentThread().getContextClassLoader(); 868 return Class.forName(className, false, cl); 869 } catch (Throwable ex) { 870 //ignore 871 } 872 return null; 873 } 874 875 876 private static void scanClassFile(List<File> fileList, String path) { 877 File[] files = new File(path).listFiles(); 878 if (null == files || files.length == 0) { 879 return; 880 } 881 for (File file : files) { 882 if (file.isDirectory()) { 883 scanClassFile(fileList, file.getAbsolutePath()); 884 } else if (file.getName().endsWith(".class")) { 885 fileList.add(file); 886 } 887 } 888 } 889 890 891 private static String javaHome; 892 893 private static String getJavaHome() { 894 if (javaHome == null) { 895 try { 896 String javaHomeString = System.getProperty("java.home"); 897 if (javaHomeString != null && javaHomeString.trim().length() > 0) { 898 javaHome = new File(javaHomeString, "..").getCanonicalPath().replace('\\', '/'); 899 } 900 } catch (Exception e) { 901 e.printStackTrace(); 902 } 903 } 904 return javaHome; 905 } 906 907}