Springboot 常用注解(苍穹外卖) 1、RestControllerRestController Controller ResponseBody它是接口的 “业务入口”你写的SetmealController、AddressBookController都用它标记里面的方法用GetMapping/PostMapping标记直接接收前端请求、执行业务逻辑、返回数据就像餐厅里的服务员直接对接顾客处理点餐、结账这些业务请求RestController RequestMapping(/admin/setmeal) public class SetmealController { // 套餐分页查询接口直接给前端返回数据 GetMapping(/page) public ResultPageResult 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、ExceptionHandlerExceptionHandler 抓异常的 “钩子”哪个方法加了它哪个方法就负责 “接住” 抛出来的异常必须配合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、RequestMappingRequestMapping一般写在类上统一给整个控制器加接口前缀RestController RequestMapping(/admin/employee) public class EmployeeController { }作用给当前类所有接口统一加路径前缀后面每个方法的地址都会拼接在它后面比如方法PostMapping(/login)最终完整路径/admin/employee/login5、PostMapping写在方法上PostMapping(/login) public Result login(){...}等价于简写RequestMapping(value /login, method RequestMethod.POST)作用限定这个接口只能用 POST 请求适合登录、新增、修改、删除要传请求体数据6、GetMapping作用只能GET 请求适合查询、分页、获取数据参数一般拼在 URL 地址栏不能传 JSON 请求体还有其他的Mapping直接总结注解请求方式业务用法GetMappingGET查询、获取详情、分页列表PostMappingPOST登录、新增、提交表单PutMappingPUT整体修改、全量更新DeleteMappingDELETE删除数据PatchMappingPATCH局部更新、部分字段修改7、Slf4jSlf4j是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、ConfigurationConfiguration标记这个类是【配置类】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手动注册。解释第一种自己写的类 → 直接加注解托管你自己写的类EmployeeServiceDishService各种工具类、自定义类你能改源代码所以直接在类上加ServiceComponentRestController举例Service public class EmployeeServiceImpl implements EmployeeService { // 这是你自己写的类想加注解随便加 }加完注解 → Spring 启动自动把这个类创建对象、放进容器后面直接Autowired private EmployeeService employeeService;第二种第三方框架类 → 你改不了源码比如RedisTemplateRestTemplateMybatis 分页插件阿里云 OSS 工具类JwtUtil 第三方类这些类不是你写的是框架自带的你打不开源码、不能在类上面加 Service / Component。用Configuration Bean手动帮你 new 对象然后放进 Spring 容器。举个最直观例子// 这是Spring框架自带的类你不能编辑它源码 public class RestTemplate { // 里面代码都是框架写好的 }你没法在上面加Component。那你就写配置类手动创建并交给 SpringConfiguration 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.htmlController // 标记这是控制器 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、MapperMapper 是 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、ComponentComponent是 Spring 通用组件注解作用把当前类交给 Spring 容器管理自动创建对象放入容器后续可以用 Autowired 注入平时用的这几个底层全部继承 ComponentRestController 控制器层Controller 控制器层Service 业务层Repository 持久层 / Mapper15、ConfigurationPropertiesConfigurationProperties(prefix sky.jwt) 自动把 application.yml 里的配置读到 Java 类里**配置文件里一定有这段application.ymlsky: 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→ 自动赋值给adminSecretKeysky.jwt.admin-ttl→ 自动赋值给adminTtl16、Swagger常用注解1. Api作用给整个控制器打标签接口文档里显示模块名称Api(tags 员工管理接口) RestController RequestMapping(/admin/employee) public class EmployeeController { }2. ApiOperation作用给单个接口加功能描述PostMapping(/login) ApiOperation(员工登录) public ResultEmployeeLoginVO 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、TargetTarget 用在自定义注解上一般和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、PointcutPointcut 切点切面的 “瞄准器”作用告诉 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、AspectAspect 告诉 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、BeforeAfterAfterReturningAfterThrowingAround注解作用执行时机Before前置通知目标方法执行之前运行After后置通知目标方法执行之后无论是否异常都执行AfterReturning返回通知目标方法正常执行完后运行AfterThrowing异常通知目标方法抛出异常后运行Around环绕通知方法执行前后都可以增强最强大1Before—— 方法执行前最常用权限校验、日志记录、自动填充Before(execution(* com.sky.service.*.*(..))) public void before(JoinPoint joinPoint) { System.out.println(方法执行前 ~~~); }2After—— 方法执行后无论是否异常都走相当于finallyAfter(execution(* com.sky.service.*.*(..))) public void after() { System.out.println(方法一定执行后 ~~~); }3AfterReturning—— 方法正常返回后只有不抛异常才会走AfterReturning(execution(* com.sky.service.*.*(..))) public void afterReturning() { System.out.println(方法正常执行完毕); }4AfterThrowing—— 方法抛异常时只有抛异常才会走AfterThrowing(execution(* com.sky.service.*.*(..))) public void afterThrowing() { System.out.println(方法出错了出现异常); }5Around—— 环绕通知最强大可以控制方法是否执行、获取返回值、计算耗时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、RequestParamRequestParam 用来接收「地址栏拼接的普通参数」也就是接收?namexxxage18这种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 种接收方式前端传参形式?ids1,2,3方式一用 RequestParam 接字符串自己分割GetMapping(/list) public Result getList(RequestParam String ids){ // 分割成数组 String[] idArr ids.split(,); // 再转 ListLong ListLong idList Arrays.stream(idArr) .map(Long::valueOf) .collect(Collectors.toList()); }优先方式二直接接ListLongSpring 自动切分GetMapping(/list) public Result getList(RequestParam ListLong 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){ // id10 }特点参数拼在请求路径中无?REST 风格常用简写参数名和占位符同名可省略括号PathVariable Integer id24、SpringBootApplicationSpringBootApplication是组合注解它内部包含了1.SpringBootConfiguration标记这是一个配置类相当于Configuration2.EnableAutoConfigurationSpringBoot 最核心自动配置自动加载 Redis、Mysql、MyBatis、OSS 等配置自动创建DataSource、RedisTemplate等 Bean3.ComponentScan自动扫描组件扫描Controller、Service、Mapper、Component、Aspect告诉 Spring 去哪里扫描这些 Bean把它们变成 Bean 交给 Spring 管理SpringBootApplication 会自动扫描启动类 所在的包 以及 所有子包例如启动类包com.sky会自动扫描com.sky.controllercom.sky.servicecom.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 必须开启事务功能→ 需要EnableTransactionManagementSpringBoot 自动开26、EnableTransactionManagement必须开这个开关Transactional才会生效SpringBootApplication EnableTransactionManagement // 开启事务 public class ReggieApplication { public static void main(String[] args) { Spring.run(...); } }27、EnableCaching开启 Spring 缓存功能的总开关必须先开这个开关缓存注解CacheableCacheEvictCachePut才会生效SpringBootApplication EnableCaching // 开启缓存 public class ReggieApplication { public static void main(String[] args) { SpringApplication.run(ReggieApplication.class, args); } }28、Cacheable1.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 W5月1-12, - * /6周1-71 周日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 周六