Java中通过泛型抽象Service基类封装通用CRUD逻辑,子类继承时指定实体与主键类型并注入对应Mapper,支持模板方法扩展和AOP增强,需注意代理方式、泛型擦除及事务注解位置。

Java 中通过继承实现通用 Service 基类,核心是定义一个泛型抽象父类,封装增删改查等共用逻辑,让具体业务 Service 继承它并指定实体类型和 Mapper 接口,从而避免重复代码。
定义泛型抽象 Service 基类
使用 泛型 T(实体类) 和 ID(主键类型),注入通用 Mapper(如 MyBatis-Plus 的 BaseMapper 或自定义通用 DAO),提供基础方法:
- 方法签名统一,如
save(T entity)、removeById(ID id)、list()等 - 内部直接调用 mapper 方法,不耦合具体实现
- 声明为
abstract,强制子类提供 mapper 实例(可通过 protected 抽象方法或构造器传入)
让具体 Service 继承并绑定类型
子类继承时明确泛型参数,并注入对应 Mapper。推荐两种方式:
-
方式一:子类注入 mapper 并覆写抽象 getter
父类定义protected abstract BaseMapper<T> getMapper();,子类中返回userMapper实例 -
方式二:构造器注入(更清晰)
父类提供public BaseService(BaseMapper<T> mapper)构造器,子类在构造时传入userMapper
例如:public class UserServiceImpl extends BaseService<User, Long> implements UserService { public UserServiceImpl(UserMapper mapper) { super(mapper); } }
立即学习“Java免费学习笔记(深入)”;
扩展能力:支持自定义逻辑与拦截
基类可预留模板方法供子类增强:
- 定义
protected void beforeSave(T entity),子类可重写做参数校验或字段填充 - 在
save方法中先调用beforeSave,再执行 mapper 插入 - 也可结合 Spring AOP 对 BaseService 的方法统一处理日志、事务边界等(注意代理限制)
注意事项与避坑点
实际使用中需留意:
- Spring 默认使用 JDK 动态代理,若 Service 没有接口,需开启 CGLIB(
@EnableAspectJAutoProxy(proxyTargetClass = true)) - 泛型在运行时被擦除,不能直接通过
this.getClass().getGenericSuperclass()安全获取实体类型(需借助 TypeReference 或子类显式传入) - 事务注解
@Transactional应加在实现类或接口方法上,不要放在 BaseService 的 public 方法上(代理可能失效) - 避免在基类中引入过多业务相关逻辑,保持“通用性”——分页、缓存、审计字段等建议按需抽离或配置化


















