【Day39】Spring 核心注解:@Component、@Autowired、@Configuration 等

本文收录于「Java 学习日记」专栏,聚焦 Spring 框架最核心的注解体系,从注解分类、使用场景到实战避坑,帮你彻底掌握 Spring 注解开发的精髓,告别繁琐的 XML 配置~

一、为什么要学 Spring 核心注解?

在上一篇 IOC/DI 的学习中,我们已经体验到注解开发的便捷性 ------ 用几行注解就能替代大量 XML 配置。但 Spring 的注解体系庞大,不同注解有不同的职责和使用场景:

  • 有的注解用于定义 Bean (如@Component),有的用于注入依赖 (如@Autowired);
  • 有的注解用于配置类 (如@Configuration),有的用于限定作用域 (如@Scope);
  • 新手容易混淆注解的用法(比如@Autowired@Resource的区别),导致项目启动失败或依赖注入异常。

今天这篇日记,我们将 Spring 核心注解按 "功能分类" 拆解,结合实战案例讲解每个注解的使用场景、核心原理和避坑要点,让你对 Spring 注解体系形成清晰的认知。

二、Spring 核心注解分类与实战

Spring 核心注解可分为 6 大类,覆盖 Bean 定义、依赖注入、配置管理、作用域控制等核心场景,我们逐一讲解:

第一类:Bean 定义注解(将对象交给 Spring 容器管理)

核心作用:替代 XML 中的<bean>标签,告诉 Spring 容器 "这个类需要被管理"。

注解 作用 适用场景
@Component 通用 Bean 定义注解 所有层的 Bean(通用,无明确分层)
@Controller 标识 Controller 层 Bean(Web 场景) MVC 中的控制器层(接收请求)
@Service 标识 Service 层 Bean 业务逻辑层
@Repository 标识 Repository/DAO 层 Bean 数据访问层(与数据库交互)
  1. 基础使用示例

java

运行

java 复制代码
// 1. DAO层(@Repository)
@Repository // 等价于<bean id="userDao" class="com.example.dao.UserDaoImpl"/>
public class UserDaoImpl implements UserDao {
    @Override
    public void query() {
        System.out.println("DAO层:查询用户数据");
    }
}

// 2. Service层(@Service)
@Service("userService") // 指定Bean id为userService(默认首字母小写userServiceImpl)
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    @Override
    public void business() {
        userDao.query();
        System.out.println("Service层:处理业务逻辑");
    }
}

// 3. Controller层(@Controller)
@Controller // Bean id默认userController
public class UserController {
    @Autowired
    private UserService userService;

    public void handleRequest() {
        userService.business();
        System.out.println("Controller层:处理请求");
    }
}

// 4. 通用组件(@Component)
@Component // 非MVC分层的通用组件(如工具类)
public class CommonUtils {
    public void print() {
        System.out.println("通用组件:Spring管理的工具类");
    }
}
  1. 核心细节
  • @Controller/@Service/@Repository本质是@Component的 "别名",仅用于语义化区分分层;
  • 不指定value时,Bean id 默认是类名首字母小写 (如UserServiceImpluserServiceImpl);
  • 必须配合@ComponentScan(或 XML 的<context:component-scan>)扫描注解所在包,否则注解无效。

第二类:依赖注入注解(自动注入 Bean)

核心作用:替代 XML 中的<property>/<constructor-arg>,自动将依赖的 Bean 注入到目标对象中。

注解 作用 核心特点
@Autowired 自动注入(Spring 注解) 类型匹配,支持 required 属性
@Resource 自动注入(JDK 注解) 名称匹配(默认),可指定 name
@Qualifier 限定 Bean 名称 配合@Autowired解决同类型多 Bean 问题
@Value 注入基本类型 / 配置文件属性 @Value("${jdbc.url}")
  1. @Autowired(最常用)

java

运行

java 复制代码
@Service
public class UserServiceImpl implements UserService {
    // 方式1:字段注入(最简洁,推荐)
    @Autowired(required = false) // required=false:找不到Bean不报错(默认true)
    private UserDao userDao;

    // 方式2:setter注入(兼容老版本)
    // @Autowired
    // public void setUserDao(UserDao userDao) {
    //     this.userDao = userDao;
    // }

    // 方式3:构造器注入(推荐用于必须依赖)
    // @Autowired
    // public UserServiceImpl(UserDao userDao) {
    //     this.userDao = userDao;
    // }
}
  1. @Autowired + @Qualifier(解决同类型多 Bean)

当同类型有多个 Bean 时,@Autowired按类型匹配会报错,需用@Qualifier指定 Bean 名称:

java

运行

java 复制代码
// 两个同类型的DAO Bean
@Repository("userDaoImpl1")
public class UserDaoImpl1 implements UserDao { /* ... */ }

@Repository("userDaoImpl2")
public class UserDaoImpl2 implements UserDao { /* ... */ }

// 注入指定名称的Bean
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    @Qualifier("userDaoImpl1") // 指定注入userDaoImpl1
    private UserDao userDao;
}
  1. @Resource(JDK 注解)

java

运行

java 复制代码
@Service
public class UserServiceImpl implements UserService {
    // 方式1:按名称注入(name=Bean id)
    @Resource(name = "userDaoImpl2")
    private UserDao userDao;

    // 方式2:默认按名称(字段名),找不到则按类型
    // @Resource
    // private UserDao userDaoImpl1; // 匹配id=userDaoImpl1的Bean
}
  1. @Value(注入基本类型 / 配置属性)

java

运行

java 复制代码
@Service
public class UserServiceImpl implements UserService {
    // 注入基本类型
    @Value("18")
    private Integer defaultAge;

    // 注入配置文件属性(需先加载配置文件)
    @Value("${app.name}") // 读取application.properties中的app.name
    private String appName;
}

加载配置文件(在配置类上添加):

java

运行

java 复制代码
@Configuration
@PropertySource("classpath:application.properties") // 加载配置文件
public class SpringConfig { /* ... */ }

第三类:配置类注解(替代 XML 配置文件)

核心作用:用 Java 类替代传统的applicationContext.xml,实现 "零 XML 配置"。

注解 作用 示例
@Configuration 标记配置类(等价于 XML 配置文件) @Configuration public class SpringConfig {}
@ComponentScan 扫描指定包下的注解 @ComponentScan("com.example")
@Bean 手动注册 Bean(替代 XML 的<bean> @Bean public UserDao userDao() {}
@Import 导入其他配置类 @Import(DataSourceConfig.class)
  1. 核心示例:纯 Java 配置

java

运行

java 复制代码
// 1. 配置类(替代applicationContext.xml)
@Configuration // 标记为配置类
@ComponentScan("com.example") // 扫描注解(等价于<context:component-scan>)
@PropertySource("classpath:application.properties") // 加载配置文件
public class SpringConfig {

    // 手动注册Bean(替代XML的<bean>,适用于第三方类)
    @Bean("userDao") // Bean id默认方法名(userDao)
    public UserDao userDao() {
        return new UserDaoImpl(); // 手动创建对象,交给Spring管理
    }

    // 依赖注入:方法参数自动注入容器中的Bean
    @Bean
    public UserService userService(UserDao userDao) {
        UserServiceImpl service = new UserServiceImpl();
        service.setUserDao(userDao);
        return service;
    }
}

// 2. 测试:加载配置类创建容器
public class ConfigTest {
    public static void main(String[] args) {
        // 加载Java配置类,创建IOC容器
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserController controller = context.getBean(UserController.class);
        controller.handleRequest();
    }
}
  1. 核心细节
  • @Configuration修饰的类会被 Spring 代理,@Bean方法调用时会从容器获取 Bean(而非新建);
  • @Bean方法的参数会自动从容器中注入(无需@Autowired);
  • 纯 Java 配置是 Spring Boot 的核心方式,彻底替代 XML。

第四类:作用域与生命周期注解

核心作用:控制 Bean 的作用域和生命周期(初始化 / 销毁)。

注解 作用 示例
@Scope 指定 Bean 作用域 @Scope("prototype")
@PostConstruct Bean 初始化后执行(替代 init-method) 方法上标注
@PreDestroy Bean 销毁前执行(替代 destroy-method) 方法上标注

示例:作用域 + 生命周期

java

运行

java 复制代码
@Service
@Scope("singleton") // 单例(默认),可选prototype/request/session
public class UserServiceImpl implements UserService {

    // 初始化方法:Bean创建后执行(仅单例生效)
    @PostConstruct
    public void init() {
        System.out.println("UserServiceImpl初始化完成");
    }

    // 销毁方法:容器关闭时执行(仅单例生效)
    @PreDestroy
    public void destroy() {
        System.out.println("UserServiceImpl销毁");
    }
}

第五类:条件注解(按需创建 Bean)

核心作用:根据条件动态创建 Bean(Spring Boot 自动配置的核心)。

注解 作用 示例
@Conditional 自定义条件 @Conditional(MyCondition.class)
@ConditionalOnClass 存在指定类时创建 Bean @ConditionalOnClass(RedisTemplate.class)
@ConditionalOnMissingBean 不存在指定 Bean 时创建 @ConditionalOnMissingBean(UserDao.class)

示例:自定义条件

java

运行

java 复制代码
// 1. 自定义条件类
public class MyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 自定义条件:存在"enable.userService"配置且值为true时创建Bean
        String enable = context.getEnvironment().getProperty("enable.userService");
        return "true".equals(enable);
    }
}

// 2. 应用条件注解
@Service
@Conditional(MyCondition.class) // 满足条件才创建该Bean
public class UserServiceImpl implements UserService { /* ... */ }

第六类:AOP 相关注解(基础)

核心作用:实现面向切面编程(后续专题讲解,这里仅列基础注解)。

注解 作用 示例
@Aspect 标记切面类 @Aspect @Component public class LogAspect {}
@Before 前置通知 @Before("execution(* com.example.service.*.*(..))")
@After 后置通知 同上
@Around 环绕通知 同上

三、注解开发完整实战案例

1. 项目结构

plaintext

复制代码
src/main/
├── java/
│   └── com/example/
│       ├── config/
│       │   └── SpringConfig.java // 配置类
│       ├── dao/
│       │   ├── UserDao.java
│       │   └── UserDaoImpl.java
│       ├── service/
│       │   ├── UserService.java
│       │   └── UserServiceImpl.java
│       ├── controller/
│       │   └── UserController.java
│       └── test/
│           └── AnnotationTest.java
└── resources/
    └── application.properties // 配置文件

2. 核心代码

(1)配置文件:application.properties

properties

复制代码
app.name=Spring注解实战
app.version=1.0

(2)配置类:SpringConfig.java

java

运行

java 复制代码
package com.example.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@ComponentScan("com.example") // 扫描所有注解
@PropertySource("classpath:application.properties") // 加载配置文件
public class SpringConfig {
}

(3)DAO 层:UserDaoImpl.java

java

运行

java 复制代码
package com.example.dao;

import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImpl implements UserDao {
    @Override
    public void queryUser(String userId) {
        System.out.println("查询用户ID:" + userId);
    }
}

(4)Service 层:UserServiceImpl.java

java

运行

java 复制代码
package com.example.service;

import com.example.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    @Value("${app.name}")
    private String appName;

    @Override
    public void getUserInfo(String userId) {
        System.out.println("应用名称:" + appName);
        userDao.queryUser(userId);
    }
}

(5)Controller 层:UserController.java

java

运行

java 复制代码
package com.example.controller;

import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {
    @Autowired
    private UserService userService;

    public void handle(String userId) {
        userService.getUserInfo(userId);
        System.out.println("请求处理完成");
    }
}

(6)测试类:AnnotationTest.java

java

运行

java 复制代码
package com.example.test;

import com.example.config.SpringConfig;
import com.example.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AnnotationTest {
    public static void main(String[] args) {
        // 加载Java配置类,创建IOC容器
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        
        // 获取Controller并调用方法
        UserController controller = context.getBean(UserController.class);
        controller.handle("1001");
    }
}

3. 运行结果

plaintext

复制代码
应用名称:Spring注解实战
查询用户ID:1001
请求处理完成

四、核心避坑指南

1. 常见注解错误与解决方案

错误现象 根因 解决方案
NoSuchBeanDefinitionException 1. 注解未扫描到;2. Bean 名称错误;3. 条件不满足 1. 检查 @ComponentScan 包路径;2. 核对 Bean id;3. 检查条件注解
NoUniqueBeanDefinitionException 同类型有多个 Bean,@Autowired 无法匹配 1. 用 @Qualifier 指定名称;2. 用 @Resource 按名称注入
NullPointerException @Autowired 注入的 Bean 为 null 1. 确保类被注解标记(如 @Service);2. 确保被 Spring 容器管理(非手动 new)
@Value 注入 ${} 为 null 未加载配置文件或配置项不存在 1. 添加 @PropertySource 加载配置;2. 核对配置项名称

2. 注解使用最佳实践

  1. 依赖注入 :优先使用字段注入 (简洁),必须依赖用构造器注入,避免循环依赖;
  2. Bean 命名:默认首字母小写,特殊场景(如类名前两个字母大写)手动指定 name;
  3. 配置方式 :新项目优先用纯 Java 配置(@Configuration),老项目兼容 XML;
  4. 作用域 :非线程安全的 Bean(如含成员变量)用@Scope("prototype"),避免单例线程安全问题;
  5. 注解扫描@ComponentScan指定最小包路径(如com.example),避免扫描无关类,提升性能。

五、今日实战小任务

  1. 基于实战案例,新增@Scope("prototype")到 UserService,测试每次 getBean () 是否创建新对象;
  2. 新增两个同类型的 UserDao 实现类,用@Autowired + @Qualifier@Resource分别注入不同实现;
  3. 自定义@Conditional条件,实现 "只有当 JDK 版本大于 8 时,才创建 UserService Bean"。

总结

  1. Spring 核心注解按功能可分为 6 大类:Bean 定义(@Component/@Service 等)、依赖注入(@Autowired/@Resource 等)、配置类(@Configuration/@Bean 等)、作用域 / 生命周期(@Scope/@PostConstruct 等)、条件注解(@Conditional)、AOP 注解(@Aspect);
  2. @Autowired按类型注入,配合@Qualifier解决同类型多 Bean 问题;@Resource按名称注入,是 JDK 注解,兼容性更好;
  3. @Configuration + @Bean可完全替代 XML 配置,是 Spring Boot 的核心配置方式;
  4. 注解开发的核心是 "让 Spring 容器管理对象,自动注入依赖",需注意包扫描、Bean 命名、条件注解等细节,避免注入失败。

下一篇【Day40】预告:SpringMVC 全注解开发:请求映射、参数绑定、返回值处理,关注专栏从 Spring 核心过渡到 Web 实战~若本文对你有帮助,欢迎点赞 + 收藏 + 关注,你的支持是我更新的最大动力💖!

相关推荐
输出输入2 小时前
JAVA能进行鸿蒙系统应用的开发吗
java
a努力。2 小时前
宇树Java面试被问:数据库死锁检测和自动回滚机制
java·数据库·elasticsearch·面试·职场和发展·rpc·jenkins
魔芋红茶2 小时前
Spring Security 学习笔记 1:快速开始
笔记·学习·spring
PwnGuo2 小时前
Android逆向:在 Unidbg 中解决 native 函数内调用 Java 方法的报错
android·java·python
输出输入2 小时前
IJ IDEA 目录结构
java
Kratzdisteln2 小时前
【1902】预先生成完整的树状PPT结构
java·前端·powerpoint
Sylvia-girl2 小时前
Lambda表达式
java·开发语言
Pluchon2 小时前
硅基计划4.0 算法 动态规划入门
java·数据结构·算法·动态规划
Java程序员威哥2 小时前
Java应用容器化最佳实践:Docker镜像构建+K8s滚动更新(生产级完整模板+避坑指南)
java·开发语言·后端·python·docker·kubernetes·c#