Spring整合MyBatis-Pluss 部分课堂学习笔记

@NoArgsConstructor @AllArgsConstructor这两个注解是什么意思

@NoArgsConstructor@AllArgsConstructorLombok 框架 提供的注解,核心作用是自动生成对应构造方法,避免手动编写重复的构造方法代码,简化开发。

1. @NoArgsConstructor:生成无参构造方法

作用

自动为类生成一个没有参数的构造方法(空构造)。

等效手动代码

java

运行

复制代码
// 无参构造方法
public Book() {
}
适用场景
  • JavaBean 规范要求(很多框架如 Spring、MyBatis、Jackson 等,反射实例化对象时会默认调用无参构造);
  • 创建空对象后,通过 Setter 方法逐个赋值的场景(如 Book book = new Book(); book.setName("西游记");)。

2. @AllArgsConstructor:生成全参构造方法

作用

自动为类生成一个包含所有成员变量的构造方法,参数顺序与成员变量声明顺序一致。

等效手动代码

java

运行

复制代码
// 全参构造方法(参数顺序 = 成员变量声明顺序)
public Book(Integer id, String name, Double price) {
    this.id = id;
    this.name = name;
    this.price = price;
}
适用场景
  • 创建对象时直接给所有属性赋值(如 Book book = new Book(2, "西游记", 66.60);),无需多次调用 Setter;
  • 简化测试代码、DTO/VO 对象初始化等场景。

补充说明

  1. 使用前提:项目必须引入 Lombok 依赖(Maven 示例):

xml

复制代码
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
    <scope>provided</scope>
</dependency>
  1. 注意事项
    • @AllArgsConstructor 会包含所有成员变量 (包括 static 除外的所有字段,如 private/protected/public);
    • 如果类中有 final 成员变量,@NoArgsConstructor 会报错(final 变量必须初始化),需结合 @NoArgsConstructor(force = true) 强制生成(会给 final 变量赋默认值,如 int=0、String=null);
    • 配合 @Data(包含 Getter/Setter/toString/equals/hashCode)使用,能最大程度简化实体类代码。

示例对比(Lombok vs 手动)

Lombok 写法(简洁)

java

运行

复制代码
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Data;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
    private Integer id;
    private String name;
    private Double price;
}
手动写法(冗余)

java

运行

复制代码
public class Book {
    private Integer id;
    private String name;
    private Double price;

    // 无参构造(@NoArgsConstructor 自动生成)
    public Book() {}

    // 全参构造(@AllArgsConstructor 自动生成)
    public Book(Integer id, String name, Double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    // Getter/Setter/toString 等(@Data 自动生成)
    public Integer getId() { return id; }
    public void setId(Integer id) { this.id = id; }
    // ... 其他 Getter/Setter
    @Override
    public String toString() {
        return "Book(id=" + id + ", name=" + name + ", price=" + price + ")";
    }
}

public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements BookService这个是什么意思什么语法

这是 MyBatis-Plus 框架 提供的 Service 层快速开发语法 ,核心是通过继承 ServiceImpl 来快速实现 Service 层的通用 CRUD 功能,属于Java 泛型继承 + 框架封装的结合用法。

逐部分解析

1. 整体结构

java

运行

复制代码
public class BookServiceImpl 
    extends ServiceImpl<BookMapper, Book>  // 继承MyBatis-Plus的通用Service实现
    implements BookService                 // 实现自定义的BookService接口
2. 核心组件说明
(1)ServiceImpl<BookMapper, Book>

这是 MyBatis-Plus 提供的通用 Service 实现类,作用是:

  • 通过泛型指定 Mapper 接口BookMapper)和 实体类Book);
  • 内部已经封装了 所有通用 CRUD 方法 (如 list()/getById()/save()/updateById()/removeById() 等);
  • 你不需要手动编写这些基础方法,直接调用即可。
(2)implements BookService

这是自定义的 Service 接口(通常由你自己定义),作用是:

  • 定义当前业务模块(Book)的业务方法规范 (比如除了通用 CRUD,还可以加自定义业务方法,如 findBooksByPriceRange());
  • 符合面向接口编程的规范,便于后续扩展、解耦。

为什么这么写?

MyBatis-Plus 提供 ServiceImpl 的目的是减少 Service 层的重复代码

  • 不用自己写 @Autowired BookMapper bookMapperServiceImpl 内部已经注入了 Mapper);
  • 不用自己实现 list()/save() 等基础方法(ServiceImpl 已经封装好了);
  • 直接专注于自定义业务逻辑即可。

等效的 "手动编写" 对比

如果不用 ServiceImpl,你需要手动写这些代码:

java

运行

复制代码
@Service
public class BookServiceImpl implements BookService {
    // 手动注入Mapper
    @Autowired
    private BookMapper bookMapper;

    // 手动实现"查询所有"
    @Override
    public List<Book> findAllBooks() {
        return bookMapper.selectList(null);
    }

    // 手动实现"根据ID查询"
    @Override
    public Book getBookById(Integer id) {
        return bookMapper.selectById(id);
    }

    // 手动实现"新增"
    @Override
    public boolean saveBook(Book book) {
        return bookMapper.insert(book) > 0;
    }
}

而用 ServiceImpl 后,这些方法自动拥有,你只需要写:

java

运行

复制代码
@Service
public class BookServiceImpl extends ServiceImpl<BookMapper, Book> implements BookService {
    // 这里可以直接用父类的方法,比如:
    // list() → 等价于 bookMapper.selectList(null)
    // getById(id) → 等价于 bookMapper.selectById(id)
    // save(book) → 等价于 bookMapper.insert(book)

    // 只需要写自定义业务方法,比如:
    @Override
    public List<Book> findBooksByPriceRange(Double min, Double max) {
        // 用MyBatis-Plus的条件构造器
        return lambdaQuery()
                .ge(Book::getPrice, min)
                .le(Book::getPrice, max)
                .list();
    }
}

配套的 BookService 接口写法

通常 BookService 会继承 MyBatis-Plus 的 IService 接口(通用 Service 接口),这样接口也能直接拥有通用方法:

java

运行

复制代码
import com.baomidou.mybatisplus.extension.service.IService;
import 你的包名.Book;

public interface BookService extends IService<Book> {
    // 这里可以定义自定义业务方法
    List<Book> findBooksByPriceRange(Double min, Double max);
}

总结

这种写法是 MyBatis-Plus 简化 Service 层开发的标准用法,核心是:

  • 继承 ServiceImpl<Mapper, Entity> → 自动获得通用 CRUD 实现;
  • 实现自定义 Service 接口 → 规范业务方法,支持自定义逻辑。

类级别注解(核心:启动 Spring 容器)

java

运行

复制代码
@ExtendWith(SpringExtension.class) 
@ContextConfiguration(locations = {
    "classpath:application-service.xml",
    "classpath:application-dao.xml"
})
public class BookServiceTest { // 测试类名,通常是"被测试类+Test"
  • @ExtendWith(SpringExtension.class):JUnit 5 的核心扩展注解,作用是让 JUnit 5 整合 Spring 测试框架 ,启动 Spring 容器(否则无法使用@Autowired注入 Bean)。(如果是 SpringBoot 项目,通常用@SpringBootTest替代这两个注解,这里是传统 Spring 项目的写法)

  • @ContextConfiguration(locations = {...}):指定 Spring 配置文件的位置,告诉 Spring 容器加载哪些配置文件来创建 Bean:

    • classpath:application-service.xml:业务层配置文件(通常包含 Service 的<bean>定义、事务配置等);
    • classpath:application-dao.xml:数据层配置文件(通常包含 Mapper 的扫描、数据源、MyBatis 配置等);
    • classpath: 表示从项目的类路径(resources 目录)下查找配置文件。
3. 注入 Service 实例

java

运行

复制代码
@Autowired
private BookService bookService;
  • @Autowired:Spring 的自动装配注解,从容器中找到BookService类型的 Bean,注入到当前测试类的bookService变量中;
  • 注意:BookService必须是 Spring 容器中的 Bean(通常在application-service.xml中配置<bean>,或加@Service注解),否则会报 "找不到 Bean" 的错误。

BookServiceImpl与BookService都会被扫描注册为bean吗

在你当前的配置下:

  • <context:component-scan base-package="com.itheima.service"/> 会扫描 com.itheima.service 包 ** 及其子包(比如 Impl 子包)** 下的类。

所以:

  1. BookServiceImpl(在 com.itheima.service.Impl 子包下):只要它加了 @Service(或 @Component 等组件注解),就会被扫描并注册为 Bean

  2. BookService(是接口):Spring 不会将接口 注册为 Bean(因为接口无法实例化),只会注册接口的实现类(如 BookServiceImpl

简单说:BookServiceImpl 会被注册为 Bean;BookService 作为接口不会被注册,它是用来注入实现类的类型标识。

@Autowired的作用

@AutowiredSpring 框架提供的核心注解之一 ,核心作用是自动从 Spring 容器中查找并注入(装配)匹配类型的 Bean,避免手动创建对象,实现 "依赖注入(DI)"。

1. 核心功能

一句话总结:让 Spring 帮你 "自动找对象、赋值给变量"

举个例子,原本你需要手动写:

java

运行

复制代码
// 手动创建对象(繁琐且耦合)
BookService bookService = new BookServiceImpl();

@Autowired 后,只需写:

java

运行

复制代码
@Autowired
private BookService bookService; // Spring自动把容器里的BookServiceImpl对象赋值给这个变量

2. 注入规则

@Autowired 默认按照 "类型(Type)匹配" 注入:

  • Spring 会在容器中找 和变量类型(如 BookService)匹配的 Bean (比如 BookServiceImplBookService 的实现类,类型匹配);
  • 如果容器中只有一个匹配类型的 Bean,直接注入;
  • 如果有多个匹配类型的 Bean,需要配合 @Qualifier("bean名称") 指定具体 Bean 名称。

3. 注意事项

  • 注入的对象必须是 Spring 容器中的 Bean :被注入的类(比如 BookServiceImpl)必须加了 @Service/@Component 等注解,或在 XML 中配置了 <bean>,否则 Spring 找不到这个 Bean,会报错 NoSuchBeanDefinitionException

  • 接口类型也能注入 :虽然 BookService 是接口,但 Spring 会自动注入它的实现类 Bean(如 BookServiceImpl,这也是 "面向接口编程" 的常用写法。

  • 非必须注入的场景 :可以加 required = false 表示 "找不到匹配的 Bean 时不报错,变量设为 null":

    java

    运行

    复制代码
    @Autowired(required = false)
    private BookService bookService;

4. 常见使用场景

  • Service 层注入 Mapper/Dao

    java

    运行

    复制代码
    @Service
    public class BookServiceImpl {
        @Autowired
        private BookMapper bookMapper; // 注入Mapper接口的代理Bean
    }
  • Controller 层注入 Service

    java

    运行

    复制代码
    @Controller
    public class BookController {
        @Autowired
        private BookService bookService; // 注入Service的实现类Bean
    }
  • 测试类中注入 Bean:就是你之前代码里的用法:

    java

    运行

    复制代码
    public class BookServiceTest {
        @Autowired
        private BookService bookService; // 注入容器中的BookService实现类
    }

总结

@Autowired 的核心是 "自动装配",让 Spring 帮你管理对象的依赖关系,不用手动 new 对象,是 Spring 实现 "解耦" 和 "控制反转(IOC)" 的关键注解之一。


@Autowired private BookMapper bookMapper; Mapper不也是接口,不是说接口不能注册为bean

你这个问题问到了核心!BookMapper 确实是接口,但它能被 @Autowired 注入,核心原因是:MyBatis(或 MyBatis-Plus)会为 Mapper 接口动态生成代理实现类,并将这个代理类注册为 Spring Bean ------ 你注入的不是 "接口本身",而是接口的动态代理 Bean

一、先澄清核心结论

类型 是否能直接注册为 Bean? 为什么能被 @Autowired 注入?
普通接口(如 BookService) 注入的是它的手动编写的实现类(BookServiceImpl)
Mapper 接口(如 BookMapper) 注入的是MyBatis 动态生成的代理实现类

简单说:BookMapper 接口本身不会被注册为 Bean,但 MyBatis 会帮你 "偷偷" 创建它的实现类(代理类),并把这个代理类交给 Spring 容器管理,所以 @Autowired 能找到并注入。

二、MyBatis 如何为 Mapper 接口生成 Bean?

以你之前的配置(application-dao.xml)为例,核心是这行配置(或注解):

xml

复制代码
<!-- 传统SSM配置:扫描Mapper接口,生成代理Bean -->
<mybatis-spring:scan base-package="com.itheima.mapper"/>

<!-- 或SpringBoot中用@MapperScan注解(启动类上) -->
// @MapperScan("com.itheima.mapper")

这个配置的作用是:

  1. 扫描指定包下的所有 Mapper 接口(如 BookMapper);
  2. MyBatis 会通过 JDK 动态代理 为每个 Mapper 接口生成一个代理类(比如 BookMapperProxy);
  3. 将这个代理类封装成 Spring Bean,注册到 Spring 容器中;
  4. 当你 @Autowired private BookMapper bookMapper; 时,Spring 注入的是这个代理类的实例,而非接口本身。

三、直观对比:普通接口 vs Mapper 接口

1. 普通接口(BookService)

java

运行

复制代码
// 接口(无Bean)
public interface BookService { List<Book> findAllBooks(); }

// 手动编写实现类(加@Service,注册为Bean)
@Service
public class BookServiceImpl implements BookService { ... }

// 注入时,Spring找的是BookServiceImpl这个Bean
@Autowired
private BookService bookService; 
2. Mapper 接口(BookMapper)

java

运行

复制代码
// 接口(无Bean)
public interface BookMapper extends BaseMapper<Book> { ... }

// 无需手动写实现类!MyBatis自动生成代理类(注册为Bean)
// 注入时,Spring找的是MyBatis生成的BookMapper代理Bean
@Autowired
private BookMapper bookMapper; 

四、关键验证:打印注入的对象类型

你可以在代码中加一行打印,就能看到注入的是代理类:

java

运行

复制代码
@Service
public class BookServiceImpl {
    @Autowired
    private BookMapper bookMapper;

    public void test() {
        // 打印bookMapper的实际类型(不是BookMapper接口,而是代理类)
        System.out.println(bookMapper.getClass().getName());
        // 输出示例:com.sun.proxy.$Proxy68(JDK动态代理类)
    }
}

五、常见误区澄清

  1. "接口不能注册为 Bean" 是对的:所有接口都无法直接实例化,自然不能成为 Bean;
  2. Mapper 能注入≠接口成了 Bean:注入的是接口的 "动态代理实现类",这个实现类才是 Bean;
  3. 必须配置 Mapper 扫描 :如果没加 <mybatis-spring:scan>@MapperScan/@Mapper,MyBatis 不会生成代理类,此时 @Autowired BookMapper 会报错(找不到 Bean)。

总结

BookMapper 作为接口本身确实不是 Bean,但 MyBatis 替你完成了 "编写实现类 + 注册为 Bean" 的工作,你注入的是 MyBatis 动态生成的代理对象 ------ 这也是 MyBatis 简化开发的核心设计,不用手动写 Mapper 实现类。

相关推荐
I'm Jie1 小时前
Java 字节码工具 ASM,实现类的动态增强
java·spring boot·spring·asm·cglib·class
八个程序员1 小时前
汉字古诗生成c++
开发语言·c++
zore_c1 小时前
【C语言】数据结构——顺序表超详解!!!(包含顺序表的实现)
c语言·开发语言·数据结构·c++·经验分享·笔记·线性回归
⑩-1 小时前
Spring 的事务传播行为(Propagation)
java·数据库·spring
没有bug.的程序员1 小时前
K8s 环境中的 JVM 调优实战
java·jvm·spring·云原生·容器·kubernetes
Trouvaille ~1 小时前
【Java篇】以简驭繁:接口的精简与程序的优雅
java·开发语言·接口·抽象工厂模式·类和对象·javase·基础入门
一只乔哇噻1 小时前
java后端工程师+AI大模型开发进修ing(研一版‖day62)
java·开发语言·算法·语言模型
利刃大大1 小时前
【JavaSE】十、ArrayList && LinkedList
java·链表·数组
lsx2024061 小时前
PHP 可用的函数
开发语言