核心是业务层仅依赖窄而专的数据访问接口,如userqueryservice,方法单一明确,实现类(jdbc/redis/cachefirst)各自封装技术细节,通过依赖注入解耦,切换数据库无需修改业务代码。

核心是让业务层只依赖数据访问接口,不感知数据库类型、SQL 写法或连接细节;接口只声明“要查什么、存什么”,具体怎么连、怎么查、用 MySQL 还是 Redis,全由实现类决定。
定义窄而专的数据访问接口
接口名体现业务意图,不是技术名词。比如叫 UserQueryService 而不是 JdbcUserDao;方法只做一件事,参数不超过 2 个,返回值明确:
- 查用户:User findUserById(Long id)
-
查活跃用户列表:List
findActiveUsers(LocalDateTime since) - 保存用户:void save(User user)
别把分页、缓存、事务控制塞进同一个接口——这些属于横切关注点,应由外层或独立组件处理。
业务类只持接口引用,不碰 new 或 if-else
业务服务(如 UserService)构造函数接收接口类型,不创建具体实现:
- ✅ 正确:public UserService(UserRepository repo) { this.repo = repo; }
- ❌ 错误:new JdbcUserRepository() 出现在方法里;或 if (env == "cache") { new RedisUserRepository() }
没有 Spring 时,可用工厂类统一创建,但调用方仍只认接口类型,不 import 任何实现类。
实现类各自封装技术细节
每个实现类专注一种数据源,互不影响:
- JdbcUserRepository:用 JdbcTemplate 或 MyBatis,写 SQL,处理 JDBC 异常
- RedisUserRepository:用 StringRedisTemplate,序列化/反序列化,设置过期时间
- CacheFirstUserRepository:组合前两者,先查缓存,未命中再查库,自动回填
切换数据库只需替换 Bean 注册或工厂返回值,UserService 类本身一行代码都不用改。
配合桥接与依赖注入落地
DataSource、JdbcTemplate 等底层能力,也应通过接口抽象(如 ConnectionProvider),避免业务层被绑定到某一种连接池。Spring 中用 @Autowired UserRepository repo 即可,前提是容器里有且仅有一个非 @Primary 的实现类;若需多实现共存(如读写分离),可用 @Qualifier 区分。










