在面向对象编程中,类是封装数据和行为的基本单位。Java 提供了嵌套类(Nested Class)机制,使得在一个外部类中可以定义一个或多个内部类,按功能可分为:
静态嵌套类(Static Nested Class):与外部类同级别,不依赖外部类实例,可像普通类一样被静态访问。
成员内部类(Member Inner Class):定义在外部类成员位置,持有对外部类实例的隐式引用,可访问外部类的实例成员。
局部内部类(Local Inner Class):定义在方法内部,作用域仅限方法内,对方法的局部变量只能访问被 final
或 effectively final 的变量。
匿名内部类(Anonymous Inner Class):没有类名,常用于一次性实现接口或继承抽象类,简化回调和监听场景。
嵌套类可以增进封装性,将与外部类关联紧密的辅助类隐藏在类型内部,也可将功能模块化。典型场景包括:
构建器模式(Builder Pattern):经常将 Builder
定义为外部类的静态嵌套类,构造复杂对象。
事件监听:在 GUI 编程中常用匿名内部类做事件回调。
DSL 设计:通过成员内部类或局部内部类实现流式 API,如 Map.Entry
、Stream
的内部结构。
隐藏实现:将具体策略类定义为私有内部类,对外暴露接口或工厂方法。
然而,在大型项目里正确地获取和展示“嵌套类的信息”(如类名、类型、修饰符、所属外部类、所在包、成员关系等),并在运行时或编译时对其进行统一管理和使用,却并非开箱即用。比如:
需要在文档或控制台中列出某个外部类所有嵌套类及其属性。
需要在运行时反射地加载外部类及其静态嵌套类实例。
希望通过注解自动注册所有成员内部类到某个容器中。
本项目的目标是:构建一套通用工具,通过反射和注解机制,动态获取并管理 Java 类的嵌套类信息,并提供简洁的 API 供业务层使用。该工具既能在运行时列举任意类的嵌套类信息,也能在扫描包时根据注解筛选出需要关注的嵌套类。
为实现上述目标,项目需满足以下需求:
嵌套类扫描与加载
能够扫描指定包下的所有类,识别每个类中定义的静态嵌套类和成员内部类。
仅在必要时加载局部内部类(如在方法调用时动态获取其 Class 对象)。
注解筛选机制
定义注解 @NestedInfo
,可标记在嵌套类上,提供描述信息,如名称 name
、版本 version
、用途 description
等。
工具在扫描时,只收集带有 @NestedInfo
注解的嵌套类,并读取注解属性。
信息封装与存储
定义数据模型 NestedClassInfo
,封装以下字段:
String outerClassName
:外部类全限定名
String nestedClassName
:嵌套类全限定名
NestedType type
:枚举类型(STATIC、MEMBER、LOCAL、ANONYMOUS)
Set
:修饰符集合(public、private、static...)
Annotation[] annotations
:嵌套类上的注解信息
Field[] fields
、Method[] methods
:成员信息(可选)
提供持久化接口,将扫描到的信息序列化到 JSON 或写入数据库。
运行时查询 API
提供 NestedClassScanner.scanPackage(String packageName)
方法,返回 List
。
提供 NestedClassRegistry.register(Class> outerClass)
,手动注册某个外部类,返回该类所有嵌套类信息。
支持根据注解属性或类型过滤查询,如 findByVersion("1.0")
、findByType(Static)
。
性能与并发
包扫描采用缓存机制,避免重复扫描;
支持并发扫描不同包或不同外部类,线程安全。
文档与示例
提供使用示例代码,演示扫描整个包、查询符合条件的嵌套类、调用默认构造并执行方法。
提供 README 文档,说明使用方法和配置选项。
反射 API
java.lang.Class
:获取 getDeclaredClasses()
、getClasses()
;
java.lang.reflect.Modifier
:解析 int mod = clazz.getModifiers()
;
加载类:Class.forName()
、自定义 ClassLoader
(可选)。
注解处理
定义运行时注解 @NestedInfo
,设置 @Retention(RetentionPolicy.RUNTIME)
;
通过 clazz.getAnnotation(NestedInfo.class)
读取注解属性。
包扫描
使用第三方库(如 Reflections、ClassGraph)或基于类路径资源扫描手工实现;
处理不同运行环境(IDE、Jar 包、Web 容器)的资源定位。
JSON 序列化
使用 Jackson 或 Gson,将 NestedClassInfo
对象序列化为 JSON;
可选:使用 Lombok 简化数据模型定义。
并发与缓存
ConcurrentHashMap
作为扫描结果缓存;
使用 CompletableFuture
并行扫描多个包,提高性能。
日志框架
SLF4J + Logback:记录扫描流程、遇到异常时的错误信息;
可配置日志级别,输出扫描细节。
定义注解和模型类
注解 NestedInfo
:包括 name
、version
、description
;
模型 NestedClassInfo
:封装嵌套类所有元数据。
实现包扫描器
基于 ClassGraph:
ClassGraph cg = new ClassGraph().enableAllInfo().acceptPackages(packageName);
ScanResult result = cg.scan();
for (ClassInfo ci : result.getAllClasses()) {
Class> clazz = ci.loadClass();
for (Class> nested : clazz.getDeclaredClasses()) { … }
}
或手写:获取 classpath 下 .class
文件,转换为类名并加载。
过滤与注解解析
对每个 nested
,检查 nested.isAnnotationPresent(NestedInfo.class)
;
读取注解属性并创建对应的 NestedClassInfo
实例,填充 type
、modifiers
等。
扫描结果缓存
在 NestedClassScanner
中维护静态 ConcurrentHashMap
;
scanPackage
时先检查 cache.containsKey(packageName)
,若存在直接返回,否则并发执行扫描并存入缓存。
运行时注册与查询
NestedClassRegistry.register(outerClass)
:直接读取 outerClass.getDeclaredClasses()
,解析并返回;
NestedClassRegistry.findByVersion
, findByType
:基于缓存结果做 stream().filter(...)
。
API 设计与示例
组织在 com.example.nested
包下:
NestedClassScanner
、NestedClassRegistry
、NestedClassInfo
、NestedType
、NestedInfo
注解。
在 README 中给出用法示例。
/*
* =====================================================
* File: NestedInfo.java
* 注解:标记嵌套类元数据
* =====================================================
*/
package com.example.nested;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NestedInfo {
String name();
String version() default "1.0";
String description() default "";
}
/*
* =====================================================
* File: NestedType.java
* 枚举:嵌套类类型
* =====================================================
*/
package com.example.nested;
public enum NestedType {
STATIC, // 静态嵌套类
MEMBER, // 成员内部类
LOCAL, // 局部内部类
ANONYMOUS // 匿名内部类
}
/*
* =====================================================
* File: NestedClassInfo.java
* 模型:封装嵌套类信息
* =====================================================
*/
package com.example.nested;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Set;
public class NestedClassInfo {
private String outerClassName; // 外部类全限定名
private String nestedClassName; // 嵌套类全限定名
private NestedType type; // 嵌套类类型
private Set modifiers; // 修饰符字符串集合
private Annotation[] annotations; // 嵌套类上的注解
private Field[] fields; // 字段列表
private Method[] methods; // 方法列表
private String infoName; // @NestedInfo.name
private String infoVersion; // @NestedInfo.version
private String infoDescription; // @NestedInfo.description
// 构造方法
public NestedClassInfo(Class> outer, Class> nested, NestedInfo meta) {
this.outerClassName = outer.getName();
this.nestedClassName = nested.getName();
this.type = detectType(outer, nested);
this.modifiers = ModifierUtil.modifiersToStringSet(nested.getModifiers());
this.annotations = nested.getAnnotations();
this.fields = nested.getDeclaredFields();
this.methods = nested.getDeclaredMethods();
this.infoName = meta.name();
this.infoVersion = meta.version();
this.infoDescription = meta.description();
}
// 辅助:根据反射判断类型
private NestedType detectType(Class> outer, Class> nested) {
if (nested.isAnonymousClass()) return NestedType.ANONYMOUS;
if (nested.isLocalClass()) return NestedType.LOCAL;
if (Modifier.isStatic(nested.getModifiers())) return NestedType.STATIC;
return NestedType.MEMBER;
}
// getters / toString 略
}
/*
* =====================================================
* File: ModifierUtil.java
* 工具:将修饰符 int 转为字符串 Set
* =====================================================
*/
package com.example.nested;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;
public class ModifierUtil {
public static Set modifiersToStringSet(int mods) {
Set set = new HashSet<>();
if (Modifier.isPublic(mods)) set.add("public");
if (Modifier.isProtected(mods)) set.add("protected");
if (Modifier.isPrivate(mods)) set.add("private");
if (Modifier.isStatic(mods)) set.add("static");
if (Modifier.isFinal(mods)) set.add("final");
if (Modifier.isAbstract(mods)) set.add("abstract");
return set;
}
}
/*
* =====================================================
* File: NestedClassScanner.java
* 扫描指定包下所有带 @NestedInfo 注解的嵌套类
* =====================================================
*/
package com.example.nested;
import io.github.classgraph.*;
import org.slf4j.*;
import java.util.*;
import java.util.concurrent.*;
public class NestedClassScanner {
private static final Logger logger = LoggerFactory.getLogger(NestedClassScanner.class);
private static final ConcurrentMap> cache = new ConcurrentHashMap<>();
/**
* 扫描包
* @param packageName 包名,如 "com.example"
*/
public static List scanPackage(String packageName) {
// 从缓存直接返回
if (cache.containsKey(packageName)) {
return cache.get(packageName);
}
logger.info("开始扫描包:{}", packageName);
List result = new ArrayList<>();
try (ScanResult scan = new ClassGraph()
.enableClassInfo()
.acceptPackages(packageName)
.scan()) {
for (ClassInfo ci : scan.getAllClasses()) {
Class> outer = ci.loadClass();
for (Class> nested : outer.getDeclaredClasses()) {
if (nested.isAnnotationPresent(NestedInfo.class)) {
NestedInfo meta = nested.getAnnotation(NestedInfo.class);
result.add(new NestedClassInfo(outer, nested, meta));
}
}
}
} catch (Exception e) {
logger.error("扫描包失败", e);
}
cache.put(packageName, result);
return result;
}
}
/*
* =====================================================
* File: NestedClassRegistry.java
* 注册与查询已加载嵌套类信息
* =====================================================
*/
package com.example.nested;
import java.util.*;
import java.util.stream.Collectors;
public class NestedClassRegistry {
// 手动注册某外部类
public static List register(Class> outerClass) {
List list = new ArrayList<>();
for (Class> nested : outerClass.getDeclaredClasses()) {
if (nested.isAnnotationPresent(NestedInfo.class)) {
NestedInfo meta = nested.getAnnotation(NestedInfo.class);
list.add(new NestedClassInfo(outerClass, nested, meta));
}
}
return list;
}
// 按版本过滤
public static List findByVersion(String version) {
return getAll().stream()
.filter(info -> info.getInfoVersion().equals(version))
.collect(Collectors.toList());
}
// 按类型过滤
public static List findByType(NestedType type) {
return getAll().stream()
.filter(info -> info.getType() == type)
.collect(Collectors.toList());
}
// 获取所有缓存中的
private static List getAll() {
return NestedClassScanner.scanPackage("com.example")
.stream().collect(Collectors.toList());
}
}
/*
* =====================================================
* File: README.md
* 使用示例与说明
* =====================================================
*/
# 嵌套类信息工具
## 快速开始
```java
// 扫描整个包
List infos = NestedClassScanner.scanPackage("com.example");
// 手动注册单个外部类
List userInfos = NestedClassRegistry.register(com.example.model.User.class);
// 按版本查询
List v1Infos = NestedClassRegistry.findByVersion("1.0");
// 按类型查询
List staticInfos = NestedClassRegistry.findByType(NestedType.STATIC);
// 调用嵌套类默认构造
for (NestedClassInfo info : infos) {
Class> cls = Class.forName(info.getNestedClassName());
Object obj = cls.getDeclaredConstructor().newInstance();
// 调用方法示例
// Method m = cls.getMethod("execute");
// m.invoke(obj);
}
分布式扫描
将扫描任务分发至多台节点并汇总结果,适用于微服务或大规模模块化项目。
注解增强
支持更多属性,如 priority
、author
,并在 NestedClassInfo
中存储。
自定义 ClassLoader
针对插件化系统,实现基于 URLClassLoader 的按需加载/卸载。
UI 管理界面
提供基于 Web 的可视化界面,展示扫描结果、支持在线筛选和动态调用。
缓存持久化
将扫描结果持久化到 Redis 或本地文件,下次启动直接加载,无需重新扫描。
代码生成
根据扫描结果自动生成工厂类、文档或配置文件,减少手写代码量。
安全沙箱
动态加载并执行嵌套类时,使用 Java 安全管理器(SecurityManager
)控制权限,防止恶意代码执行。