1、@RestController
@RestController = @Controller + @ResponseBody
它是接口的 “业务入口”:
- 你写的
SetmealController、AddressBookController都用它标记 - 里面的方法用
@GetMapping/@PostMapping标记,直接接收前端请求、执行业务逻辑、返回数据 - 就像餐厅里的服务员,直接对接顾客,处理点餐、结账这些业务请求
@RestController @RequestMapping("/admin/setmeal") public class SetmealController { // 套餐分页查询接口,直接给前端返回数据 @GetMapping("/page") public Result<PageResult> page(...) { return Result.success(pageResult); } }2.@RestControllerAdvice
它是所有接口的 “全局管家”:(作全局异常处理器)
- 不写业务接口,只做 “增强处理”,比如全局异常捕获、统一响应格式、全局数据处理
- 它的方法永远不会被前端直接调用,只有当业务接口出问题 / 需要增强时,才会被触发
- 就像餐厅里的后勤主管,服务员出问题了(比如顾客投诉、订单报错),主管统一出来处理,不用每个服务员自己解决
@RestControllerAdvice public class GlobalExceptionHandler { // 全局捕获所有业务异常,统一返回错误信息 @ExceptionHandler(BaseException.class) public Result handleException(BaseException ex) { log.error("异常:{}", ex.getMessage()); return Result.error(ex.getMessage()); } }3、@ExceptionHandler
@ExceptionHandler = 抓异常的 “钩子”
哪个方法加了它,哪个方法就负责 “接住” 抛出来的异常!
必须配合@RestControllerAdvice一起用!
@RestControllerAdvice // 全局管家 public class GlobalExceptionHandler { @ExceptionHandler // 抓异常钩子 public Result handler(BaseException e) { // 处理异常 } }括号里写的是什么异常,它就抓什么异常
例子 1:只抓自定义业务异常
@ExceptionHandler(BaseException.class)只抓:(所有继承BaseException的异常)
优先匹配最精确、最子类的异常,找不到才去匹配父类。
例子 2:抓所有异常(包括空指针、数据库错)
@ExceptionHandler(Exception.class)例子3:括号里啥都不写 → 默认捕获「方法参数声明的那个异常类型」
@ExceptionHandler public Result exceptionHandler(BaseException ex){ }方法参数写的是BaseException ex
那它就只抓:BaseException 本身,所有继承 BaseException 的子类自定义异常
4、@RequestMapping
@RequestMapping:一般写在类上,统一给整个控制器加接口前缀
@RestController @RequestMapping("/admin/employee") public class EmployeeController { }作用:
- 给当前类所有接口统一加路径前缀
- 后面每个方法的地址,都会拼接在它后面
比如:
- 方法
@PostMapping("/login") - 最终完整路径:/admin/employee/login
5、@PostMapping
写在方法上
@PostMapping("/login") public Result login(){...}等价于简写:
@RequestMapping(value = "/login", method = RequestMethod.POST)作用:
- 限定这个接口只能用 POST 请求
- 适合:登录、新增、修改、删除(要传请求体数据)
6、@GetMapping
作用:
- 只能GET 请求
- 适合:查询、分页、获取数据
- 参数一般拼在 URL 地址栏,不能传 JSON 请求体
还有其他的Mapping直接总结:
| 注解 | 请求方式 | 业务用法 |
|---|---|---|
| @GetMapping | GET | 查询、获取详情、分页列表 |
| @PostMapping | POST | 登录、新增、提交表单 |
| @PutMapping | PUT | 整体修改、全量更新 |
| @DeleteMapping | DELETE | 删除数据 |
| @PatchMapping | PATCH | 局部更新、部分字段修改 |
7、@Slf4j
@Slf4j是Lombok 提供的注解,加在类上,自动帮你生成日志对象,不用自己手动写。
写上@Slf4j之后,你在类里可以直接用:
log.info("员工登录:{}", dto); log.error("异常信息:{}", e.getMessage());不用自己手动声明 Logger 对象。
不加 @Slf4j 你要自己手写
如果不用这个注解,你得每一个类都手动写一行:
private static final Logger log = LoggerFactory.getLogger(EmployeeController.class);很麻烦、重复代码多。
加了 @Slf4j,这行代码 Lombok 自动帮你生成了。
日志常用四个级别(从低到高)
log.debug("调试信息"); // 开发调试用 log.info("普通业务日志"); // 正常业务记录 log.warn("警告信息"); // 不崩溃但有风险 log.error("错误异常"); // 报错、异常8、@Configuration
@Configuration标记这个类是【配置类】Spring 会把它当成配置文件来用,代替传统 xml 配置。
一、核心作用
被
@Configuration修饰的类,本身也会被 Spring 当成 Bean 纳入容器管理类里面配合
@Bean注解,手动创建对象、交给 Spring 容器,供后续@Autowired注入使用
二、最经典用法搭配
@Configuration public class MyConfig { // @Bean:把方法返回的对象,交给Spring容器 @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }之后在任意 Controller/Service 直接注入:
@Autowired private RestTemplate restTemplate;注意:
自己写的类用
@Service/@Component托管;第三方框架类(RedisTemplate、RestTemplate、Mybatis 插件等)没法加注解,就用@Configuration + @Bean手动注册。
解释:
第一种:自己写的类 → 直接加注解托管
你自己写的类:
EmployeeService
DishService
各种工具类、自定义类
你能改源代码,所以直接在类上加:
@Service@Component@RestController
举例:
@Service public class EmployeeServiceImpl implements EmployeeService { // 这是你自己写的类,想加注解随便加 }加完注解 → Spring 启动自动把这个类创建对象、放进容器后面直接:
@Autowired private EmployeeService employeeService;第二种:第三方框架类 → 你改不了源码
比如:RedisTemplate,RestTemplate,Mybatis 分页插件,阿里云 OSS 工具类,JwtUtil 第三方类
这些类不是你写的,是框架自带的,你打不开源码、不能在类上面加 @Service / @Component。
用@Configuration + @Bean手动帮你 new 对象,然后放进 Spring 容器。
举个最直观例子
// 这是Spring框架自带的类,你不能编辑它源码 public class RestTemplate { // 里面代码都是框架写好的 }你没法在上面加@Component。
那你就写配置类,手动创建并交给 Spring
@Configuration public class WebConfig { @Bean public RestTemplate restTemplate() { // 手动new出来 return new RestTemplate(); } }@Configuration:告诉 Spring 这是配置类,@Bean:把方法返回的对象自动放入 Spring 容器
三、关键特性(重点)
1. 配置类里面@Bean方法互相调用,不会重复 new 对象
@Configuration是Full 全配置模式,底层用 CGLIB 动态代理,调用本类@Bean方法,直接从容器拿现成 Bean,不会新建。
9、@ConditionalOnMissingBean
只有当 Spring 容器里没有这个 Bean时,我才创建
作用:保证一个类在 Spring 容器里只有一个实例(Bean)。
它不是必须加的!它只用于一种场景:
你不确定别人会不会也创建这个 Bean!
示例:
@Configuration public class MyConfig { /** * @ConditionalOnMissingBean * 作用:当 Spring 容器中 没有 JdbcTemplate 这个 Bean 时 * 才创建这个 Bean */ @Bean @ConditionalOnMissingBean public JdbcTemplate jdbcTemplate() { return new JdbcTemplate(); } }10、@Controller
标记这是一个控制器
用来接收前端请求
默认返回:页面(视图)比如返回login.html
@Controller // 标记这是控制器 public class UserController { @RequestMapping("/hello") // 访问地址:localhost:8080/hello public String hello() { return "index"; // 返回页面 index.html } }11、@ResponseBody
不可以单独使用。它必须搭配控制器注解使用:@Controller。
@ResponseBody仅负责转换返回数据为 JSON / 字符串,本身不能把类交给 Spring 管理、也无法接收请求。
@Controller // 接收请求 public class UserController { // 加了 @ResponseBody → 返回 JSON,不返回页面 @GetMapping("/user") @ResponseBody public Result getUser() { return Result.success("查询成功"); } }不加 @ResponseBody:
@Controller @GetMapping("/user") public Result getUser() { return Result.success(); }Spring 会把返回值当成页面名字去查找
比如去找:Result.success.jsp→ 直接报 404 错误
12、@Mapper
@Mapper 是 MyBatis 提供的注解,专门标记一个接口为 Mapper 接口。
@Mapper public interface UserMapper { User selectById(Integer id); }自动做 :
不用写实现类,自动生成一个代理对象(impl),把这个对象交给 Spring 管理,可以 @Autowired 注入
自动生成的实现类(impl),会自动帮你做:连接数据库,执行 SQL,封装结果集,关闭连接,处理异常
13、@MapperScan
和@Mapper功能一模一样,都是告诉 MyBatis:这是 Mapper 接口,帮我生成代理对象!
@MapperScan直接在启动类上加一行,扫描整个包,所有接口自动生效!
@SpringBootApplication @MapperScan("com.sky.mapper") // 扫描这个包下所有Mapper public class SkyApplication { public static void main(String[] args) { SpringApplication.run(SkyApplication.class, args); } }14、@Component
@Component是 Spring 通用组件注解作用:把当前类交给 Spring 容器管理,自动创建对象,放入容器,后续可以用 @Autowired 注入
平时用的这几个,底层全部继承 @Component:
@RestController👉 控制器层@Controller👉 控制器层@Service👉 业务层@Repository👉 持久层 / Mapper
15、@ConfigurationProperties
@ConfigurationProperties(prefix = "sky.jwt")
= 自动把 application.yml 里的配置,读到 Java 类里!**
配置文件里一定有这段
application.yml
sky: jwt: admin-secret-key: the-key admin-ttl: 7200000它会自动匹配前缀sky.jwt把下面的配置自动赋值到 Java 类的字段里!
比如这个类:
@Component @ConfigurationProperties(prefix = "sky.jwt") public class JwtProperties { private String adminSecretKey; private Long adminTtl; // get set }效果:
sky.jwt.admin-secret-key→ 自动赋值给adminSecretKey
sky.jwt.admin-ttl→ 自动赋值给adminTtl
16、Swagger常用注解
1. @Api
作用:给整个控制器打标签,接口文档里显示模块名称
@Api(tags = "员工管理接口") @RestController @RequestMapping("/admin/employee") public class EmployeeController { }2. @ApiOperation
作用:给单个接口加功能描述
@PostMapping("/login") @ApiOperation("员工登录") public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO dto){ }3. @ApiModel
作用:给实体类起说明名称
@ApiModel("员工登录DTO") public class EmployeeLoginDTO { }4. @ApiModelProperty
作用:给类的字段加说明、是否必填、示例值
@ApiModelProperty(value = "用户名",required = true,example = "admin") private String username; @ApiModelProperty(value = "密码",required = true,example = "123456") private String password;17、@Target
@Target 用在自定义注解上,(一般和@Retention搭配使用)
作用:规定这个注解能贴在哪
常用值(必须记住这 4 个)
@Target({ ElementType.TYPE, // 类、接口、枚举 ElementType.METHOD, // 方法 ElementType.FIELD, // 字段(成员变量) ElementType.PARAMETER // 方法参数 })示例:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AutoFill { OperationType value(); }18、@Retention
作用:控制注解能保留到哪个阶段(生命周期)
三种级别(从短到长):
RetentionPolicy.SOURCE // 1. 只在源码阶段,编译后直接丢弃 RetentionPolicy.CLASS // 2. 保留到class字节码,运行时丢失(默认) RetentionPolicy.RUNTIME // 3. 一直保留,运行时可以通过反射读取1.SOURCE源码级别
只在
.java源码里有效编译成 class 就没了
用途:IDE 校验、Lombok 这类编译期生成代码
2.CLASS字节码级别(默认不写就是它)
留在 class 文件里
程序运行时拿不到
一般普通标记注解用的少
3.RUNTIME运行时级别(开发最常用)
源码 → class →运行时还在
可以通过反射获取注解信息
Spring、MyBatis、自定义切面、@AutoFill 全都必须用这个
使用场景:
例子 1:自动填充注解 @AutoFill
// 限制:只能写在方法上 @Target(ElementType.METHOD) // 限制:运行时还能被反射/AOP读取 @Retention(RetentionPolicy.RUNTIME) public @interface AutoFill { }例子 2:权限注解、日志注解、拦截注解 全是这样
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LogRecord { }19、@Pointcut
@Pointcut = 切点(切面的 “瞄准器”)作用:告诉 AOP 要对哪些方法进行增强!
@Pointcut 最常用的 2 种写法(两种可用&&连接)
1)按包 / 类 / 方法拦截(execution)
// 拦截 mapper 包下所有类的所有方法 @Pointcut("execution(* com.sky.mapper.*.*(..))") public void pt(){}2)按注解拦截(@annotation)
// 拦截所有加了 @AutoFill 注解的方法 @Pointcut("@annotation(com.sky.annotation.AutoFill)") public void autoFillPointCut(){}20、@Aspect
@Aspect = 告诉 Spring:这个类是【切面类】,里面装 AOP 增强逻辑
import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * @Aspect 【关键】标记这个类是 AOP 切面类 * @Component 必须交给 Spring 管理,否则 AOP 不生效 * @Slf4j 日志输出 */ @Aspect // ✅ 核心注解:标记这是【切面】 @Component // ✅ 必须加:交给 Spring 容器管理 @Slf4j public class MyAspect { // 1. 定义切点:要拦截哪些方法 @Pointcut("execution(* com.sky.service.*.*(..))") public void pt(){} // 2. 前置通知:方法执行前运行 @Before("pt()") public void before(){ log.info("【切面】方法执行前..."); } // 3. 后置通知:方法执行后运行 @After("pt()") public void after(){ log.info("【切面】方法执行后..."); } }21、@Before,@After,@AfterReturning,@AfterThrowing,@Around
| 注解 | 作用 | 执行时机 |
|---|---|---|
| @Before | 前置通知 | 目标方法执行之前运行 |
| @After | 后置通知 | 目标方法执行之后(无论是否异常都执行) |
| @AfterReturning | 返回通知 | 目标方法正常执行完后运行 |
| @AfterThrowing | 异常通知 | 目标方法抛出异常后运行 |
| @Around | 环绕通知 | 方法执行前后都可以增强(最强大) |
1)@Before—— 方法执行前
最常用:权限校验、日志记录、自动填充
@Before("execution(* com.sky.service.*.*(..))") public void before(JoinPoint joinPoint) { System.out.println("方法执行前 ~~~"); }2)@After—— 方法执行后(无论是否异常都走)
相当于finally
@After("execution(* com.sky.service.*.*(..))") public void after() { System.out.println("方法一定执行后 ~~~"); }3)@AfterReturning—— 方法正常返回后
只有不抛异常才会走
@AfterReturning("execution(* com.sky.service.*.*(..))") public void afterReturning() { System.out.println("方法正常执行完毕!"); }4)@AfterThrowing—— 方法抛异常时
只有抛异常才会走
@AfterThrowing("execution(* com.sky.service.*.*(..))") public void afterThrowing() { System.out.println("方法出错了!出现异常!"); }5)@Around—— 环绕通知(最强大)
可以控制方法是否执行、获取返回值、计算耗时
@Around("execution(* com.sky.service.*.*(..))") public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("方法执行前"); // 执行目标方法(必须写!) Object result = pjp.proceed(); System.out.println("方法执行后"); return result; // 返回方法结果 }三、执行顺序
正常执行:
@Around 前 @Before 目标方法 @AfterReturning @After @Around 后抛异常时:
@Around 前 @Before 目标方法(报错) @AfterThrowing @After22、@RequestParam
@RequestParam 用来接收「地址栏拼接的普通参数」也就是接收:?name=xxx&age=18这种Query 参数
一、核心用法
1. 基本写法
// 浏览器地址:/user/get?name=张三 @GetMapping("/user/get") public Result getUser(@RequestParam String name){ // name 就拿到 "张三" }2. 参数名不一致时
前端传:?username=张三后端变量叫name,就要指定映射:
@RequestParam("username") String name3. 设置是否必传
// 默认 required = true 必传,不传400报错 @RequestParam(value = "username", required = false) String name4. 设置默认值
不传参数时给默认值:
@RequestParam(defaultValue = "1") Integer page不加 @RequestParam前端参数名 和 后端形参名一致也能自动接收;
加 @RequestParam可以起别名、设必传、设默认值,更灵活规范。
示例:
前端传1,2,3逗号拼接字符串,后端 3 种接收方式
前端传参形式:
?ids=1,2,3方式一:用 @RequestParam 接字符串自己分割
@GetMapping("/list") public Result getList(@RequestParam String ids){ // 分割成数组 String[] idArr = ids.split(","); // 再转 List<Long> List<Long> idList = Arrays.stream(idArr) .map(Long::valueOf) .collect(Collectors.toList()); }(优先)方式二:直接接List<Long>(Spring 自动切分)
@GetMapping("/list") public Result getList(@RequestParam List<Long> ids){ // 直接就能用,ids 里面就是 [1,2,3] }最简单、项目最常用原理:Spring 自动按逗号分割封装成集合
方式三:直接接 数组
@GetMapping("/list") public Result getList(@RequestParam Long[] ids){ // ids = [1,2,3] }23、@PathVariable
作用:获取 URL 路径占位符{变量}里的参数(路径参数)
请求格式:/user/10接口定义
@GetMapping("/user/{id}") public Result getUser(@PathVariable Integer id){ // id=10 }特点:参数拼在请求路径中,无?,REST 风格
常用简写:参数名和占位符同名可省略括号,@PathVariable Integer id
24、@SpringBootApplication
@SpringBootApplication是组合注解,它内部包含了:
1.@SpringBootConfiguration
标记这是一个配置类
相当于@Configuration
2.@EnableAutoConfiguration
SpringBoot 最核心:自动配置
自动加载 Redis、Mysql、MyBatis、OSS 等配置
自动创建DataSource、RedisTemplate等 Bean
3.@ComponentScan
自动扫描组件
扫描:@Controller、@Service、@Mapper、@Component、@Aspect
告诉 Spring 去哪里扫描这些 Bean
把它们变成 Bean 交给 Spring 管理
@SpringBootApplication 会自动扫描:
启动类 所在的包 以及 所有子包
例如:启动类包:com.sky
会自动扫描:
com.sky.controller
com.sky.service
com.sky.mapper
等所有子包
示例:
@SpringBootApplication @EnableTransactionManagement //开启注解方式的事务管理 @Slf4j @EnableCaching//开启缓存注解功能 @EnableScheduling//开启任务调度 public class SkyApplication { public static void main(String[] args) { SpringApplication.run(SkyApplication.class, args); log.info("server started"); } }25、@Transactional
声明事务!让方法里的数据库操作:要么全部成功,要么全部失败回滚。
@Service public class UserService { @Transactional // 加这里! public void add() { // 两步数据库操作 userMapper.insert(); logMapper.insert(); // 如果这里报错 → 两步操作全部回滚! } }默认:运行时异常(RuntimeException)才回滚编译期异常不回滚
想让所有异常都回滚:
@Transactional(rollbackFor = Exception.class)不能单独用
因为 @Transactional 必须满足 2 个条件才生效:
这个类必须交给 Spring 管理(变成 Bean)→ 需要:
@Service/@ComponentSpring 必须开启事务功能→ 需要:
@EnableTransactionManagement(SpringBoot 自动开)
26、@EnableTransactionManagement
必须开这个开关,@Transactional才会生效
@SpringBootApplication @EnableTransactionManagement // 开启事务 public class ReggieApplication { public static void main(String[] args) { Spring.run(...); } }27、@EnableCaching
开启 Spring 缓存功能的总开关
必须先开这个开关,缓存注解@Cacheable,@CacheEvict,@CachePut才会生效
@SpringBootApplication @EnableCaching // 开启缓存 public class ReggieApplication { public static void main(String[] args) { SpringApplication.run(ReggieApplication.class, args); } }28、@Cacheable
1.@Cacheable —— 查询用
作用:先查缓存,缓存没有才执行方法,查完自动存缓存
@Cacheable(cacheNames = "dishCache", key = "#categoryId")2.@CachePut —— 修改 / 新增用
作用:一定执行方法,把最新结果更新到缓存
@CachePut(cacheNames = "dishCache", key = "#dish.id")3.@CacheEvict —— 删除 / 清空用
作用:删除缓存
//删除某一对应key的缓存 @CacheEvict(cacheNames = "dishCache", key = "#dish.id")// 清空整个缓存 @CacheEvict(cacheNames = "dishCache", allEntries = true)29、@EnableScheduling
开启 Spring 的【定时任务】功能,让@Scheduled注解生效。
@SpringBootApplication @EnableScheduling // 开启定时任务 public class ReggieApplication { public static void main(String[] args) { SpringApplication.run(ReggieApplication.class, args); } }30、@Scheduled
示例:
@Scheduled(cron = "0 0 2 * * ?") public void test3() { System.out.println("每天凌晨2点执行 → " + new Date()); }@Scheduled 有 3 种核心用法(全部掌握)
1. fixedRate —— 固定频率执行
每隔多久执行一次,不管方法执行多久
@Scheduled(fixedRate = 5000) // 单位:毫秒意思:每 5 秒执行一次
2. fixedDelay —— 固定延迟执行
方法执行完后,再等多久执行
@Scheduled(fixedDelay = 5000)意思:执行完 → 等 5 秒 → 再执行
3. cron —— 最强大、最常用(指定时间)
像闹钟一样,指定:秒 分 时 日 月 周
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行一、cron 表达式固定格式
一共 6 个部分,用空格分开:
秒 分 时 日 月 周顺序绝对不能乱
二、每个位置的取值范围
位置 | 名称 | 取值 | 允许符号 |
|---|---|---|---|
1 | 秒 | 0-59 | , - * / |
2 | 分 | 0-59 | , - * / |
3 | 时 | 0-23 | , - * / |
4 | 日 | 1-31 | , - * / ? L W |
5 | 月 | 1-12 | , - * / |
6 | 周 | 1-7(1 = 周日,7 = 周六) | , - * / ? L # |
三、5 个核心符号
1.*:每一秒 / 每一分 / 每一小时
代表 “所有”例:
* * * * * ?意思:每秒都执行
2.?:不指定(日和周不能同时有值,必须一个用?)
日和周互斥!
指定了日 → 周必须用?
指定了周 → 日必须用?
例:
0 0 0 * * ?每天 0 点执行(日用了 *,周用?)
3./:每隔多久执行一次
格式:开始时间/间隔
包括开始的0
例:
每 5 秒执行一次
0/5 * * * * ?每 5 分钟执行一次
0 0/5 * * * ?每 2 小时执行一次
0 0 0/2 * * ?4.-:范围
例:
10-20 * * * * ?每分钟的 10 秒~20 秒,每秒都执行
5.,:枚举(多个值)
例:
10,20,30 * * * * ?每分钟的 10、20、30 秒执行
四、注意用例
每周一 9 点
0 0 9 ? * 2周:1 = 周日,2 = 周一,3 = 周二…7 = 周六