1. 使用场景
Reflections扫描classpath, 缓存metadata, 以备运行期间使用.
- 可获取某类的所有子类
- 可获取某注解标注的所有类或字段
- 可获取某正则匹配的资源
- 可获取特定签名的方法
-
优点
具备Java原生反射技术, 所不具有的功能, 反射扫描.
-
缺点
底层实现, 会遍历classpath, 以及遍历jar包中的类, 太耗时间.
只能在启动时, 做些初始化的功能.
2. 简单使用
-
模型
package demo.java.reflections.model; import java.io.Serializable; public class BaseModel implements Serializable { } public class Account extends BaseModel { } public class User extends BaseModel { }
-
测试 (Java Main)
package demo.java.reflections; import demo.java.reflections.model.BaseModel; import org.reflections.Reflections; import org.reflections.scanners.SubTypesScanner; import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; import org.reflections.util.FilterBuilder; public class ReflectionsTest { /** * 如下示例 * 1. 基本使用 和 高级使用 的方式是等价的 * 2. 运行耗时主要在new Reflections() * 3. Reflections(Scanner:扫描器, URL:扫描路径), 扫描器和扫码路径 越少越好, 这个非常影响耗时!!! * 4. 这种反射扫描框架, 还是少用为好. 可以考虑用 "Spring IOC", "Java SPI", "配置文件解析" 等方式解决. * @param args */ public static void main(String[] args) { // 基本使用 Reflections reflections1 = new Reflections("demo.java.reflections.model"); System.out.println(reflections1.getSubTypesOf(BaseModel.class)); // 高级使用 Reflections reflections2 = new Reflections(new ConfigurationBuilder() .setUrls(ClasspathHelper.forPackage("demo.java.reflections.model")) .setScanners(new SubTypesScanner()) .filterInputsBy(new FilterBuilder().includePackage("demo.java.reflections.model")) ); System.out.println(reflections2.getSubTypesOf(BaseModel.class)); } }
- 测试 (SpringBoot Main)
package demo.java.reflections; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ReflectionsApp { public static void main(String[] args) { SpringApplication.run(ReflectionsApp.class, args); ReflectionsTest.main(args); } }
3. 常见错误
- 错误 (zip file closed)
-
问题现象
idea跑java main方法正常, idea跑springboot main方法正常. cmd跑springboot fat-jar, 报如下异常 -
解决方案
目前: 把reflections从0.9.12降到0.9.11. 详细参考issues
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:107)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
Caused by: org.reflections.ReflectionsException: Scanner SubTypesScanner was not configured
at org.reflections.Store.get(Store.java:39)
at org.reflections.Store.get(Store.java:61)
at org.reflections.Store.get(Store.java:46)
at org.reflections.Store.getAll(Store.java:93)
at org.reflections.Reflections.getSubTypesOf(Reflections.java:404)
at demo.java.reflections.ReflectionsTest.main(ReflectionsTest.java:32)
at demo.java.reflections.ReflectionsApp.main(ReflectionsApp.java:11)
... 8 more
-
错误 (尝试获取所有java.lang.Number的子类, 失败)
-
问题现象
idea跑java main方法正常, idea跑springboot main方法正常. cmd跑springboot fat-jar异常
-
问题原因
idea跑项目时, 会带上classpath, 这方便reflections从jar包中查找java.lang.Number的子类
另, springboot入口org.springframework.boot.loader.JarLauncher#main
有自定义的ClassLoader(LaunchedURLClassLoader)
-