Lesson 59 · Spring 生态核心原理

Spring 核心注解速查:@Autowired vs @Resource、@Component vs @Bean

初级·🔥 极高·#Spring·#注解

第 1 站

面试官:@Autowired 和 @Resource 有什么区别?

"都是注入 Bean,@Autowired、@Resource、@Inject 到底有什么不同?什么场景该用哪个?面试时被追问 byType 和 byName 你怎么答?" —— 注解选择看似简单,背后涉及 Spring 的依赖解析策略。

Spring 支持三种依赖注入注解,分别来自不同的规范:

注解来源默认策略是否必须 Spring
@AutowiredSpring 框架byType(按类型)
@ResourceJSR-250(javax.annotation)byName(按名称)否(JDK 自带)
@InjectJSR-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 名 → 匹配
@Autowired vs @Resource 匹配策略 @Autowired (byType) ① 按类型查找所有候选 Bean ② 如果多个 → @Qualifier 指定 ③ 如果多个且无 Qualifier → @Primary ④ 兜底:变量名 == Bean 名 required=false → 找不到也不报错 @Resource (byName) ① 指定 name → 直接按名称查找 ② 未指定 name → 用字段名查找 ③ 按名称找不到 → 降级为 byType ④ 可用 type 属性限定类型 无 required 属性,找不到直接报错
图 1 @Autowired 按类型、@Resource 按名称的匹配策略对比
推荐用法
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
    }
}
@Component 家族:语义递进 @Component @Service @Repository @Controller 业务层 持久层 + 异常转换 Web 控制层
图 2 @Component 是基础,@Service / @Repository / @Controller 是语义化特化
@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();
    }
}
Bean 生命周期关键节点 实例化 属性填充 Aware 接口 BeanNameAware... @PostConstruct 初始化回调 就绪 @PreDestroy 销毁 实例化 → 属性填充 → Aware → BeanPostProcessor → @PostConstruct → 就绪 → @PreDestroy → 销毁
图 3 Bean 生命周期的关键回调节点
第 5 站

Web 层注解:@RequestMapping 家族

注解作用示例
@RequestMapping通用映射(类 / 方法)@RequestMapping("/api")
@GetMappingGET 请求@GetMapping("/users/{id}")
@PostMappingPOST 请求@PostMapping("/users")
@PutMappingPUT 请求@PutMapping("/users/{id}")
@DeleteMappingDELETE 请求@DeleteMapping("/users/{id}")
@PathVariableURL 路径变量@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 核心注解速查表

分类注解核心要点
依赖注入@AutowiredSpring 原生,byType,支持 required=false、@Qualifier
@ResourceJSR-250 标准,byName,可指定 name 属性
@InjectJSR-330 标准,byType,无 required,需引入 javax.inject
Bean 声明@Component通用组件,自动扫描注册
@Service / @Repository / @Controller@Component 的语义化特化,分别对应业务层/持久层/控制层
@Bean方法级别,用于第三方库或复杂初始化
@Configuration标记配置类,配合 @Bean 使用
配置读取@Value单个属性注入,支持 SpEL 和默认值
@ConfigurationProperties批量属性绑定到 POJO,类型安全
@PropertySource加载自定义 .properties 文件
生命周期@Scopesingleton / prototype / request / session
@Lazy延迟初始化,首次使用时创建
@PostConstruct / @PreDestroy初始化回调 / 销毁回调
Web 层@RequestMapping / @GetMapping ...URL 映射,类级别为前缀,方法级别为完整路径
@PathVariableURL 路径变量 /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 额外提供持久层异常转换