Lesson 59 · Spring 生态核心原理
Spring 核心注解速查:@Autowired vs @Resource、@Component vs @Bean
第 1 站
面试官:@Autowired 和 @Resource 有什么区别?
"都是注入 Bean,@Autowired、@Resource、@Inject 到底有什么不同?什么场景该用哪个?面试时被追问 byType 和 byName 你怎么答?" —— 注解选择看似简单,背后涉及 Spring 的依赖解析策略。
Spring 支持三种依赖注入注解,分别来自不同的规范:
| 注解 | 来源 | 默认策略 | 是否必须 Spring |
|---|---|---|---|
@Autowired | Spring 框架 | byType(按类型) | 是 |
@Resource | JSR-250(javax.annotation) | byName(按名称) | 否(JDK 自带) |
@Inject | JSR-330(javax.inject) | byType(按类型) | 否(需引包) |
三种注入注解用法对比
@Service
public class OrderService {
// ① @Autowired:按类型匹配,多个实现时用 @Qualifier 指定
@Autowired
@Qualifier("mysqlUserDao")
private UserDao userDao;
// ② @Resource:默认按字段名匹配,可指定 name
@Resource(name = "mysqlUserDao")
private UserDao userDao2;
// ③ @Inject:按类型匹配,无 required 属性
@Inject
@Named("mysqlUserDao")
private UserDao userDao3;
}
@Autowired 的 byType 匹配规则:当存在多个同类型 Bean 时,按以下优先级解析:
@Autowired 多实现解析顺序
// 假设 UserDao 有两个实现:MysqlUserDao 和 RedisUserDao
// 方式 1:@Qualifier 指定 Bean 名称
@Autowired
@Qualifier("mysqlUserDao")
private UserDao userDao;
// 方式 2:@Primary 标记优先(在配置类或实现类上)
@Primary
@Component
public class MysqlUserDao implements UserDao { ... }
// 方式 3:变量名自动匹配(最后兜底)
@Autowired
private UserDao mysqlUserDao; // 变量名 = Bean 名 → 匹配
推荐用法
Spring 项目中推荐用 @Autowired(Spring 原生,支持 required、@Qualifier、@Primary 等丰富特性)。如果团队要求与 Spring 解耦,可用 @Resource(JSR-250 标准,JDK 自带)。第 2 站
@Component vs @Bean vs @Service vs @Repository
声明一个 Bean 有两种基本方式:类级别注解扫描 和 方法级别显式声明。
| 注解 | 位置 | 声明方式 | 使用场景 |
|---|---|---|---|
@Component | 类上 | 自动扫描 | 通用组件,无特殊语义 |
@Service | 类上 | 自动扫描 | 业务逻辑层(@Component 的特化) |
@Repository | 类上 | 自动扫描 | 数据访问层 + 异常转换 |
@Controller | 类上 | 自动扫描 | Web 控制器层 |
@Bean | 方法上 | 显式声明 | 第三方库、复杂初始化逻辑 |
@Component 扫描 vs @Bean 声明
// 方式一:@Component 自动扫描
@Service // 等价于 @Component + "业务层"语义
public class UserService {
@Autowired
private UserDao userDao;
}
// 方式二:@Bean 显式声明(在 @Configuration 类中)
@Configuration
public class AppConfig {
@Bean
public DataSource dataSource() {
// 可以写复杂的初始化逻辑
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:mysql://localhost/mydb");
ds.setMaximumPoolSize(20);
return ds;
}
@Bean
public UserDao userDao(DataSource dataSource) {
return new UserDaoImpl(dataSource); // 方法参数 = 其他 Bean
}
}
@Repository 的隐藏功能
除了标记为 Bean,@Repository 还会自动启用 持久层异常转换(PersistenceExceptionTranslationPostProcessor),将底层的数据访问异常(如 SQLException)转为 Spring 统一的 DataAccessException 体系。何时用 @Bean
自己写的类优先用 @Component / @Service / @Repository;第三方库的类(如 DataSource、RestTemplate)无法加注解,必须用 @Bean 在配置类中显式声明。第 3 站
@Value vs @ConfigurationProperties vs @PropertySource
Spring 提供了多种从配置文件读取属性的方式:
| 方式 | 注入粒度 | SpEL 支持 | 类型安全 | 适用场景 |
|---|---|---|---|---|
@Value | 单个属性 | ✅ | ❌ | 少量简单配置 |
@ConfigurationProperties | 整组属性 | ❌ | ✅ 绑定到 POJO | 批量配置、复杂结构 |
@PropertySource | 加载外部文件 | — | — | 加载自定义 properties 文件 |
@Value 用法
@Component
public class AppConfig {
// 基本用法:注入单个属性
@Value("${app.name}")
private String appName;
// 默认值:找不到时使用默认
@Value("${app.port:8080}")
private int port;
// SpEL 表达式
@Value("#{systemProperties['user.name']}")
private String systemUser;
}
@ConfigurationProperties 用法 · 推荐
// application.yml
// app:
// name: my-service
// port: 8080
// features:
// - logging
// - metrics
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private int port;
private List<String> features;
// 必须有 getter / setter(Spring 通过 setter 绑定)
public String getName() { return name; }
public void setName(String name) { this.name = name; }
// ... 其他 getter/setter
}
@PropertySource · 加载自定义配置文件
@Configuration
@PropertySource("classpath:custom.properties")
public class CustomConfig {
@Value("${custom.key}")
private String customValue;
}
配置注入选型
1-2 个简单属性用 @Value;一组相关配置用 @ConfigurationProperties(类型安全、支持嵌套、IDE 提示)。@PropertySource 用于加载非默认的 properties 文件(注意:不支持 YAML)。第 4 站
@Scope、@Lazy、@DependsOn:控制 Bean 的作用域与生命周期
| 注解 | 作用 | 常用值 |
|---|---|---|
@Scope | 定义 Bean 的作用域 | singleton / prototype / request / session |
@Lazy | 延迟初始化 | 首次使用时才创建 Bean |
@DependsOn | 声明初始化顺序依赖 | 确保某些 Bean 先初始化 |
@PostConstruct | 初始化回调 | Bean 创建完成后执行 |
@PreDestroy | 销毁回调 | Bean 销毁前执行 |
@Scope 示例 · prototype 多例模式
@Component
@Scope("prototype") // 每次注入都创建新实例
public class OrderProcessor {
// 有状态的处理器,不能用单例
private List<OrderItem> items = new ArrayList<>();
}
@Lazy + @DependsOn
@Component
@Lazy // 启动时不创建,第一次使用时才初始化
public class HeavyReportService {
// 初始化耗时,延迟加载优化启动速度
}
@Component
@DependsOn("cacheManager") // 确保 cacheManager 先初始化
public class DataService {
// 启动时需要预热缓存
}
@PostConstruct / @PreDestroy · 生命周期回调
@Service
public class CacheService {
@PostConstruct
public void init() {
// Bean 创建完成后自动调用:预热缓存
loadInitialData();
}
@PreDestroy
public void cleanup() {
// 容器关闭时调用:释放资源
clearCache();
}
}
第 5 站
Web 层注解:@RequestMapping 家族
| 注解 | 作用 | 示例 |
|---|---|---|
@RequestMapping | 通用映射(类 / 方法) | @RequestMapping("/api") |
@GetMapping | GET 请求 | @GetMapping("/users/{id}") |
@PostMapping | POST 请求 | @PostMapping("/users") |
@PutMapping | PUT 请求 | @PutMapping("/users/{id}") |
@DeleteMapping | DELETE 请求 | @DeleteMapping("/users/{id}") |
@PathVariable | URL 路径变量 | @PathVariable Long id |
@RequestParam | 查询参数 / 表单参数 | @RequestParam String name |
@RequestBody | 请求体(JSON 反序列化) | @RequestBody User user |
@RequestHeader | 请求头 | @RequestHeader("Authorization") |
@ResponseBody | 返回值写入响应体 | 自动 JSON 序列化 |
完整 Controller 示例
@RestController
@RequestMapping("/api/users")
public class UserController {
// GET /api/users?page=1&size=10
@GetMapping
public Page<User> list(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
return userService.findByPage(page, size);
}
// GET /api/users/42
@GetMapping("/{id}")
public User getById(
@PathVariable Long id,
@RequestHeader("Accept") String accept) {
return userService.findById(id);
}
// POST /api/users body: {"name":"Alice","email":"..."}
@PostMapping
public User create(@RequestBody @Valid CreateUserRequest req) {
return userService.create(req);
}
// DELETE /api/users/42
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) {
userService.delete(id);
}
}
@RequestParam vs @PathVariable
@RequestParam 用于查询参数(?key=value)和表单数据;@PathVariable 用于 URL 路径中的变量(/users/{id})。语义上:PathVariable 标识资源,RequestParam 过滤/分页资源。第 6 站
总结:Spring 核心注解速查表
| 分类 | 注解 | 核心要点 |
|---|---|---|
| 依赖注入 | @Autowired | Spring 原生,byType,支持 required=false、@Qualifier |
| @Resource | JSR-250 标准,byName,可指定 name 属性 | |
| @Inject | JSR-330 标准,byType,无 required,需引入 javax.inject | |
| Bean 声明 | @Component | 通用组件,自动扫描注册 |
| @Service / @Repository / @Controller | @Component 的语义化特化,分别对应业务层/持久层/控制层 | |
| @Bean | 方法级别,用于第三方库或复杂初始化 | |
| @Configuration | 标记配置类,配合 @Bean 使用 | |
| 配置读取 | @Value | 单个属性注入,支持 SpEL 和默认值 |
| @ConfigurationProperties | 批量属性绑定到 POJO,类型安全 | |
| @PropertySource | 加载自定义 .properties 文件 | |
| 生命周期 | @Scope | singleton / prototype / request / session |
| @Lazy | 延迟初始化,首次使用时创建 | |
| @PostConstruct / @PreDestroy | 初始化回调 / 销毁回调 | |
| Web 层 | @RequestMapping / @GetMapping ... | URL 映射,类级别为前缀,方法级别为完整路径 |
| @PathVariable | URL 路径变量 /users/{id} | |
| @RequestParam | 查询参数 ?key=value | |
| @RequestBody | 请求体 JSON 反序列化 |
面试回答模板
- @Autowired vs @Resource:@Autowired 是 Spring 的,默认 byType,多实现用 @Qualifier;@Resource 是 JSR-250 标准,默认 byName。推荐 Spring 项目用 @Autowired
- @Component vs @Bean:@Component 是类级别自动扫描;@Bean 是方法级别显式声明,用于第三方类或需要复杂初始化的场景
- @Value vs @ConfigurationProperties:@Value 注入单个属性,支持 SpEL;@ConfigurationProperties 绑定一组属性到 POJO,类型安全、支持嵌套结构
- @Service/@Repository/@Controller:都是 @Component 的特化注解,语义不同。@Repository 额外提供持久层异常转换