标准五层架构(由外到内)
- Controller 层
- Service 层
- Repository / DAO 层
- Entity / Model 层
- DTO / VO 层
架构依赖关系(正确方向)
Controller → Service → Repository → Entity
上层可以调用下层
下层 不能 依赖上层(如 Repository 不能调用 Controller)
注解
| 层级 | 名称 | 职责 | 常见注解 / 技术 |
|---|---|---|---|
| 1 | Controller 层 (表现层 / Web 层) | 接收 HTTP 请求,参数校验,调用 Service,返回响应 | @RestController, @RequestMapping |
| 2 | Service 层 (业务逻辑层) | 实现核心业务规则、事务管理、协调多个 DAO 操作 | @Service, @Transactional |
| 3 | Repository / DAO 层 (数据访问层) | 封装对数据库的操作(增删改查) | @Repository, Spring Data JPA, MyBatis |
| 4 | Entity / Model 层 (领域模型层) | 定义与数据库表对应的实体类(POJO) | @Entity, @Table, @Id(JPA) |
| 5 | DTO / VO 层 (数据传输对象层) | 在各层之间安全传递数据,避免暴露内部实体 | 无注解,纯 Java Bean |
一个简单的查询结构
java
@RestController
@RequestMapping("/system/post")
public class SysPostController {
@Autowired
private ISysPostService postService;
@GetMapping("/list")
public TableDataInfo list(SysPost post)
{
startPage();
List<SysPost> list = postService.selectPostList(post);
return getDataTable(list);
}
}
下面引出一系列的疑问, 对于新手来说
why
问题: 什么?接口里的方法,不是空的,只定义没实现么, 为什么?postService.selectPostList() 能直接调用?
java
@Autowired
private ISysPostService postService;
答
这里的 ISysPostService 通常是一个接口(Interface),而 Spring 容器会自动注入该接口的一个实现类(Implementation Class)的实例
类似于: 接口名 obj = new Object()
why
明明我们没这么写, 为什么?
答
约定大于实现, springboot 就是这么设计的
java
@Service
public class SysPostServiceImpl implements ISysPostService {
@Autowired
private SysPostMapper postMapper;
@Override
public List<SysPost> selectPostList(SysPost post)
{
return postMapper.selectPostList(post);
}
}
这里类里 实现了ISysPostService 接口,且顶部加了@Service注释
Spring 容器启动时:
扫描到 @Service 注解,将 SysPostServiceImpl 注册为一个 Bean。
当遇到 @Autowired private ISysPostService postService; 时,
Spring 会在容器中查找 类型为 ISysPostService 的 Bean。
找到 SysPostServiceImpl(因为它实现了该接口),并自动注入其实例。
这样
java
@Autowired
private ISysPostService postService;
就相当于new了一个对象postService ,能使用 SysPostServiceImpl 类里面的方法了
why
@Service 里面又来个@Autowired, 这里面又是什么
java
@Autowired
private SysPostMapper postMapper;
这里面没有看到上面所说的逻辑, SysPostMapper 这个接口,没有类对其实现, 也没有加@Services ,它既没有 @Component,也没有 @Repository
答
-
@MapperScan 注解(关键!)
Spring 本身不会自动把任意接口变成 Bean,但 MyBatis 与 Spring 集成后(通过 mybatis-spring),会利用 MapperScannerConfigurer 或 @MapperScan 来动态生成实现类并注册为 Spring Bean。
-
动态代理实现
MyBatis 在启动时,通过 JDK 动态代理(或 CGLIB)为 SysPostMapper 生成一个实现类。
当你调用 postMapper.selectPostList(...) 时,实际执行的是 MyBatis 的 SQL 执行逻辑(从 XML 或注解中读取 SQL)。
这个代理对象被 Spring 管理,因此可以被 @Autowired 注入。
-
不需要 @Repository 的原因
虽然传统 DAO 类会加 @Repository,但 MyBatis Mapper 是接口 + XML/注解驱动,不是普通 Java 类。
@MapperScan 已经承担了"注册 Bean"的职责,无需额外注解。
即使你手动给 Mapper 接口加 @Repository,Spring 也会忽略它(因为 MyBatis 自己管理这些 Bean)。
why
, @Repository 和 MyBatis 的 Mapper(接口 + XML)之间的关系, @Repository 相当于 mapper里面吗加注解么
答
@Repository 并不"相当于" Mapper,但在某些场景下可以起到类似的作用 ------ 它们都是为了让数据访问层的组件被 Spring 容器管理。
但在 MyBatis + Spring Boot(如若依 RuoYi)项目中,Mapper 接口通常不需要加 @Repository,因为 MyBatis 通过 @MapperScan 自动将其注册为 Bean。
| 特性 | @Repository(传统方式) |
MyBatis Mapper(接口 + XML) |
|---|---|---|
| 本质 | 一个 Spring 注解,标记 DAO 类为 Repository Bean | 一个 接口,由 MyBatis 动态生成实现类 |
| 是否需要实现类 | 需要手写 DAO 实现类(如用 JdbcTemplate) | 不需要 实现类,MyBatis 自动生成代理 |
| 如何被 Spring 管理 | 加 @Repository 后,被组件扫描(@ComponentScan)发现 |
通过 @MapperScan 扫描接口,MyBatis-Spring 自动注册为 Bean |
是否需要 @Repository 注解 |
✅ 必须加(或 @Component) |
❌ 不需要,加了也无效(除非配合 @Mapper) |
| 典型使用场景 | Spring JDBC、Hibernate 原生 DAO | MyBatis、MyBatis-Plus |
场景 1:使用 JdbcTemplate(需要 @Repository)
java
@Repository // ← 必须加!否则 Spring 找不到这个 Bean
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public User findById(Long id) {
return jdbcTemplate.queryForObject("SELECT * FROM user WHERE id = ?", ...);
}
}
场景 2:使用 MyBatis Mapper(不需要 @Repository)
java
// 没有任何注解!
public interface SysPostMapper
{
public List<SysPost> selectPostList(SysPost post);
public List<SysPost> selectPostAll();
}
配合:
java
@SpringBootApplication
@MapperScan("com.example.mapper") // ← 关键!自动注册所有 Mapper 接口为 Bean
public class Application { ... }
此时,SysPostMapper 已经是一个 Spring Bean,可以直接注入:
除了 @MapperScan,你也可以在每个 Mapper 接口上加 @Mapper:
java
@Mapper
public interface SysPostMapper
{
public List<SysPost> selectPostList(SysPost post);
public List<SysPost> selectPostAll();
}
这样就不需要 @MapperScan 了。但缺点是每个接口都要写,所以推荐用 @MapperScan 批量处理。
注意:@Mapper 是 org.apache.ibatis.annotations.Mapper,不是 Spring 的注解。
| 问题 | 回答 |
|---|---|
@Repository 相当于 Mapper 吗? |
❌ 不相当。@Repository 是 Spring 注解,Mapper 是 MyBatis 机制。 |
Mapper 需要加 @Repository 吗? |
❌ 不需要,MyBatis 通过 @MapperScan 自动注册。 |
| 两者目的相同吗? | ✅ 广义上都是"让 DAO 被 Spring 管理",但实现机制完全不同。 |
| 能混用吗? | ⚠️ 不推荐。MyBatis Mapper 用 @MapperScan,传统 DAO 用 @Repository。 |
MyBatis 的 Mapper 是"接口即 DAO",靠 @MapperScan 变成 Spring Bean;
而 @Repository 是给"具体实现类"用的,用于传统 JDBC 或 Hibernate 场景。