java泛型虽不支持运行时元编程,但可通过分层泛型接口、抽象基类、反射元数据与编译期契约,在框架层面实现类型安全、无样板sql的多数据源/租户适配。

Java 泛型本身不支持“元编程”意义上的运行时类型生成或代码编织,但通过泛型接口 + 抽象基类 + 反射元数据 + 编译期契约,可在高级框架设计中逼近元编程效果:用一套模板逻辑适配任意实体、任意数据源、任意租户策略,且类型安全、无强制转换、零样板 SQL。
用分层泛型接口定义可组合的行为契约
不写一个“万能 DAO 接口”,而是按能力拆解为正交接口:
-
IDao
:仅声明基础操作,如 save(T)、findById(ID)、deleteById(ID);要求T extends BaseEntity,确保有getId()、getVersion()等统一契约 -
IDataSourceAware
:不执行任何数据库操作,只提供上下文标记方法,如 withDataSource(String name)或useTenant(String tenantId) -
IUserDao:业务接口,继承
IDao<user></user>和IDataSourceAware<user></user>,可扩展findByEmail(String)等语义方法,不包含 SQL、连接或事务细节
抽象基类封装模板逻辑,子类只填元信息
BaseDao
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 把表名、主键字段、插入参数映射、结果集映射等交给子类实现:例如
getTableName()、getIdColumn()、getInsertArgs(T)、mapRow(ResultSet, int) - 基类中完成 SQL 拼装、参数绑定、资源关闭、异常转换——子类不用写
try-catch,也不用手动close() - 以
save(T entity)为例:自动拼INSERT INTO ${getTableName()} (...) VALUES (...),调用getInsertArgs(entity)获取参数,委托底层模板执行
为不同数据源提供专用泛型基类,复用原生能力
不追求“一套通吃”,而是针对每种技术栈建对应抽象层,注入路由与上下文逻辑:
-
JDBC 系:写
AbstractJdbcDao<t></t>,持DataSourceRouter,所有执行前调用lookupDataSource(name)获取连接 -
MyBatis-Plus:Mapper 接口直接继承
BaseMapper<t></t>,配合@MapperScan(sqlSessionTemplateRef = "tenantSqlSession")指向动态会话模板 -
JPA:定义
JpaDao<t id> extends JpaRepository<t id></t></t>,配合AbstractRoutingDataSource重写determineCurrentLookupKey(),根据 ThreadLocal 中的租户标识返回数据源名
运行时联动泛型类型与数据源,靠反射缓存 + 上下文驱动
泛型擦除后仍能支撑多数据源路由,关键在启动期预处理与运行期查表:
- 启动时扫描所有 DAO 实现类,用
ParameterizedType解析其泛型实参T,读取@TableSchema("user_db")或@TenantIsolated注解,构建Class> → DataSourceKey映射缓存 - 运行时结合
ThreadLocal<string> currentTenant</string>,用T.class.getSimpleName() + "_" + tenantId拼 key,查缓存获取目标数据源,避免每次反射 - 所有 DAO 实现将原生异常(
SQLException、MyBatisSystemException)统一转为DataAccessException,上层业务不感知底层差异
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










