2人参与 • 2026-03-20 • Java
spring boot开发中,注解无处不在。但注解太多,容易忘记怎么用?
本文整理了30个最常用的注解,分为7大类,每个注解都配有:
@springbootapplication 是 spring boot 核心组合注解,整合@configuration、@enableautoconfiguration、@componentscan三大核心注解的功能,用于标注 spring boot 项目的主启动类,是开启 spring boot 自动配置、组件扫描的核心入口,简化项目配置,一键启动 spring boot 应用。
@configuration:将类标记为配置类,支持注解式配置;@enableautoconfiguration:开启自动配置,自动加载适配的框架配置;@componentscan:扫描主类所在包及子包的 @component 系列注解(@controller/@service /@mapper等),将 bean 纳入容器。实战代码:
package com.liu;
import org.mybatis.spring.annotation.mapperscan;
import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;
@mapperscan("com.liu.mapper") // 扫描mapper接口
@springbootapplication // 核心注解
public class simpleaccountingapplication {
public static void main(string[] args) {
springapplication.run(simpleaccountingapplication.class, args);
}
}使用场景: 每个spring boot项目有且仅有一个启动类
常见错误:
// 错误:启动类放在子包里,扫描不到其他组件
package com.liu.config; // 错误位置
@springbootapplication
public class application { }
// 正确:启动类放在根包
package com.liu; // 正确位置
@springbootapplication
public class application { }作用: @component 是 spring 框架的核心通用组件注解,用于标记普通 java 类为spring 容器可管理的 bean,标注后类会被 spring 扫描并实例化,纳入 ioc 容器统一管理,实现对象的依赖注入与生命周期管控,解耦组件间依赖。
它是 @componentscan 扫描的基础注解,@controller、@service、@repository 等注解均基于它扩展,分别适配控制层、业务层、数据层等场景,@component 则适用于无明确分层的通用组件,是 spring 组件化开发的基础。
实战代码:
@component
public class jwtutil {
@value("${jwt.secret}")
private string secret;
public string generatetoken(long userid, integer role, string username) {
// 生成jwt token
}
}使用场景: 工具类、拦截器、切面等
衍生注解:
@controller:控制器@service:服务层@repository:数据访问层@configuration:配置类作用: @autowired 是 spring 核心的自动依赖注入注解,用于实现 spring ioc 容器中 bean 的自动装配,简化组件间依赖管理,无需手动创建对象实例,直接注入所需依赖 bean,大幅降低代码耦合度。
其默认按类型(bytype) 匹配容器中的 bean 完成注入,支持标注在类的构造方法、字段、setter 方法上;若同类型 bean 存在多个,可结合 @qualifier 按名称精准匹配,或用 @primary 指定默认注入 bean。
实战代码:
@service
public class userserviceimpl implements userservice {
@autowired
private usermapper usermapper; // 自动注入mapper
@autowired
private jwtutil jwtutil; // 自动注入工具类
public user login(string username, string password) {
return usermapper.selectbyusername(username);
}
}使用场景: 注入service、mapper、工具类等
推荐写法(构造器注入):
@service
@requiredargsconstructor // lombok注解,自动生成构造器
public class userserviceimpl implements userservice {
private final usermapper usermapper; // final修饰
private final jwtutil jwtutil;
// lombok自动生成构造器,spring自动注入
}为什么推荐构造器注入?
作用: 注入配置文件中的值。@value 是 spring 框架的属性注入注解,核心用于将配置文件值、系统属性、常量值注入到 spring 容器管理的 bean 的字段 / 方法参数中,实现配置与代码解耦,无需硬编码配置信息,简化配置读取与使用。
支持直接注入常量(如@value("hello")),更常用的是通过${key}读取 application.properties/yaml 中的配置项(如@value("${server.port}")),可标注在 bean 的字段、构造方法参数、setter 方法上,适配 spring 管理的所有组件(@component/@service/@controller 等)。
实战代码:
@component
public class jwtutil {
@value("${jwt.secret}")
private string secret; // 注入配置:jwt.secret
@value("${jwt.expiration}")
private long expiration; // 注入配置:jwt.expiration
}配置文件(application.yml):
jwt: secret: my-secret-key-12345 expiration: 7200000 # 2小时
使用场景: 注入配置参数(密钥、超时时间、文件路径等)
常见错误:
// 错误:配置文件中没有这个key
@value("${jwt.secretkey}") // 配置文件是jwt.secret,不是jwt.secretkey
private string secret;
// 正确:设置默认值
@value("${jwt.secret:default-secret}") // 如果没有配置,使用default-secret
private string secret;作用: @configuration 是 spring 框架的核心配置类注解,用于标记 java 类为spring 配置类,替代传统 xml 配置文件,实现纯注解式配置开发。
标注该注解的类会被 spring 解析,类中通过@bean注解修饰的方法,其返回对象会被注册为 spring ioc 容器中的 bean,由容器统一管理实例化、依赖注入与生命周期,是 spring 注解驱动开发的核心基础。
实战代码:
@configuration
public class webconfig implements webmvcconfigurer {
@autowired
private jwtinterceptor jwtinterceptor;
// 注册拦截器
@override
public void addinterceptors(interceptorregistry registry) {
registry.addinterceptor(jwtinterceptor)
.addpathpatterns("/**");
}
// 配置跨域
@override
public void addcorsmappings(corsregistry registry) {
registry.addmapping("/**")
.allowedoriginpatterns("*")
.allowedmethods("get", "post", "delete", "put");
}
}使用场景: 配置拦截器、跨域、数据源、线程池等
作用: @restcontroller 是 spring mvc 的组合注解,整合@controller和@responsebody核心功能,专门用于标注restful 风格的控制器类,是 spring boot 开发接口的核心注解,无需额外配置即可实现 json/xml 等数据的返回。
@controller:将类标记为 spring mvc 控制器,接收前端请求;@responsebody:默认对类中所有 @requestmapping 系列注解的方法生效,自动将方法返回值序列化为 json/xml 格式,直接写入响应体,替代传统视图跳转,适配前后端分离场景。对比单独使用@controller+ 方法级@responsebody,该注解简化了 rest 接口的开发,无需在每个接口方法上重复标注@responsebody。
等价于: @controller + @responsebody
实战代码:
@restcontroller
@requestmapping("/user")
public class usercontroller {
@postmapping("/login")
public result<string> login(@requestparam string username,
@requestparam string password) {
// 返回json:{"code":200,"msg":"登录成功","data":"token"}
return result.success("登录成功", token);
}
}使用场景: 前后端分离项目,返回json数据
对比:
| 注解 | 返回类型 | 使用场景 |
|---|---|---|
@controller | 视图(html) | 传统mvc项目 |
@restcontroller | json数据 | 前后端分离项目 |
作用: @requestmapping 是 spring mvc核心请求映射注解,用于将前端 http 请求与后端控制器方法建立绑定关系,使 spring 能精准匹配请求并调用对应方法处理,是开发 web 接口 / 页面请求的基础注解,可标注在控制器类或方法上。
实战代码:
@restcontroller
@requestmapping("/user") // 类级别:所有方法的url前缀
public class usercontroller {
// 方法级别:完整url = /user/login
@requestmapping(value = "/login", method = {requestmethod.get, requestmethod.post})
public result<string> login() {
// 支持get和post请求
}
}简化衍生注解(spring mvc 推荐)
为简化method属性配置,spring 提供了 @requestmapping 的专用衍生注解,语义更清晰,开发中优先使用:
@getmapping:等价于@requestmapping(method = requestmethod.get),处理 get 查询请求;@postmapping:等价于@requestmapping(method = requestmethod.post),处理 post 新增 / 提交请求;@putmapping:等价于 @requestmapping(method = requestmethod.put),处理 put 更新请求;@deletemapping:等价于 @requestmapping(method = requestmethod.delete),处理 delete 删除请求。@getmapping("/list") // 等价于 @requestmapping(method = requestmethod.get)
@postmapping("/add") // 等价于 @requestmapping(method = requestmethod.post)
@putmapping("/update") // 等价于 @requestmapping(method = requestmethod.put)
@deletemapping("/delete")// 等价于 @requestmapping(method = requestmethod.delete)
作用: 获取url参数(?key=value)
实战代码:
@postmapping("/login")
public result<string> login(@requestparam string username,
@requestparam string password) {
// 请求:post /user/login?username=admin&password=123456
// username = "admin"
// password = "123456"
}可选参数:
@getmapping("/list")
public result<list<bill>> list(
@requestparam(required = false) string keyword, // 可选参数
@requestparam(defaultvalue = "1") integer page // 默认值
) {
// 请求:get /bill/list
// keyword = null
// page = 1
}
作用:
@requestbody 是 spring mvc 的核心请求体解析注解,专门用于接收前端通过 http 请求体传递的非表单格式数据(如 json、xml),并自动将请求体中的数据反序列化为指定的 java 实体类 / map / 字符串对象,绑定到控制器方法的参数上,是前后端分离场景中接收 json 请求的必备注解。
实战代码:
@postmapping("/register")
public result<user> register(@requestbody user user) {
// 请求体:{"username":"admin","password":"123456"}
// 自动转换为user对象
// user.getusername() = "admin"
// user.getpassword() = "123456"
}
使用场景: post/put请求,提交json数据
常见错误:
// 错误:get请求不能用@requestbody
@getmapping("/list")
public result<list<bill>> list(@requestbody map<string, object> params) {
// get请求没有请求体,会报错
}
// 正确:get请求用@requestparam
@getmapping("/list")
public result<list<bill>> list(@requestparam string keyword) {
// 正确
}作用:
@pathvariable 是 spring mvc 的路径参数绑定注解,核心用于提取 url 路径中的动态占位符参数,并自动将其转换为指定类型后绑定到控制器方法的入参上,是开发restful 风格动态接口的核心注解(如/user/123、/order/456/detail这类含动态 id 的请求)。
实战代码:
@deletemapping("/delete/{id}")
public result<boolean> delete(@pathvariable long id) {
// 请求:delete /bill/delete/123
// id = 123
billservice.deletebyid(id);
return result.success("删除成功");
}
@getmapping("/user/{userid}/bill/{billid}")
public result<bill> getbill(@pathvariable long userid,
@pathvariable long billid) {
// 请求:get /user/10/bill/20
// userid = 10
// billid = 20
}使用场景: restful风格的url
作用: @responsebody 是 spring mvc 的核心响应体处理注解,核心作用是将控制器方法的返回值,直接序列化为 json/xml 等格式的数据流,写入 http 响应体(response body),替代传统的视图跳转(如跳转到 jsp/html 页面),是实现前后端分离接口和restful 风格接口的关键注解。
核心使用特性
@restcontroller是@controller + @responsebody的组合注解,标注 @restcontroller 的类,底层已默认添加类级别的 @responsebody,无需重复标注。实战代码:
@controller // 注意:不是@restcontroller
@requestmapping("/user")
public class usercontroller {
@getmapping("/tologin")
public string tologin() {
return "redirect:/login.html"; // 返回视图
}
@responsebody // 单独使用,返回json
@postmapping("/login")
public result<string> login() {
return result.success("登录成功"); // 返回json
}
}使用场景: 混合使用视图和json返回时
作用: @valid 和 @validated 均是 spring 中用于开启参数校验的核心注解,基于 jsr-380(bean validation 2.0)规范实现,配合 @notnull/@notblank/@min 等校验注解,可自动完成对实体类参数的合法性校验,替代手动 if 判断,简化参数校验逻辑,是开发接口时的必备注解。
二者核心作用一致,但所属规范、功能特性、适用场景存在明确区别,其中 @validated 是 spring 对 @valid 的增强扩展版,开发中可根据需求选择。
实战代码:
// 实体类(user.java)
@data
public class user {
@notblank(message = "用户名不能为空")
@size(min = 3, max = 20, message = "用户名长度必须在3-20个字符之间")
private string username;
@notblank(message = "密码不能为空")
@size(min = 6, max = 10, message = "密码长度必须在6-10个字符之间")
@pattern(regexp = "^(?=.*[a-z])(?=.*[a-z])(?=.*\\d)[a-za-z\\d]{6,10}$",
message = "密码必须包含大小写字母和数字")
private string password;
@pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private string phone;
}
// 控制器
@postmapping("/register")
public result<user> register(@requestbody @valid user user) {
// 如果校验失败,自动返回400错误,不会执行方法体
// 如果校验成功,继续执行
return userservice.register(user);
}常用校验注解:
在博主以前的文章里面有详细的jsr303校验讲解: jsr303 bean validation 详解(权威版).md
| 注解 | 作用 | 示例 |
|---|---|---|
@notnull | 不能为null | @notnull(message = "id不能为空") |
@notblank | 不能为空字符串 | @notblank(message = "用户名不能为空") |
@size | 长度限制 | @size(min = 3, max = 20) |
@pattern | 正则验证 | @pattern(regexp = "^1[3-9]\\d{9}$") |
@min / @max | 数值范围 | @min(value = 0, message = "金额不能为负") |
@email | 邮箱格式 | @email(message = "邮箱格式不正确") |
作用: @exceptionhandler 是 spring mvc 提供的异常处理核心注解,核心作用是捕获并处理 spring 容器中指定类型的异常,可标注在控制器(@controller/@restcontroller)或全局异常处理器的方法上,实现异常的局部 / 全局统一处理,替代传统的 try-catch 代码块,简化异常处理逻辑,让接口返回统一、友好的错误响应。
核心核心特性
@exceptionhandler(nullpointerexception.class)),仅捕获匹配的异常;实战代码:
@restcontrolleradvice // 全局异常处理
public class globalexceptionhandler {
// 处理参数校验异常
@exceptionhandler(methodargumentnotvalidexception.class)
public result<string> handlevalidexception(methodargumentnotvalidexception e) {
string message = e.getbindingresult().getfielderror().getdefaultmessage();
return result.fail(message); // 返回:{"code":400,"msg":"用户名不能为空"}
}
// 处理业务异常
@exceptionhandler(runtimeexception.class)
public result<string> handleruntimeexception(runtimeexception e) {
return result.fail(e.getmessage());
}
// 处理数据库唯一约束异常
@exceptionhandler(sqlintegrityconstraintviolationexception.class)
public result<string> handlesqlexception(sqlintegrityconstraintviolationexception e) {
if (e.getmessage().contains("duplicate entry")) {
return result.fail("数据已存在");
}
return result.fail("数据库操作失败");
}
}使用场景: 统一处理异常,避免在每个方法中try-catch
类上标注的是全局异常处理注解:@restcontrolleradvice 是 spring mvc 提供的全局异常处理 + 数据绑定 + 返回值增强的核心组合注解,是 @controlleradvice + @responsebody 的整合版,专门适配前后端分离项目,核心作用是实现全局统一的异常处理、全局数据绑定、全局返回值封装,无需在每个控制器重复编写逻辑,让接口返回格式统一、异常处理更简洁。
作用: @service 是 spring 框架中业务层组件专用注解,属于 @component 的分层扩展注解,核心作用是标记普通 java 类为spring ioc 容器可管理的业务层 bean,让类被 @componentscan 自动扫描并纳入容器统一管理,是 spring 分层开发(控制层 / 业务层 / 数据层)的核心标识之一。
核心核心特性:
@component,让代码分层更清晰、可读性更强;@component 一致,spring 启动时通过组件扫描,自动将标注该注解的类实例化并注册到 ioc 容器,无需手动配置 bean;@service 组件,可通过 @autowired/@resource 等注解,自动注入依赖的其他 bean(如 @repository 数据层组件);@transactional 注解,可直接在 @service 方法上标注,实现业务层的事务控制(核心适用场景)。实战代码:
@service
@requiredargsconstructor//lombok注解,自动生成构造器,构造器注入,避免字段注入
public class userserviceimpl implements userservice {
private final usermapper usermapper;
@override
public user login(string username, string password) {
// 业务逻辑
user user = usermapper.selectbyusername(username);
if (user == null) {
throw new runtimeexception("用户不存在");
}
if (!user.getpassword().equals(password)) {
throw new runtimeexception("密码错误");
}
return user;
}
}使用场景: 业务逻辑层
作用: @transactional 是 spring 框架实现声明式事务管理的核心注解,基于 aop(面向切面编程)实现,无需手动编写 try-catch 事务提交 / 回滚代码,仅通过注解标注即可为方法 / 类开启事务控制,保证一组数据库操作的原子性(要么全部成功提交,要么任意一步失败则整体回滚),是解决数据库事务问题的标准方案。
核心注解属性(常用配置)
@transactional 提供多个属性用于自定义事务行为,开发中可根据业务需求配置,核心常用属性如下:
| 属性名 | 作用 | 可选值示例、默认值 |
|---|---|---|
readonly | 设置事务是否为只读(适用于仅查询操作,提升数据库性能) | true/false(默认) |
isolation | 设置事务隔离级别(解决并发事务的脏读 / 不可重复读 / 幻读问题) | read_uncommitted/read_committed/repeatable_read/serializable 数据库默认隔离级别(如 mysql 为 repeatable_read) |
propagation | 设置事务传播行为(解决方法嵌套调用时,事务的创建 / 复用规则) | required(最常用)/requires_new/supports等 |
rollbackfor | 指定触发回滚的异常类型(可包含 checked 异常) | exception.class/businessexception.class 默认:runtimeexception.class |
norollbackfor | 指定不触发回滚的异常类型 | illegalargumentexception.class 默认:无 |
timeout | 设置事务超时时间(单位:秒),超时则自动回滚 | 3/5 默认:-1(无超时限制) |
实战代码:
@service
public class announcementserviceimpl implements announcementservice {
@transactional // 开启事务
public void publishannouncement(announcement announcement) {
// 1. 保存公告
announcementmapper.insert(announcement);
// 2. 记录操作日志
logmapper.insert(new log("发布公告", announcement.getid()));
// 如果第2步失败,第1步会自动回滚
}
}使用场景: 多个数据库操作需要保证原子性
常见错误:
// 错误:@transactional只对public方法有效
@transactional
private void saveuser(user user) {
// 事务不生效
}
// 正确:必须是public方法
@transactional
public void saveuser(user user) {
// 事务生效
}
作用:@tablename 是 mybatis-plus(mp)框架的核心表映射注解,专门用于解决java 实体类与数据库表之间的名称映射问题,通过注解直接指定实体类对应的数据库表名,替代传统 mybatis 中手动编写 sql 映射或 xml 配置表名的操作,简化 mp 的单表 crud 开发,是 mp 实现零 sql 单表操作的基础注解之一。
核心设计背景
mybatis-plus 有默认表名映射规则:实体类名(驼峰命名)自动转换为数据库表名(下划线命名)(如 java 实体userinfo → 数据库表user_info)。
当实体类名与数据库表名不一致 / 不满足驼峰转下划线规则时(如实体user对应表t_user、实体order对应表order_info),默认规则失效,需通过@tablename手动指定映射关系,否则 mp 执行 crud 时会因表名不存在抛出 sql 异常。
实战代码:
@data
@tablename("user") // 对应数据库的user表
public class user {
private long userid;
private string username;
}
使用场景: 实体类名和表名不一致时
作用: @tableid 是 mybatis-plus(mp)框架的专属主键映射注解,核心作用是将java 实体类的属性与数据库表的主键字段建立绑定关系,同时指定主键的生成策略(如自增、雪花算法、uuid 等),是 mp 实现单表 crud 时主键自动处理的核心注解,替代传统 mybatis 手动配置主键映射和生成逻辑的操作。
核心设计背景
mybatis-plus 默认会将实体类中名为id的属性映射为数据库表的主键字段,且默认使用雪花算法(idtype.assign_id) 生成主键值。
当实体主键属性名与表主键字段名不一致(如实体userid对应表user_id)、需要自定义主键生成策略(如数据库自增、uuid)时,默认规则失效,需通过@tableid显式配置,否则 mp 执行新增 / 查询等操作时会因主键映射错误 / 生成规则不符抛出异常。
实战代码:
@data
@tablename("user")
public class user {
@tableid(type = idtype.auto) // 主键自增
private long userid;
}
主键策略:
| 策略 | 说明 |
|---|---|
idtype.auto | 数据库自增 |
idtype.assign_id | 雪花算法生成id(默认) |
idtype.input | 手动输入 |
作用: @tablefield 是 mybatis-plus(mp)框架的普通字段专属映射注解,核心作用是将java 实体类的非主键属性与数据库表的普通字段建立绑定关系,同时支持配置字段是否参与 crud、是否为查询条件、字段填充规则等特性,是 mp 解决非主键字段映射不一致、实现字段精细化控制的核心注解,替代传统 mybatis 手动在 xml/sql 中配置字段映射的操作。
核心设计背景
mybatis-plus 有默认普通字段映射规则:实体类属性名(驼峰命名)自动转换为数据库表字段名(下划线命名)(如实体username → 表user_name)。
当实体属性名与表字段名不一致 / 不满足驼峰转下划线规则、需要排除某些字段不参与 mp 的自动 crud、需要配置字段自动填充(如创建时间、更新时间)时,默认规则无法满足需求,需通过@tablefield显式配置,否则 mp 执行操作时会因字段映射错误抛出 sql 异常,或无法实现字段的精细化控制。
实战代码:
@data
@tablename("user")
public class user {
@tableid(type = idtype.auto)
private long userid;
// 自动填充:插入时填充
@tablefield(fill = fieldfill.insert)
private localdatetime createtime;
// 自动填充:插入和更新时都填充
@tablefield(fill = fieldfill.insert_update)
private localdatetime updatetime;
}自动填充处理器:
@component
public class mymetaobjecthandler implements metaobjecthandler {
@override
public void insertfill(metaobject metaobject) {
this.strictinsertfill(metaobject, "createtime", localdatetime.class, localdatetime.now());
this.strictinsertfill(metaobject, "updatetime", localdatetime.class, localdatetime.now());
}
@override
public void updatefill(metaobject metaobject) {
this.strictupdatefill(metaobject, "updatetime", localdatetime.class, localdatetime.now());
}
}作用: @version 是 mybatis-plus(mp)框架实现乐观锁机制的专属核心注解,核心作用是将java 实体类的属性与数据库表的版本数字段绑定,让 mp 自动基于该字段实现乐观锁的版本控制,解决多线程 / 多用户并发更新数据时的脏写问题(避免多个请求同时修改同一条数据,导致数据覆盖、一致性丢失),是 mp 简化乐观锁开发的关键注解。
实战代码:
@data
@tablename("user")
public class user {
@tableid(type = idtype.auto)
private long userid;
@version // 乐观锁
private integer version;
}工作原理:
-- 更新时自动加上version条件 update user set username = ?, version = version + 1 where user_id = ? and version = ? -- 如果version不匹配(被其他线程修改了),更新失败
使用场景: 防止并发修改冲突
作用: @tablelogic 是 mybatis-plus(mp)框架实现逻辑删除的专属核心注解,核心作用是将 java 实体类的属性与数据库表的逻辑删除标识字段绑定,让 mp 自动将单表 crud 中的物理删除(delete)操作替换为逻辑删除(update)操作,同时自动过滤查询 / 更新结果中已逻辑删除的数据,实现数据的「假删除、软删除」,避免真实删除数据导致的历史数据丢失、关联查询异常问题,是 mp 简化逻辑删除开发的关键注解。
实战代码:
@data
@tablename("user")
public class user {
@tableid(type = idtype.auto)
private long userid;
@tablelogic // 逻辑删除:0=正常,1=已删除
private integer deleted;
}效果:
// 调用删除方法 usermapper.deletebyid(1); // 实际执行的sql(不是真删除) update user set deleted = 1 where user_id = 1 and deleted = 0 // 查询时自动过滤已删除的数据 select * from user where deleted = 0
作用: @mapperscan 是 mybatis 及 mybatis-plus 框架的核心 mapper 接口扫描注解,核心作用是指定项目中 mapper 接口的扫描包路径,让框架自动扫描该包下所有的 mapper 接口,并为每个接口动态生成代理实现类,最终将这些代理类注册到 spring ioc 容器中,实现 mapper 接口的依赖注入和数据库操作调用,是整合 mybatis/mp 与 springboot 的关键注解。
实战代码:
@mapperscan("com.liu.mapper") // 扫描mapper包下的所有接口
@springbootapplication
public class simpleaccountingapplication {
public static void main(string[] args) {
springapplication.run(simpleaccountingapplication.class, args);
}
}使用场景: 启动类上,扫描所有mapper接口
作用: @select、@insert、@update、@delete 是 mybatis/mybatis-plus(mp)框架的核心原生 sql 注解,属于 mybatis 基础注解(mp 完全兼容),核心作用是直接在 mapper 接口的方法上标注对应的 sql 语句,让框架将接口方法与 sql 语句直接绑定,调用接口方法时自动执行标注的 sql,实现数据库的查询、新增、更新、删除操作。
这组注解替代了传统 mybatis 中xml 映射文件的编写(如usermapper.xml中的<select>/<insert>标签),实现「sql 与 mapper 接口方法一体化」,简化单表 / 多表的 sql 开发,是 mybatis/mp 中除了 mp 自动 crud 外,自定义 sql 的核心实现方式。
实战代码:
@mapper
public interface usermapper extends basemapper<user> {
@select("select * from user where username = #{username}")
user selectbyusername(string username);
@update("update user set status = #{status} where user_id = #{userid}")
int updatestatus(@param("userid") long userid, @param("status") integer status);
}#{参数名}:实现预编译参数绑定,mybatis 会自动处理参数类型转换,有效防止 sql 注入,是开发中的标准用法;${参数名}:直接将参数拼接到 sql 中,无预编译处理,有 sql 注入风险,仅适用于表名、字段名动态拼接(如select * from ${tablename});// 多参数需标注@param
@select("select * from t_user where username = #{name} and age = #{age}")
user selectbyusernameandage(@param("name") string username, @param("age") integer age);
使用场景: 简单sql可以用注解,复杂sql建议用xml
作用:@aspect 是 spring 框架实现 aop(面向切面编程)的核心注解,核心作用是标记一个普通 java 类为「切面类(aspect)」,让 spring 识别该类为 aop 的核心载体 —— 用于封装切入点(pointcut) 和通知(advice) 逻辑,实现业务代码与非业务横切逻辑(如日志记录、事务控制、权限校验、性能监控)的解耦,是 spring aop 开发的基础注解。
实战代码:
@aspect
@component
@slf4j
public class rolecheckaspect {
@before("@annotation(requirerole)")//在切点之前运行的代码
public void checkrole(joinpoint joinpoint, requirerole requirerole) {
// 权限验证逻辑
int[] allowedroles = requirerole.value();
int userrole = tokenhelper.getrole(request);
boolean haspermission = arrays.stream(allowedroles)
.anymatch(role -> role == userrole);
if (!haspermission) {
throw new runtimeexception("无权限访问");
}
}
}@aspect 关键使用注意事项(避坑重点)
@aspect仅标记该类为切面,不会自动将其注册到 spring ioc 容器,必须配合@component(或 @service/@repository),让 spring 扫描并管理该 bean,否则 aop 逻辑完全失效。
@around是唯一能控制目标方法执行的通知,必须遵守 3 个规则,否则会导致业务异常:
joinpoint.proceed():执行目标业务方法,省略则目标方法不会执行;proceed()的结果:否则目标方法的返回值会丢失(前端获取不到数据);execution(* *..*.*(..))),会匹配所有方法,导致性能损耗;spring aop 默认基于动态代理实现,存在 2 个核心限制,会导致切面逻辑不生效:
4.1 仅对 spring 容器中的 bean 生效
非 spring 管理的类(未标注 @component/@service 等),其方法无法被切面增强,解决方案:将类注册为 spring bean。
4.2 仅对公共(public)方法生效
private/protected/default 访问权限的方法,spring aop 不会生成代理,切面逻辑无法织入,解决方案:将方法改为 public(若需增强非公共方法,需使用 aspectj 原生织入,而非 spring aop)。
同一类中,无切面增强的方法调用有切面增强的方法,因未经过 spring 代理对象,切面逻辑无法织入,这是 spring aop 最常见的坑:
@service
public class userservice {
// 无切面增强的方法
public void test() {
this.saveuser(); // 内部自调用,saveuser的切面逻辑失效
}
// 有切面增强的方法(如@transactional/@around)
public void saveuser() {
// 业务逻辑
}
}解决方案:
aopcontext.currentproxy()获取代理对象调用。使用场景: 权限验证、日志记录、性能监控
作用: @before、@after、@around 是 spring aop 中最核心的三大通知注解,与 @aspect 切面类配合使用,核心作用是定义横切逻辑的织入时机—— 将日志记录、性能监控、权限校验等通用非业务逻辑,精准织入到目标方法的执行前、执行后、执行前后全生命周期,实现业务代码与横切逻辑的解耦,是 spring aop 实现「无侵入式功能增强」的核心载体。
三者均标注在切面类(@aspect + @component 标注)的方法上,需绑定切入点(@pointcut) 指定增强的目标方法,共同覆盖目标方法的核心执行节点,其中 @around 功能最全面,可实现前两者的所有效果。
三大注解核心定义与核心作用:
核心定义:标注在切面类方法上,指定横切逻辑在目标方法执行前执行,是最基础的通知类型。
核心作用:用于实现目标方法执行前的预处理逻辑,无返回值,无法修改目标方法的入参,也无法控制目标方法是否执行。
核心适用场景:权限校验、接口入参日志记录、资源初始化(如数据库连接准备)、请求参数预处理等。
核心定义:标注在切面类方法上,指定横切逻辑在目标方法执行结束后执行(无论目标方法是否抛出异常),是「必执行」的通知类型。
核心作用:用于实现目标方法执行后的收尾逻辑,无返回值,无法获取目标方法返回值或异常信息,仅做最终兜底处理。
核心适用场景:资源统一释放(如关闭数据库连接、清空临时缓存、关闭文件流)、方法执行完成的通用日志标记等。
核心定义:标注在切面类方法上,指定横切逻辑包裹目标方法的全执行生命周期(执行前 + 执行中 + 执行后 + 异常时),是功能最强大、最灵活的通知类型。
核心作用:可实现目标方法的全流程控制:能修改入参、手动控制目标方法是否执行、获取 / 修改返回值、捕获处理异常,完全覆盖 @before 和 @after 的能力。
核心适用场景:性能监控(统计方法执行耗时)、全流程日志记录(入参 + 执行结果 + 耗时)、核心接口的权限控制、方法返回值缓存等。
核心使用差异
| 注解 | 织入时机 | 核心能力 | 方法入参 | 关键特性 | 核心限制 |
|---|---|---|---|---|---|
| @before | 目标方法执行前 | 仅做前置预处理,无返回值 | joinpoint(获取方法信息) | 执行时机固定,逻辑简单 | 无法修改入参、无法控制目标方法执行、无返回值 |
| @after | 目标方法执行结束后(必执行) | 仅做最终收尾处理,无返回值 | joinpoint(获取方法信息) | 无论异常与否都会执行 | 无法获取返回值、无法捕获异常、无返回值 |
| @around | 目标方法执行前后全生命周期 | 修改入参、控制执行、获取 / 修改返回值、捕获异常,覆盖前两者所有能力 | proceedingjoinpoint(含执行方法) | 功能最全面,灵活性最高 | 必须调用 proceed() 执行目标方法,必须返回结果 |
关键入参说明
@before/@after 可直接使用;joinpoint,新增 proceed() 方法 ——手动执行目标业务方法,是 @around 的专属入参,无此方法则无法触发目标方法执行。完整使用示例(同一切面,对比实现)
以业务层方法(@service) 为增强目标,实现「日志记录 + 性能监控」功能,分别演示三者的使用方式,统一绑定同一个切入点,便于对比差异。
环境前提
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-aop</artifactid>
</dependency>import org.springframework.stereotype.service;
@service
public class userservice {
// 正常方法:返回结果
public string getuserbyid(long id) throws interruptedexception {
thread.sleep(80); // 模拟业务执行耗时
return "查询到用户id:" + id;
}
// 异常方法:抛出运行时异常
public void deleteuserbyid(long id) {
throw new runtimeexception("模拟删除失败:用户不存在");
}
}切面类实现(@aspect + @component)
import org.aspectj.lang.joinpoint;
import org.aspectj.lang.proceedingjoinpoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springframework.stereotype.component;
import java.util.arrays;
// 1. 标记为切面类 2. 注册为spring bean(必须,否则aop失效)
@aspect
@component
public class aopadvicedemoaspect {
private static final logger log = loggerfactory.getlogger(aopadvicedemoaspect.class);
// 定义公共切入点:匹配userservice类的所有公共方法(精准增强目标)
@pointcut("execution(public * com.example.demo.service.userservice.*(..))")
public void userservicepointcut() {
// 切入点方法:仅做标识,无需编写任何逻辑
}
// ==================== 1. @before 前置通知 ====================
@before("userservicepointcut()")
public void dobefore(joinpoint joinpoint) {
// 获取方法信息:类名.方法名
string methodfullname = joinpoint.getsignature().getdeclaringtypename() + "." + joinpoint.getsignature().getname();
// 获取方法入参
object[] args = joinpoint.getargs();
// 前置逻辑:记录方法开始执行 + 入参
log.info("【@before 前置通知】方法{}开始执行,入参:{}", methodfullname, arrays.tostring(args));
}
// ==================== 2. @after 最终通知 ====================
@after("userservicepointcut()")
public void doafter(joinpoint joinpoint) {
string methodname = joinpoint.getsignature().getname();
// 最终逻辑:标记方法执行结束(无论是否异常,都会打印)
log.info("【@after 最终通知】方法{}执行结束(必执行)", methodname);
}
// ==================== 3. @around 环绕通知 ====================
@around("userservicepointcut()")
public object doaround(proceedingjoinpoint joinpoint) throws throwable {
// ① 执行前逻辑:记录开始时间 + 获取方法信息(等效@before)
long starttime = system.currenttimemillis();
string methodfullname = joinpoint.getsignature().getdeclaringtypename() + "." + joinpoint.getsignature().getname();
object[] args = joinpoint.getargs();
log.info("【@around 执行前】方法{}入参:{}", methodfullname, arrays.tostring(args));
object result = null;
try {
// ② 手动执行目标方法(核心:必须调用,否则目标方法不执行)
result = joinpoint.proceed();
// ③ 执行成功后逻辑:记录返回值 + 耗时
long costtime = system.currenttimemillis() - starttime;
log.info("【@around 执行成功】方法{}返回值:{},耗时:{}ms", methodfullname, result, costtime);
} catch (throwable e) {
// ④ 执行异常时逻辑:记录异常信息 + 耗时
long costtime = system.currenttimemillis() - starttime;
log.error("【@around 执行异常】方法{}执行失败,耗时:{}ms,异常:{}", methodfullname, costtime, e.getmessage());
throw e; // 重新抛出异常,让全局异常处理器处理(不可省略)
}
// ⑤ 返回目标方法结果(核心:必须返回,否则调用方获取不到返回值)
return result;
}
}实战代码:
@aspect
@component
public class logaspect {
// 前置通知:方法执行前
@before("execution(* com.liu.controller.*.*(..))")
public void before(joinpoint joinpoint) {
log.info("方法执行前:{}", joinpoint.getsignature().getname());
}
// 后置通知:方法执行后
@after("execution(* com.liu.controller.*.*(..))")
public void after(joinpoint joinpoint) {
log.info("方法执行后:{}", joinpoint.getsignature().getname());
}
// 环绕通知:方法执行前后
@around("execution(* com.liu.controller.*.*(..))")
public object around(proceedingjoinpoint joinpoint) throws throwable {
long start = system.currenttimemillis();
object result = joinpoint.proceed(); // 执行方法
long end = system.currenttimemillis();
log.info("方法执行耗时:{}ms", end - start);
return result;
}
}作用:自定义注解是 java 提供的元编程能力,允许开发者根据业务需求定义专属的注解类型,结合 spring 框架的 aop、反射等特性,可实现无侵入式的业务增强(如自定义权限校验、操作日志、接口限流等),是企业级开发中实现通用逻辑复用、简化业务代码的核心手段。
自定义注解的核心价值是将通用逻辑与业务代码解耦:通过注解标记需要增强的类 / 方法 / 属性,再通过注解解析器(反射 / aop)识别注解并执行对应的横切逻辑,无需修改业务代码即可实现功能增强。
实战代码:
// 1. 定义注解
@target(elementtype.method) // 作用在方法上
@retention(retentionpolicy.runtime) // 运行时有效
public @interface requirerole {
int[] value() default {}; // 允许的角色
}
// 2. 使用注解
@restcontroller
@requestmapping("/admin")
public class admincontroller {
@requirerole({constants.role_admin}) // 只允许管理员访问
@getmapping("/users")
public result<list<user>> getallusers() {
return result.success(userservice.list());
}
}
// 3. 切面处理注解
@aspect
@component
public class rolecheckaspect {
@before("@annotation(requirerole)")
public void checkrole(joinpoint joinpoint, requirerole requirerole) {
// 验证权限
}
}使用场景: 权限验证、日志记录、缓存控制
首先先引入依赖:
<!-- maven 依赖(与其他 lombok 注解共用) -->
<dependency>
<groupid>org.projectlombok</groupid>
<artifactid>lombok</artifactid>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>作用: @data 是 lombok 框架的核心注解,核心作用是自动为 java 类生成通用的模板代码,包括所有成员变量的getter/setter方法、tostring()、equals()、hashcode()方法,以及全参 / 无参构造器的核心部分,彻底消除实体类、pojo 类中大量重复的模板代码,大幅简化类的编写,提升开发效率并减少代码维护成本。
实战代码:
@data
public class user {
private long userid;
private string username;
private string password;
}
// 等价于手写:
public class user {
private long userid;
private string username;
private string password;
public long getuserid() { return userid; }
public void setuserid(long userid) { this.userid = userid; }
// ... 其他getter/setter
@override
public string tostring() { ... }
@override
public boolean equals(object o) { ... }
@override
public int hashcode() { ... }
}作用: @noargsconstructor 和 @allargsconstructor 是 lombok 框架的核心基础注解,二者配合使用可分别为 java 类自动生成无参构造器和全参构造器,彻底消除手动编写构造器的模板代码,简化对象实例化操作,常与 @data 组合成为实体类、pojo 类的标配注解。
构造器是 java 类实例化对象的核心入口,手动编写多参构造器不仅繁琐,还会在新增 / 删除字段时需同步修改,而这两个注解通过编译期字节码增强自动生成构造器,开发时仅需注解标注,编译后字节码中会包含对应构造器,与手动编写完全一致且运行时无性能损耗。
实战代码:
@data
@noargsconstructor // 无参构造器
@allargsconstructor // 全参构造器
public class user {
private long userid;
private string username;
}
// 等价于:
public class user {
public user() {} // 无参构造器
public user(long userid, string username) { // 全参构造器
this.userid = userid;
this.username = username;
}
}作用: @requiredargsconstructor 是 lombok 框架的核心构造器注解,核心作用是为 java 类自动生成包含「必须初始化成员变量」的构造器,这里的「必须初始化变量」特指被 final 修饰的成员变量和被 @nonnull 注解标注的成员变量(这两类变量在 java 语法中要求必须初始化,否则编译报错)。该注解精准解决了「仅为关键变量生成带参构造器」的需求,避免全参构造器参数冗余,常与 @data、@noargsconstructor 等注解组合使用,是实体类、配置类、服务类开发中的常用注解。
实战代码:
@service
@requiredargsconstructor // 自动生成构造器
public class userserviceimpl implements userservice {
private final usermapper usermapper; // final修饰,依赖注入
private final jwtutil jwtutil;
// lombok自动生成:
// public userserviceimpl(usermapper usermapper, jwtutil jwtutil) {
// this.usermapper = usermapper;
// this.jwtutil = jwtutil;
// }
}为什么推荐?
作用: @slf4j 是 lombok 框架的核心日志注解,核心作用是为 java 类自动生成基于 slf4j 规范的日志对象(private static final org.slf4j.logger log = org.slf4j.loggerfactory.getlogger(当前类.class);),彻底消除手动声明日志对象的模板代码,直接通过 log.info()/log.error() 等方法打印日志,是企业级 java 开发中日志记录的标配注解,与 @data、@service、@restcontroller 等注解无缝配合使用。
实战代码:
@service
@slf4j // 自动生成log对象
public class userserviceimpl implements userservice {
public user login(string username, string password) {
log.info("用户登录:{}", username); // 直接使用log
log.error("登录失败:{}", username);
log.debug("调试信息:{}", username);
}
}
// 等价于:
public class userserviceimpl implements userservice {
private static final logger log = loggerfactory.getlogger(userserviceimpl.class);
}作用: @builder 是 lombok 框架的核心创建型模式注解,核心作用是为 java 类自动实现建造者(builder)设计模式,通过链式调用的方式灵活创建对象,替代传统的无参构造 + 多次 setter、多参构造器的对象实例化方式,解决多字段对象创建时代码繁琐、参数顺序易混淆、可选参数处理麻烦的问题,是企业级开发中创建复杂对象的标配注解,常与 @data、@noargsconstructor、@allargsconstructor 组合使用。
实战代码:
@data
@builder
public class user {
private long userid;
private string username;
private string password;
}
// 使用:
user user = user.builder()
.userid(1l)
.username("admin")
.password("123456")
.build();使用场景: 构建复杂对象
@restcontroller
@requestmapping("/user")
@requiredargsconstructor
@slf4j
public class usercontroller {
private final userservice userservice;
@postmapping("/login")
public result<string> login(@requestparam string username,
@requestparam string password) {
log.info("用户登录:{}", username);
return userservice.login(username, password);
}
}@data
@noargsconstructor
@allargsconstructor
@tablename("user")
public class user {
@tableid(type = idtype.auto)
private long userid;
@notblank(message = "用户名不能为空")
private string username;
@tablefield(fill = fieldfill.insert)
private localdatetime createtime;
@version
private integer version;
@tablelogic
private integer deleted;
}@service
@requiredargsconstructor
@slf4j
public class userserviceimpl implements userservice {
private final usermapper usermapper;
@transactional
public void saveuser(user user) {
log.info("保存用户:{}", user.getusername());
usermapper.insert(user);
}
}// 错误:没有@service注解,spring不会管理这个类
public class userserviceimpl implements userservice {
@autowired
private usermapper usermapper; // 注入失败,usermapper为null
}
// 正确:加上@service
@service
public class userserviceimpl implements userservice {
@autowired
private usermapper usermapper; // 注入成功
}// 错误:usermapper没有被spring管理
public interface usermapper { // 缺少@mapper注解
user selectbyid(long id);
}
// 正确:加上@mapper或在启动类加@mapperscan
@mapper
public interface usermapper {
user selectbyid(long id);
}// 错误:private方法,事务不生效
@transactional
private void saveuser(user user) {
usermapper.insert(user);
}
// 正确:必须是public方法
@transactional
public void saveuser(user user) {
usermapper.insert(user);
}// 错误:不能同时使用
@postmapping("/add")
public result<boolean> add(@requestbody bill bill,
@requestparam long userid) {
// 报错
}
// 正确:要么全用@requestbody,要么全用@requestparam
@postmapping("/add")
public result<boolean> add(@requestbody map<string, object> params) {
long userid = (long) params.get("userid");
bill bill = (bill) params.get("bill");
}| 排名 | 注解 | 使用频率 | 重要程度 |
|---|---|---|---|
| 1 | @restcontroller | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 2 | @requestmapping | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 3 | @autowired | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 4 | @service | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 5 | @data | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 6 | @requestparam | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 7 | @requestbody | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 8 | @pathvariable | ⭐⭐⭐ | ⭐⭐⭐ |
| 9 | @transactional | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 10 | @valid | ⭐⭐⭐ | ⭐⭐⭐⭐ |
需要返回json? → @restcontroller
需要获取url参数? → @requestparam
需要获取json数据? → @requestbody
需要获取路径参数? → @pathvariable
需要参数校验? → @valid + 校验注解
需要依赖注入? → @autowired 或 @requiredargsconstructor
需要事务? → @transactional
需要日志? → @slf4j
需要权限验证? → 自定义注解 + @aspect
这篇文章整理了我在开发记账系统时用到的所有注解,每个示例都来自真实项目代码。
到此这篇关于spring boot常用注解速查表(30个必会注解+实战案例)的文章就介绍到这了,更多相关spring boot注解速查表内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论