组件扫描到底在做什么、代价在哪?它会加载类吗?过滤器怎么用?为什么主类不能放在默认包?两个包里同名的类会怎样?这篇在 Spring Framework 7.0.9 上用脚本生成的类逐项量出来。
@Component 数 | scan() 耗时 | 扫描时新加载的类 | BeanDefinition 数 | refresh() 耗时(实例化) | refresh 时新加载的类 100 | 41 ms | 205 | 105 | 97 ms | 471 1000 | 144 ms | 205 | 1005 | 274 ms | 1371 3000 | 220 ms | 205 | 3005 | 551 ms | 3371
scan() 读 .class 的元数据而不加载类,扫描期间新加载的类数不随组件数增长;类在实例化时才加载。
包里的普通类数 | scan() 耗时 | 扫描时新加载的类 | BeanDefinition 数 0 | 44 ms | 205 | 105 2000 | 145 ms | 206 | 105 8000 | 401 ms | 206 | 105 扫描用元数据读取(读 .class 文件,不加载类),所以扫描期间新加载的类很少;但每个 .class 文件都要被打开读一遍,不管有没有注解,所以扫描的范围越大越慢;类是在后面实例化的时候才被加载
耗时随扫描范围里的类文件数增长,与是否有注解无关。
[svc0, svc1, svc10, svc11, svc12, svc13, svc14, svc15, svc16, svc17, svc18, svc19, svc2, svc3, svc4, svc5, svc6, svc7, svc8]
includeFilters / excludeFilters 控制留下哪些类。
主类在默认包:启动失败 BeanCreationException ← ClassNotFoundException: org.aspectj.lang.annotation.Pointcut 主类在 demo 包:启动成功
主类放在默认包会让扫描范围变成整个 classpath,启动失败。
ConflictingBeanDefinitionException: Annotation-specified bean name 'svc' for bean class [demo.p2.Svc] conflicts with existing, non-compatible bean definition of same name and class [demo.p1.Svc]
不同包里同名的 @Component 默认 Bean 名一样,会抛 ConflictingBeanDefinitionException。