SpringBoot常用注解
注:以下注解大多引用自springframework,如果引错包(如引用自junit)可能无法达到预期效果
@Pattern
对方法参数或字段进行正则表达式校验
public Result register(
@Pattern(regexp = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$", message="用户名格式错误") String username,
@Pattern(regexp = "^.{5,16}$", message="密码格式错误") String password
)
注册Bean
作用相同都是注册Bean对象
1.@Component
(放在类上面)
以下注解均基于@Component实现
| 注解 | 继承关系 | 所在层 | 核心职责 | 额外能力 |
|---|---|---|---|---|
@Component |
本体 | 任意 | 通用组件 | 无 |
@Controller |
@Component |
Web 层 | 请求分发 | MVC 支持 |
@Service |
@Component |
业务层 | 业务逻辑 | 事务承载 |
@Repository |
@Component |
持久层 | 数据访问 | 异常转换 |
特殊-声明配置类:@Configuration
- 放在作为配置类的类上
代理特性:调用配置类中的方法会首先从ICO容器中寻找方法,而不是当做普通的方法去新建new一个
什么时候会触发代理?
只有当 @Configuration(proxyBeanMethods = true) 时,且你 “调用了 @Bean 方法” ,才会触发代理拦截。
2.@Bean
- 写到方法上面(而非类)
- 通常写在配置类里面
原理:Spring扫描到
@Bean注解时,会执行被注解修饰的方法,并将返回值注入到容器中
可以用来配置第三方Jar包类,将第三方类注入到IOC容器中
@Configuration
public class SpringConfig {
@Bean
public IUserService userService(){
return new UserService();
}
}
特性:当被修饰的方法有参数时,spring会从容器中寻找该参数并注入
注:若对象已被注册:
1.Bean名称已存在 报错
2.Bean名不存在 不报错
3.@Import(xxx.class)
作用:将xxx.class注入到容器中。相当于额外告诉Spring记得也引用这个类。用作导入配置类或导入普通类。
- 写到类的上面,修饰一个Bean类
注:
1.被@Import修饰的这个类必须是一个Bean,否则不会起作用。
2.被@Import引用的类是否为Bean对象都可以。
3.被@Import引用的类的Bean名默认是全类名。(若已经被指定,则显示类名>默认规则)
基本用法
通过简单导入将被引用的类注册到Bean
注:被引用的类可以已被注册也可以未被注册
@Configuration
@Import(MyImportSelector.class)//导入目标类
public class SpringConfig {
@Bean
public IUserService userService(){
IUserDao iUserDao = UserDao();
UserService userService = new UserService();
return userService;
}
@Bean
public IUserDao UserDao(){
System.out.println("new UserDao()");
return new UserDao();
}
}
进阶用法:ImportSelector接口--批量注册Bean
使用方法:
- 创建一个类,继承
ImportSelector接口并实现其方法 - 批量注册
Bean(通过返回包含待注册Bean对象完整引用的字符串数组实现) - 在配置类上
@Import(实现ImportSelector的类.class)
目录结构(com.example.c3_ico引用下):
- config
- MyImportSelector.java
- SpringConfig.java
// MyImportSelector.java
package com.example.c3_ico.config;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportSelector implements ImportSelector {
/*实现该接口的方法*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
/*数组要返回的字符串就是需要配置的Bean*/
return new String[]{
"com.example.c3_ico.service.UserService",
"com.example.c3_ico.dao.UserDao",
};
}
}
@Configuration
@Import(MyImportSelector.class)//导入该类
public class SpringConfig {
@Bean
public IUserService userService(){
IUserDao iUserDao = UserDao();
UserService userService = new UserService();
return userService;
}
@Bean
public IUserDao UserDao(){
System.out.println("new UserDao()");
return new UserDao();
}
}
进阶用法:ImportBeanDefinitionRegistrar 从根本上实现对Bean对象的细微操控
Bean对象的所有属性都会被封装在BeanDefinition。一个Bean对应一个BeanDefinition对象
Bean对象注册流程详见SpringBoot小知识
作用:可以基于底层去做一些扩展
使用方法:
- 创建一个类,实现
ImportBeanDefinitionRegistrar接口 - 根据
beanDefinition注册Bean
目录结构(com.example.c3_ico引用下):
- config
- MyImportBeanDefinitionRegistrar.java
- SpringConfig.java
//MyImportBeanDefinitionRegistrar.java
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
/*新建一个引用*/
RootBeanDefinition definition = new RootBeanDefinition();
/*设置引用的目标类*/
definition.setBeanClassName("com.example.c3_ico.service.UserService");
/*设置一个Bean名并注册*/
registry.registerBeanDefinition("userService", definition);
}
}
注:
RootBeanDefinition是beanDefinition接口的实现类
//SpringConfig.java
@Configuration
@Import(MyImportBeanDefinitionRegistrar.class)
public class SpringConfig {
}
注:若目标类已被注册为Bean对象则会报错
注入Bean(@Autowired 与 @Resource )
(1)@Autowired与@Resource的使用方法
1. @Autowired 按类型注入
@Component
public class UserService {
public void hello() {
System.out.println("Hello from UserService");
}
}
@Component
public class MyController {
@Autowired
private UserService userService; // 按类型注入
public void run() {
userService.hello();
}
}
@Qualifier("BeanName")指定具体Bean
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class MyServiceA {
// 按类型注入,但因为有多个 GreetingService,需要用 @Qualifier 指定具体 bean
@Autowired
@Qualifier("englishGreeting")
private GreetingService greeting;
public void say() {
System.out.println(greeting.greet()); // 输出 "Hello"
}
}
2. @Resource 按名称优先
@Component("userService")
public class UserService {
public void hello() {
System.out.println("Hello from UserService");
}
}
@Component
public class MyController {
@Resource(name = "userService") // 按名称注入
private UserService service;
public void run() {
service.hello();
}
}
3.@Autowired vs @Resource 匹配规则
@Autowired
- 按类型 查找 Bean
- 如果有多个同类型 Bean → 按名称匹配(字段名 / 参数名)
- 如果仍然不唯一 → 需要配合
@Qualifier或@Primary或@Autowired
@Resource
- 如果写了
@Resource(name="xxx")→ 强制按名称 查找 - 如果没写
name→- 先按 字段名 查找 Bean
- 如果没找到 → 按 类型 查找
- 如果有多个同类型 Bean → ❌ 报错
@Autowired特性
@Autowired可以写在构造函数、方法、字段、参数上
- 构造函数:
- 如果有无参构造函数,则会默认启用无参构造函数
- 如果Bean只有一个有参构造函数,是省略
@Autowired,会自动注入构造函数的参数 - 如果有多个有参构造函数,并且没有无参构造函数:会报错。解决方法:使用
@Autowired标注在你想指定为默认的构造函数上
- 属性:
- 如果想单独指定某个属性为可选注入,则可以单独在属性前加
@Autowired(required = false)注解,如下:
- 如果想单独指定某个属性为可选注入,则可以单独在属性前加
@Service
public class AutoWiredServiceTest {
public AutoWiredServiceTest(@Autowired(required = false) IUserDao userDao) {
System.out.println(userDao);
}
@Autowired
public AutoWiredServiceTest(@Autowired(required = false) IUserDao userDao, IUserService userService) {
System.out.println("这里是默认构造方法"+userService.toString()+userDao.toString());
}
}
- 方法:
- 在方法上添加
@Autowired注解可以自动执行方法(哪怕没有主动调用)并注入参数
- 在方法上添加
@Service
public class AutoWiredServiceTest {
public AutoWiredServiceTest(@Autowired(required = false) IUserDao userDao) {
System.out.println(userDao);
}
@Autowired
public AutoWiredServiceTest(@Autowired(required = false) IUserDao userDao, IUserService userService) {
System.out.println("这里是默认构造方法"+userService.toString()+userDao.toString());
}
/*在方法上添加Autowired可以自动执行方法并注入参数*/
@Autowired
public void aaa(IUserDao userDao) {
System.out.println("自动执行方法并注入参数"+userDao.toString());
}
}
- 可选注入:
@Autowired(required = false)作用:告诉 Spring:这个依赖“能注入就注入,注不进来也别报错”。注入失败则值为null @Autowired默认会根据类型去容器中找对应对象注入(byType),如果找到了多个会再根据名字找(byName)(但根据名字找可能在低版本并不适用,而是直接报错,所以应该尽量避免重复)。当有多个类型共存时的解决方案:- 通过
@Primary来设置某一个为主要的。(但可能会因为Bean的扫描顺序而不生效)
- 通过
注:在构造函数上时,
@Autowired(required=false)可选注入会失效。即如果注入参数失败会直接报错
(2)@Inject强制注入(与@Autowired对比分析)
| 对比点 | @Inject |
@Autowired |
|---|---|---|
| 所属 | Java 标准(JSR-330) | Spring 私有 |
| 包名 | javax.inject / jakarta.inject |
org.springframework |
| 是否强制依赖 | ✅ 必须有 | ❌ 可选 |
是否支持 required=false |
❌ 不支持 | ✅ 支持 |
是否支持 @Primary |
❌ 不支持 | ✅ 支持 |
是否支持 @Qualifier |
✅ 支持 | ✅ 支持 |
| 推荐使用场景 | 框架无关 | Spring 工程 |
@RestController 前后端分离时声明返回数据(而非页面)
1️⃣ 作用
@RestController = @Controller + @ResponseBody
👉 方法返回值直接作为 HTTP 响应体(通常是 JSON)
2️⃣ 用在什么地方
👉 前后端分离的接口类(API Controller)
3️⃣ 解决什么问题
-
避免把返回值当成“页面名”
-
不需要每个方法都写
@ResponseBody
4️⃣ 什么时候用
✅ 注册 / 登录 / 查询接口
✅ Vue / React / App 调用的接口
5️⃣ 什么时候不用
❌ 返回 HTML 页面(Thymeleaf / JSP)
❌ 传统 MVC 页面控制器
📌 一句话记忆:
只要返回的是数据而不是页面,就用
@RestController。
示例:
@Controller
@RestController
public class UserRegister implements IUserRegister{
private UserService userService;
/*构造器注入*/
public UserRegister(UserService userService){
this.userService = userService;
}
@Override
@PostMapping("/api/register")
public CodeResponse register(@RequestBody Map<String, String> map) {
return userService.register(map.get("username"),map.get("password"));
}
}
@Value将变量注入spring容器
- @ConfigurationProperties
- @Value
- @PropertySource
1.普通注入
@Value注解需要依托于一个Bean对象@Value注解需要标注在一个变量上,将变量注入容器(注册Bean对象是将一个对象注入容器,二者有区别)- 更多详细内容见:5.Spring @Value 注解的真实工作原理(修正版)
示例:
@Component
public class User{
@Value("changchang")
private String name;
@Value("2774118934")
private Integer qq;
}
复杂类型(如Map类型、List类型):使用spel表达式
// spel复杂类型
@Value("#{{'key':'value','数学':'100'}}")
private Map<String,Integer> score;
@Value("#{'value1','value2'}")
private List<Stirng> hobbies;
2.设置默认值
@Value("${配置项:默认值}")
当配置项 不存在或为空 时,就会使用冒号后面的默认值。
例:
@Value("${server.port:8080}")
private int port;
server.port存在 → 使用配置值- 不存在 → 使用
8080
3.使用非SpringBoot配置文件
方法1:将自定义配置文件加载到Spring(与原配置文件合并)
- 自定义文件
config/custom.properties
app.name=DemoApp
app.port=9090
- 注册到Spring
@Configuration
@PropertySource("classpath:config/custom.properties")
public class CustomConfig {
}
- 正常使用
@Value(含默认值)
@Value("${app.name:defaultApp}")
private String appName;
@Value("${app.port:8080}")
private int port;
方法2:@ConfigurationProperties + 自定义配置文件(强烈推荐)
- 自定义配置文件
custom.name=test
custom.timeout=30
- 加载配置
@Configuration
@PropertySource("classpath:custom.properties")
@EnableConfigurationProperties(CustomProperties.class)
public class CustomConfig {
}
- 配置类(默认写在字段上)
@ConfigurationProperties(prefix = "custom")
@Data
public class CustomProperties {
private String name = "default";
private int timeout = 60;
}
在“配置类”里,直接给字段赋初始值,这个初始值就作为“默认值”。
- 配置文件 有值 → 用配置文件的
- 配置文件 没有这个配置项 → 用字段上的默认值
- 配置文件 完全不存在 → 也用字段默认值
@Order 改变自动注入的顺序
一、@Order 是做什么的(一句话理解)
@Order 用来指定多个同类 Bean 的执行或注入顺序,数值越小,优先级越高。
二、最基本的使用方式
@Order(1)
@Component
public class AService {
}
@Order(2)
@Component
public class BService {
}
执行顺序:
AService → BService
三、@Order 的常见使用场景(重点)
场景 1:多个 Filter 的执行顺序(最常见)
@Order(1)
@Component
public class AuthFilter implements Filter {
}
@Order(2)
@Component
public class LogFilter implements Filter {
}
执行流程:
AuthFilter → LogFilter → Controller
场景 2:多个 HandlerInterceptor 的顺序
@Order(1)
@Component
public class LoginInterceptor implements HandlerInterceptor {
}
@Order(2)
@Component
public class MetricsInterceptor implements HandlerInterceptor {
}
场景 3:同类型 Bean 注入 List 时的顺序
@Autowired
private List<MyHandler> handlers;
@Order(1)
@Component
class AHandler implements MyHandler {
}
@Order(2)
@Component
class BHandler implements MyHandler {
}
handlers 中的顺序为:
AHandler → BHandler
场景 4:ApplicationRunner / CommandLineRunner 执行顺序
@Order(1)
@Component
public class InitDbRunner implements ApplicationRunner {
}
@Order(2)
@Component
public class InitCacheRunner implements ApplicationRunner {
}
四、@Order 的核心规则(记住这 3 条)
规则 1:数值越小,优先级越高
@Order(-1) > @Order(0) > @Order(10)
规则 2:不写 @Order,默认最低优先级
等价于:
@Order(Integer.MAX_VALUE)
规则 3:只对“同一批对象”生效
- 同接口
- 同父类
- 同集合(List / 数组)
不会影响 Bean 的创建或加载顺序
@SpringBootTest指定某个类为测试启动类
1.作用
- 启动 Spring 容器
- 加载配置、Bean、自动配置
- 和你
main启动的应用几乎一致
2.基本用法
@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@Test
void testUser() {
userService.doSomething();
}
}
特点:
- 默认加载整个项目
- 适合测试:Service + Repository + 配置是否正常
- 启动慢,但最真实
3.进阶用法
3.1 指定启动类
@SpringBootTest(classes = MyApplication.class)
例:
目录结构:
- test
- A.java
- B.java
- TestOrder.java
TestOrder.java
package com.example.springboot1;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
//此处没有指定启动类,默认为Application.java,即也默认启用其配置类
@SpringBootTest
public class TestOrder {
@Bean
public A a(){
return new A();
}
@Bean
public B b(){
return new B();
}
@Test
public void test(@Autowired A a){
System.out.println(a);
}
}
该段代码会报错,因为即使给a()注册了Bean对象,在@SpringBootTest处没有指定启动类,默认以Application类启动,即也启用其配置类,而非TestOrder.java本身的配置类
package com.example.springboot1;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
//此处指定自身为启动类,即应用自身的配置
@SpringBootTest(classes = TestOrder.class)
public class TestOrder {
@Bean
public A a(){
return new A();
}
@Bean
public B b(){
return new B();
}
@Test
public void test(@Autowired A a){
System.out.println(a);
}
}
注:
@SpringBootTest默认会去找@SpringBootApplication(或@SpringBootConfiguration)标注的主启动类,而不是测试类本身。
3.2 使用测试配置
@SpringBootTest(properties = {
"server.port=0",
"spring.datasource.url=jdbc:h2:mem:test"
})
覆盖 application.yml,测试专用配置
3.3 Web环境配置
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
可选值:
NONE:不启动 Web(最快)MOCK(默认):Mock Servlet 环境RANDOM_PORT:随机端口(测试接口常用)DEFINED_PORT:固定端口
@DependsOn 调整Bean的初始化顺序
一、@DependsOn 是什么
@DependsOn 是 Spring 提供的注解,用于显式指定 Bean 的初始化顺序。
核心含义:
当前 Bean 必须在指定的 Bean 初始化完成之后 才会被创建。
@DependsOn 的语义是:
在 指定的 Bean 完全创建完成之后,才创建被标注的 Bean。
二、基本用法
1️⃣ 作用在类上
@Component
@DependsOn("dataSource")
public class UserService {
}
含义:
UserService 初始化之前,dataSource 必须先完成初始化。
2️⃣ 作用在 @Bean 方法上
@Configuration
public class AppConfig {
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
@Bean
@DependsOn("dataSource")
public UserService userService() {
return new UserService();
}
}
3️⃣ 依赖多个 Bean
@DependsOn({"beanA", "beanB"})
三、@DependsOn 与 @Autowired 的区别
| 对比点 | @DependsOn | @Autowired |
|---|---|---|
| 是否注入对象 | 否 | 是 |
| 是否控制初始化顺序 | 是 | 否 |
| 关注重点 | Bean 初始化顺序 | Bean 依赖注入 |
结论:
@DependsOn 只控制“谁先初始化”,不负责对象的注入。
四、典型使用场景
✅ 1. 初始化顺序敏感的组件
- 数据源(DataSource)
- Redis / 缓存组件
- 线程池、全局配置加载器
@DependsOn("redisTemplate")
✅ 2. 隐式依赖场景
代码中没有直接注入,但逻辑上依赖另一个 Bean 的初始化结果,例如:
- 在
@PostConstruct中使用全局配置 - 全局配置由其他 Bean 初始化
@DependsOn("globalConfig")
✅ 3. 第三方 SDK 或旧系统整合
- SDK 初始化顺序固定
- 无法通过
@Autowired明确表达依赖关系
@Lazy 懒加载Bean
- 正常的Bean对象:Spring容器启动时就加载好
- 懒加载Bean(@Lazy):在第一次被用到时才去加载。Spring容器启动时不会去new
使用方法
使用@Lazy注解
@SpringBootTest(classes= TestLazy.class)
public class TestLazy {
@Bean
@Lazy
public E e(){
return new E();
}
@Test
public void test() {
}
}
全局懒加载(Spring Boot)
spring:
main:
lazy-initialization: true
效果:
- 所有 Bean 默认懒加载
- 常用于大型项目启动加速
使用场景
✅ 适合用懒加载
- 启动慢的大型系统
- 很少用到的 Bean
- 创建成本高的 Bean(连接池、远程调用客户端)
- 有循环依赖但不想立刻触发
❌ 不建议使用
- 核心业务 Bean
- 启动阶段就必须初始化的组件
- 对首次响应时间极敏感的接口
@Scope 改变Bean的作用域
默认Bean单例(只会new一次,不管@Autowired多少,只会创建一次实例)
好处:节省内存空间(有线程安全风险)
注解可用值
- 默认单例:
@Scope("singleton") - 多例(每次注入都会重新创建一个实例):
@Scope("prototype")
使用方法
@SpringBootTest(classes= TestScope.class)
public class TestScope {
@Bean
@Scope("prototype")
public F f(){
return new F();
}
@Test
public void test(@Autowired F f,@Autowired F ff,@Autowired F fff) {
System.out.println(f);
System.out.println(ff);
System.out.println(fff);
}
}
Bean的创建逻辑
@Scope("prototype") 的 Bean:
👉 容器启动时不会创建
👉 只有在“被真正获取 / 注入”时才创建
👉 每获取一次,创建一个新对象
注:多例会造成内存负担。但有时候一定要用,否则会造成线程不安全
@Conditional及其派生注解:按条件决定某个 Bean / 配置类是否注册到 Spring 容器中
一、@Conditional 注解
1️⃣ 作用
@Conditional 用于按条件决定某个 Bean / 配置类是否注册到 Spring 容器中。
是否生效由一个 Condition 条件判断类 决定。
一句话:条件满足 → Bean 生效;条件不满足 → Bean 不创建。
2️⃣ 使用方法
- 自定义一个类,实现
org.springframework.context.annotation.Condition - 重写
matches()方法,返回true / false - 在
@Bean或@Configuration上使用@Conditional(条件类.class)
3️⃣ 使用场景
- 根据配置文件决定是否启用功能
- 根据类是否存在加载 Bean(自动配置核心机制)
- 根据运行环境(开发 / 生产)
- 组件的可插拔设计
示例1:简单应用
条件类
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
/*true代表生效*/
return true;
}
}
使用 @Conditional
@SpringBootTest(classes = TestConditional.class)
public class TestConditional {
@Bean
//必须指定一个实现了Condition接口的类,由matches方法的返回值决定当前Bean是否生效
@Conditional(MyCondition.class)
public ConditionalService conditionalService() {
return new ConditionalService();
}
@Test
public void test(@Autowired(required = false) ConditionalService conditionalService){
System.out.println(conditionalService);
}
}
示例2:根据引用的依赖来选择性注入(增强插拔性、可维护性)
目录结构:
- conditionalDB
- IDB.java
- MysqlCondition.java
- MysqlDB.java
- OracleCondition.java
- OracleDB.java
- TestDB.java
IDB.java
package com.example.springboot1.conditionalDB;
public interface IDB {
public void connection();
}
MysqlCondition.java
package com.example.springboot1.conditionalDB;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class MysqlCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//是否添加当前数据库依赖
//根据类加载,尝试加载MySQL的一个核心类。如果能加载到,说明MySQL依赖被引入
try {
context.getClassLoader().loadClass("com.mysql.cj.MysqlConnection");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}
MysqlDB.java
package com.example.springboot1.conditionalDB;
import org.springframework.context.annotation.Conditional;
public class MysqlDB implements IDB{
@Override
public void connection() {
System.out.println("连接MySQL数据库");
}
}
OracleCondition.java
package com.example.springboot1.conditionalDB;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class OracleCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//是否添加当前数据库依赖
try {
context.getClassLoader().loadClass("oracle.sql.OracleSQLOutput");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}
OracleDB.java
package com.example.springboot1.conditionalDB;
import org.springframework.context.annotation.Conditional;
public class OracleDB implements IDB{
@Override
public void connection() {
System.out.println("连接Oracle数据库");
}
}
TestDB.java
package com.example.springboot1.conditionalDB;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
@SpringBootTest(classes = TestDB.class)
public class TestDB {
@Bean
@Conditional(MysqlCondition.class)
public IDB MysqlDB(){
return new MysqlDB();
}
@Bean
@Conditional(OracleCondition.class)
public IDB OracleDB(){
return new OracleDB();
}
@Test
public void test(@Autowired IDB idb){
System.out.println(idb);
idb.connection();
}
}
二、Spring Boot 常用 Conditional 派生注解
这些是 Spring Boot 对
@Conditional的封装,用得最多
1️⃣ @ConditionalOnProperty
作用
根据配置项是否存在 / 是否为指定值决定 Bean 是否生效
示例
@Bean
@ConditionalOnProperty(name = "feature.login", havingValue = "true")
public LoginService loginService() {
return new LoginService();
}
feature.login=true
2️⃣ @ConditionalOnMissingBean
作用
当容器中不存在某个 Bean 时才创建
常用于 自动配置允许用户覆盖默认实现
示例
@Bean
@ConditionalOnMissingBean(DataSource.class)
public DataSource defaultDataSource() {
return new HikariDataSource();
}
如果用户自己定义了 DataSource,该 Bean 不会生效。
3️⃣ @ConditionalOnClass
作用
当 classpath 中存在某个类时才生效
典型用于第三方依赖的自动配置。
示例
@Bean
@ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
public MysqlService mysqlService() {
return new MysqlService();
}
三、一句话对比总结
| 注解 | 判断依据 |
|---|---|
@Conditional |
自定义逻辑 |
@ConditionalOnProperty |
配置文件 |
@ConditionalOnMissingBean |
Bean 是否存在 |
@ConditionalOnClass |
类是否存在 |
@PostConstruct 实现初始化回调方法
在 Bean 的所有依赖属性都注入完成之后,执行一段初始化逻辑。
1.什么是初始化回调方法?从Bean的生命周期说起
Bean的生命周期:
- 程序员配置@Component、@Bean...@Autowired等注解,来注册Bean对象
- 加载Spring容器
- 实例化Bean对象
- 解析依赖注入(解析@Autowired @Value等)
- 初始化(调用初始化回调方法,由程序员来配置)。关于初始化回调方法的实现,详见:二十、配置初始化回调方法
- 最终放入
Map<beanName,bean对象> - Spring容器.getBean("beanName")--->Map<beanName,bean对象>
注:从Map中获取Bean对象
- Spring容器关闭bean就会销毁调用销毁回调方法,由程序员来配置
2.初始化回调方法的实现
package com.example.springboot1.lifeCallBack;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.InitializingBean;
public class ChangChangService implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("通过实现接口初始化");
}
@PostConstruct
public void init() throws Exception{
System.out.println("基于注解初始化");
}
public ChangChangService(){
System.out.println("构造方法初始化");
}
}
3.为什么不能把初始化逻辑放在构造方法?
这是实现初始化回调的根本原因。
❌ 构造方法的问题
在构造方法执行时:
@Autowired的 Bean 还没注入@Value的配置还没赋值- 可能为
null或默认值
4.了解更多详见二十、配置初始化回调方法
@PreDestroy实现销毁回调方法
1.销毁回调方法的实现
public class ChangChangService implements DisposableBean {
public ChangChangService(){
System.out.println("构造方法初始化");
}
@Override
public void destroy() throws Exception {
System.out.println("接口:销毁destroy");
}
@PreDestroy
public void destroy2(){
System.out.println("接口:销毁destroy");
}
}
2.了解更多详见二十一、配置销毁回调方法
@ComponentScan 让Spring容器扫描指定包
- 作用:告诉 Spring 容器去扫描指定包及其子包,把带有
@Component、@Service、@Repository、@Controller等注解的类自动注册为 Bean。 - 默认行为:如果不指定包路径,默认扫描 注解所在类的包及其子包。
- 使用场景:当你的组件不在启动类所在包下,或者你想自定义扫描范围时,需要显式使用。
示例:
// 服务类
package org.example.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void addUser(String name) {
System.out.println("添加用户:" + name);
}
}
// 主配置类
package org.example;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("org.example.service") // 指定扫描包
public class AppConfig {
}
// 测试类
package org.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = context.getBean(UserService.class);
userService.addUser("张三");
}
}
输出:
添加用户:张三
✅ 总结:
-
@ComponentScan决定 Spring 扫描哪些包来注册 Bean。 -
在 Spring Boot 中,启动类
@SpringBootApplication已经隐含了@ComponentScan,不必重复。
AOP编程:面向切口编程
连接点参数类型:JoinPoint。关于连接点参数更多,详见:连接点(Join point)
@Aspect 注解
- 作用:标记一个类为 切面类(Aspect),告诉 Spring 这是一个包含横切逻辑(如日志、事务、安全等)的类。
- 特点:
- 必须配合 Spring 容器管理(通常加上
@Component)。 - 内部可以定义通知(Advice)方法,通知可以在目标方法执行前、后、异常时执行。
- 必须配合 Spring 容器管理(通常加上
@Aspect
@Component
public class LogAspect {
// 切面逻辑放在这里
}
@EnableAspectJAutoProxy 启用AOP功能
没有这个注解AOP功能无法使用
SpringBoot会帮助我们自动配置该注解,(在@SpringBootApplication注解内)
但是依然建议加上该注解,一目了然
@Around 环绕通知(前置+后置+异常+返回值)
- 作用:环绕通知(Around Advice),可在 方法执行前后 做处理,还可以决定是否执行目标方法。
- 语法:
@Around("切点表达式")
public Object 方法名(ProceedingJoinPoint joinPoint) { ... }
示例:
@Aspect // 标记为切面类
@Component
public class LogAspect {
@Around("execution(* org.example.c4_aop.UserService.*(..))")
public void log(ProceedingJoinPoint proceedingJoinPoint){
//前置通知
//...
try {
//前置通知
long begin = System.currentTimeMillis();
//执行方法并获取返回值
Object proceed = proceedingJoinPoint.proceed(); // 执行目标方法
} catch (Throwable e) {
//异常通知
System.out.println("方法执行异常"+e.getMessage());
}finally{
//后置通知
long end = System.currentTimeMillis();
}
//后置通知
System.out.println("方法用时:" + (end - begin) + "ms");
}
}
切点表达式解析
@Around("execution(* org.example.c4_aop.UserService.*(..))")
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)- 分解:
*:匹配任意返回类型org.example.c4_aop.UserService.*:匹配UserService类的所有方法(..):匹配任意数量、任意类型的参数
⚡ 小结:该表达式表示 对 UserService 类的所有方法进行环绕通知。
@Before 前置通知,目标方法之前执行
@Aspect // 标记为切面类
@Component
public class LogAspect {
@Before("execution(* org.example.c4_aop.advice.UserService.*(..))")
public void before(JoinPoint joinPoint) {
//记录当前方法名和参数
String name = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("方法名:" + name + " 参数:" + Arrays.toString(args));
}
}
@After 后置通知,目标方法之后执行
@Aspect // 标记为切面类
@Component
public class LogAspect {
// 后置通知
@After("execution(* org.example.c4_aop.advice.UserService.*(..))")
public void before(JoinPoint joinPoint) {
//记录当前方法名和参数
String name = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("后置通知:方法名:" + name + " 参数:" + Arrays.toString(args));
}
}
即使目标方法出现异常仍会触发
@AfterThrowing 异常通知,目标方法出现了异常时执行
@Aspect // 标记为切面类
@Component
public class LogAspect {
//异常通知
@AfterThrowing(value = "execution(* org.example.c4_aop.advice.UserService.*(..))",throwing = "exception")
public void afterThrowing(JoinPoint joinPoint,Exception exception) {
System.out.println("异常通知,异常信息:"+exception.getMessage());
}
}
@AfterReturning 返回通知,目标方法返回值执行
@Aspect // 标记为切面类
@Component
public class LogAspect {
//返回通知
@AfterReturning(value = "execution(* org.example.c4_aop.advice.UserService.*(..))",returning = "returnValue")
public void afterReturn(JoinPoint joinPoint, Object returnValue) {
System.out.println("返回通知,返回值:"+returnValue);
}
}
@Pointcut 抽取切点表达式
切点表达式语法详见10.Spring AOP 切点表达式语法(从基础到进阶)
@Aspect // 标记为切面类
@Component
public class LogAspect {
//抽取切点表达式
@Pointcut("execution(* org.example.c4_aop.advice.UserService.*(..))")
public void pointcut(){}
@Before("pointcut()")
public void before(JoinPoint joinPoint){
System.out.println("前置通知");
}
}
Web网络传输注解
@RequestBody:
@RequestBody 用来把「请求体里的 JSON 数据」转换成 Java 对象,并绑定到方法参数上。
示例:
@PostMapping("/user")
public String addUser(@RequestBody JsonNode jsonNode) {
String username = jsonNode.get("username").asText();
int age = jsonNode.get("age").asInt();
return "ok";
}
@RestController:
@RestController = @Controller + @ResponseBody
标记接口(非注解)
什么是标记接口?
形如
public interface Serializable {
}
✔ 没方法
✔ 没代码
✔ 只是一个“资格证”
👉 JVM 看到你 实现了它,就允许你被序列化
👉 没实现 → 直接抛异常
1.Serializable接口:让 Java 对象可以被“序列化”,也就是可以被转换成字节流,从而进行传输、存储或缓存。
什么是序列化?
序列化之前:
Result r = new Result();
👉 这是一个只存在于 JVM 内存中的对象
👉 JVM 一关,啥都没了
👉 不能:
- 存文件
- 传网络
- 放缓存
实现 Serializable 之后
public class Result implements Serializable
这个对象就可以被:
- 写到磁盘(文件)
- 通过网络发送
- 存到 Redis
- 存到 Session
- JVM 之间传输
package com.example.demo1.common;
import lombok.Data;
import java.io.Serializable;
/**
* 返回统一结果类
*/
@Data
public class Result implements Serializable {
private static final long serialVersionUID = 1L;
// 状态码
private int code;
// 提示信息message
private String msg;
// 返回的属性类型
private Object data;
/**
* 直接返回成功结果
* @param data
* @return
*/
public static Result success(Object data) {
return success(200, "操作成功", data);
}
/**
* 自定义返回成功结果
* @param code
* @param msg
* @param data
* @return
*/
public static Result success(int code, String msg, Object data) {
Result r = new Result();
r.setCode(code);
r.setMsg(msg);
r.setData(data);
return r;
}
/**
* 不带结果直接返回成功。适合不需要向前端传值的时候使用
* @return
*/
public static Result success() {
Result r = new Result();
r.setCode(200);
r.setMsg("操作成功");
return r;
}
/**
* 直接返回失败信息
* @return
*/
public static Result error() {
return error(400, "操作失败", null);
}
/**
* 带参数返回失败信息
* @param msg
* @return
*/
public static Result error(String msg) {
return error(400, msg, null);
}
/**
* 自定义返回失败信息
* @param code
* @param msg
* @param data
* @return
*/
public static Result error(int code, String msg, Object data) {
Result r = new Result();
r.setCode(code);
r.setMsg(msg);
r.setData(data);
return r;
}
}
serialVersionUID 是干嘛的?
private static final long serialVersionUID = 1L;
用于保证“反序列化时版本一致”
Lombok常用注解
Lombok 是一个 Java 编译时工具,通过注解生成 getter、setter、构造方法、toString、equals/hashCode 等样板代码,从而减少样板代码编写量。
@Data 注解
- 作用:为类自动生成 Getter、Setter、toString、equals、hashCode 方法,同时生成 全参构造方法。
- 特点:
- 自动生成的 Getter/Setter 是 public
- 适合普通的 POJO / DTO
- 依赖
@ToString、@EqualsAndHashCode等组合注解
@Data
public class User {
private String username;
private int age;
}
@Getter / @Setter 注解
- 作用:
@Getter:为类的所有字段生成 getter 方法@Setter:为类的所有字段生成 setter 方法
- 特点:
- 可以单独用在字段上生成对应方法
- 支持
AccessLevel控制生成方法的访问权限
@Getter
@Setter
public class User {
private String username;
private int age;
}
@NoArgsConstructor 注解
- 作用:生成 无参构造方法
- 特点:
- 对有 final 字段或非空字段,需要配合
@NoArgsConstructor(force = true)才能生成 - 常用于框架反射初始化(如 Jackson、MyBatis)
- 对有 final 字段或非空字段,需要配合
@NoArgsConstructor
public class User {
private String username;
private int age;
}
@AllArgsConstructor 注解
- 作用:生成 全参构造方法(所有字段作为参数)
- 特点:
- 可以快速初始化对象
- 支持
@Builder配合使用,便于链式构造
@AllArgsConstructor
public class User {
private String username;
private int age;
}
@RequiredArgsConstructor 注解
- 作用:为 final 字段 和 @NonNull 字段 生成构造方法
- 特点:
- 避免手动写大量构造方法
- 非 final / 非 @NonNull 字段不会包含在构造方法参数中
@RequiredArgsConstructor
public class User {
@NonNull
private String username;
private int age;
}
@Builder 注解
- 作用:为类提供 构建者模式(Builder Pattern) 的支持
- 特点:
- 可以链式设置字段
- 与 @AllArgsConstructor 配合使用效果最佳
- 常用于 DTO、复杂对象初始化
@Builder
public class User {
private String username;
private int age;
}
// 使用示例
User user = User.builder()
.username("zhangsan")
.age(18)
.build();
@ToString 注解
- 作用:自动生成 toString 方法
- 特点:
- 默认输出所有字段
- 可以通过
exclude排除敏感字段(如密码) - 可选择是否调用父类 toString 方法
@ToString(exclude = "password")
public class User {
private String username;
private String password;
}
@EqualsAndHashCode 注解
- 作用:自动生成 equals 和 hashCode 方法
- 特点:
- 默认比较所有字段
- 可以排除某些字段
- 支持调用父类方法
@EqualsAndHashCode(exclude = "id")
public class User {
private String id;
private String username;
}
@NonNull 注解
- 作用:标记字段或方法参数 不能为 null
- 特点:
- Lombok 会在构造方法或 setter 中自动生成 null 检查
- 可防止空指针异常(NPE)
public class User {
@NonNull
private String username;
}
@Slf4j 注解
- 作用:自动为类生成 log 日志对象
- 特点:
- 无需手动写
private static final Logger log = LoggerFactory.getLogger(...) - 支持各种日志级别(info/debug/error)
- 无需手动写
@Slf4j
public class UserService {
public void doSomething() {
log.info("执行操作");
}
}
事务
@Transactional 声明式事务
-
标注位置
-
类上:类中所有
public方法默认有事务 -
方法上:单独方法启用事务(覆盖类级别设置)
-
-
常用属性
-
propagation:事务传播行为(常用REQUIRED) -
isolation:隔离级别(常用DEFAULT或REPEATABLE_READ) -
rollbackFor:指定回滚的异常类型(默认只回滚RuntimeException) -
readOnly:只读事务(查询优化)
-
-
注意事项
-
只对
public方法有效 -
内部自调用不会触发事务
-
默认只对运行时异常回滚
-
数据库必须支持事务
-
配置路由@RequestMapping
package com.example.demo1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class Demo1Application {
public static void main(String[] args) {
SpringApplication.run(Demo1Application.class, args);
}
@RequestMapping("/hello")
public String hello(){
return "Hello World";
}
}
此时可以通过localhost:8080/hello来访问该函数
同样的,也可以给一个类配置路由
package com.example.demo1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
@RequestMapping("/api")
public class Demo1Application {
public static void main(String[] args) {
SpringApplication.run(Demo1Application.class, args);
}
@RequestMapping("/hello")
public String hello(){
return "Hello World";
}
}
此时,用以访问该函数的地址变为了localhost:8080/api/hello
-
@GetMapping:处理GET请求
用法1 自动判断
该注解可以根据请求类型自动匹配
@GetMapping
public String getRequest(){
return "GET请求已被正常处理";
}
// 函数的访问地址为localhost:8080/
用法2 设置路由
该注解也可以用来设置路由
@GetMapping("/get")
public String getRequest(){
return "GET请求已被正常处理";
}
// 函数的访问地址为localhost:8080/get
用法3 以restful的形式通过url来传递参数
@GetMapping("/{id}")
public String getRequest(@PathVariable Long id){
System.out.println(id);
return "后端接收到了:"+id;
}
用法4 以传统url字符的方式传递参数
// 传统url符号传值就不需要配置路由的变量了
@GetMapping
public String normoalURL(@RequestParam Long id,@RequestParam String name){
return "传统url符号传参以触发id:"+id+"\tname:"+name;
}
// localhost:8080/?id=1&name=abc
-
@PostMapping:处理POST请求
@PostMapping
public String save(@RequestBody Map<String,String> map){
System.out.println(map.toString());
return "POST请求接收成功";
}
-
@PutMapping:处理PUT请求
@PutMapping("/{id}")
public String update(@PathVariable Long id,@RequestBody Map<String,String> map){
// 使用%s打印是因为通用性更强
System.out.printf("ID=%s,name=%s",id,map.toString());
return "PUT请求成功";
}
-
@DeleteMapping:处理delete请求
@DeleteMapping("/{id}")
public String delete(@PathVariable Long id){
System.out.printf("ID=%s\n",id);
return "DELETE请求接收成功";
}
-
@PathVariable:Restful形式接收参数的注解 -
@RequestParam:传统url符号形式接收参数的注解
<!-- 可以设置默认值 -->
@RequestParam(defaultValue = "xxx")
-
@RequestBody:通过POST方法接收参数的注解 -
@ComponentScan:当没有设置basepackages时,默认会将当前注解所在类的包当做是扫描包

浙公网安备 33010602011771号