Spring IOC 注解开发实战:从环境搭建到纯注解配置详解(Spring系列3)

一、引言

在Spring框架的学习与实战中,传统的XML配置方式虽然稳定,但代码冗余、配置繁琐的问题始终存在。Spring从2.0开始逐步支持注解开发,3.0版本更是实现了纯注解配置,彻底摆脱了XML文件的依赖。

本文将从环境准备开始,逐步掌握Spring注解开发的核心:@Component系列注解、组件扫描、纯注解配置类,以及@ComponentScan的底层路径逻辑。通过实战代码,清晰对比XML与注解的差异,彻底吃透Spring IOC/DI的注解化开发。


二、环境准备:注解开发基础项目搭建

在学习注解开发前,我们先搭建一个标准的Maven项目,为后续注解开发做好铺垫。

2.1 项目结构

复制代码
spring_11_annotation_bean
├── src
│   └── main
│       ├── java
│       │   └── com
│       │       └── itheima
│       │           ├── dao
│       │           │   ├── BookDao.java
│       │           │   └── impl
│       │           │       └── BookDaoImpl.java
│       │           └── service
│       │               ├── BookService.java
│       │               └── impl
│       │                   └── BookServiceImpl.java
│       └── resources
│           └── applicationContext.xml
└── pom.xml

2.2 核心依赖(pom.xml)

复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.10.RELEASE</version>
    </dependency>
</dependencies>

2.3 基础代码编写

  1. 数据访问层(BookDao & BookDaoImpl)

    // BookDao接口
    public interface BookDao {
    void save();
    }

    // BookDaoImpl实现类
    public class BookDaoImpl implements BookDao {
    public void save() {
    System.out.println("book dao save ...");
    }
    }

  2. 业务逻辑层(BookService & BookServiceImpl)

    // BookService接口
    public interface BookService {
    void save();
    }

    // BookServiceImpl实现类
    public class BookServiceImpl implements BookService {
    public void save() {
    System.out.println("book service save ...");
    }
    }

2.4 传统XML配置(applicationContext.xml)

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 配置Bean:id指定名称,class指定全限定类名 -->
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl"/>
</beans>

2.5 运行类(App)

复制代码
public class App {
    public static void main(String[] args) {
        // 加载XML配置文件
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取Bean
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();
    }
}

此时运行正常,控制台输出:book dao save ...。但XML配置文件在项目类增多后会变得极其臃肿,这就是注解开发要解决的核心问题。


三、注解开发定义Bean:替代XML的<bean>标签

我们将逐步用注解替代XML配置,核心步骤如下:

3.1 核心步骤拆解

步骤1:删除XML中的<bean>配置

直接移除applicationContext.xml中<bean>相关配置,保留空文件即可。

步骤2:在实现类上添加@Component注解

@Component注解是Spring中最基础的组件注解,用于标识当前类为Spring管理的Bean(替代XML中的<bean>标签)。

复制代码
import org.springframework.stereotype.Component;


// @Component("bookDao") 括号内指定bean的id,与xml中的id对应
@Component("bookDao")
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
}


// 给BookService也添加注解
@Component("bookService")
public class BookServiceImpl implements BookService {
    public void save() {
        System.out.println("book service save ...");
    }
}

⚠️ 注意: @Component注解不能添加在接口上,因为接口无法实例化对象,注解只能标注在具体的实现类上。

步骤3:配置组件扫描

Spring无法自动扫描项目中的注解,需要通过<context:component-scan>标签指定扫描路径,告知Spring去哪个包下寻找@Component注解。

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 组件扫描:base-package指定扫描的基础包 -->
    <context:component-scan base-package="com.itheima"/>
</beans>

组件扫描说明:

  • component-scan:组件扫描,将Spring管理的Bean视为组件
  • base-package:指定Spring扫描的包路径,会扫描该包及其子包下所有类的注解
  • 扫描范围越小,速度越快;一般扫描项目根包(如com.itheima)即可。

步骤4:运行验证

复制代码
public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取BookDao
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao);
        // 获取BookService
        BookService bookService = (BookService) ctx.getBean(BookService.class);
        System.out.println(bookService);
    }
}

控制台输出:

复制代码
com.itheima.dao.impl.BookDaoImpl@73d4cc9e
com.itheima.service.impl.BookServiceImpl@48d37741f
Process finished with exit code 0

四、@Component注解的衍生注解(分层开发规范)

通过查看源码可知,@Controller、@Service、@Repository三个注解本质上都继承了@Component,它们是@Component的衍生注解。

4.1 注解源码对比

复制代码
// @Component 源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {}

// @Controller 源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 本质是@Component
public @interface Controller {}

// @Service 源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 本质是@Component
public @interface Service {}

// @Repository 源码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 本质是@Component
public @interface Repository {}

4.2 衍生注解的作用(分层语义化)

虽然功能与@Component一致,但衍生注解让代码更具语义化,清晰区分代码层级:

注解 适用层级 作用
@Controller 表现层(Web层) 标识控制层组件,处理请求与响应
@Service 业务层(Service层) 标识业务逻辑组件,处理核心业务
@Repository 数据访问层(DAO层) 标识数据访问组件,操作数据库
@Component 通用层 通用组件,无明确层级划分时使用

4.3 实战改造

复制代码
// BookDaoImpl 改为 @Repository
@Repository("bookDao")
public class BookDaoImpl implements BookDao { ... }
// BookServiceImpl 改为 @Service
@Service("bookService")
public class BookServiceImpl implements BookService { ... }

五、纯注解开发:彻底替代XML配置文件

Spring 3.0版本引入了纯注解开发模式,使用Java类替代XML配置文件,是Spring开发的主流方式。

5.1 实现思路

  1. 删除applicationContext.xml配置文件;
  2. 创建Java配置类,替代XML文件;
  3. 使用@Configuration标识配置类;
  4. 使用@ComponentScan替代<context:component-scan>组件扫描;
  5. 通过AnnotationConfigApplicationContext加载配置类。

5.2 实现步骤

步骤1:创建配置类(SpringConfig)

复制代码
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
// 1. 标识该类为Spring配置类,替代XML文件
@Configuration
// 2. 组件扫描:指定基础包,替代<context:component-scan base-package="com.itheima"/>
@ComponentScan("com.itheima")
public class SpringConfig {
}

步骤2:修改运行类(AppForAnnotation)

复制代码
import com.itheima.config.SpringConfig;
import com.itheima.dao.BookDao;
import com.itheima.service.BookService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AppForAnnotation {
    public static void main(String[] args) {
        // 加载纯注解配置类
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        // 获取Bean
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        System.out.println(bookDao);
        BookService bookService = (BookService) ctx.getBean(BookService.class);
        System.out.println(bookService);
    }
}

六、核心难点:@ComponentScan 路径深度解析

@ComponentScan是纯注解开发的核心,很多开发者对其扫描路径存在误解,这里重点拆解其绝对路径特性。

6.1 核心结论

@ComponentScan("com.itheima")中的包名不是相对路径 ,而是Java类路径下的绝对路径 ,其定位规则与Java类的包结构直接关联,本质是全限定包名

6.2 详细解读

1. 本质是"全限定包名"

"com.itheima"是从根包开始的完整层级,对应项目中:

  • 源码路径:src/main/java/com/itheima
  • 编译路径:target/classes/com/itheima(Maven/Gradle项目)

2. 与类路径(classpath)的关系

Spring会从Java类路径(classpath)的根目录开始,按照包名层级查找。

3. 与相对路径的区别

路径类型 示例 特点
相对路径 ./subpackage 相对于当前目录,与包名无关
绝对路径(ComponentScan) com.itheima 从类路径根开始,与当前类所在包无关

七、SpringConfig.class 查找逻辑(类路径核心)

SpringConfig.class的查找与**类路径(classpath)**密切相关,分为编译期和运行期两个阶段。

7.1 编译期:从类路径加载

项目编译后,SpringConfig.java会被编译为字节码文件,输出到target/classes目录:

  • 类路径:target/classes 是类路径的核心;
  • 查找规则:JVM和Spring会从类路径根加载SpringConfig.class。

7.2 关键前提:包路径必须一致

SpringConfig.class能被找到的核心是:代码中的package声明与实际文件路径一致。


八、总结与核心知识点回顾

8.1 核心对比

配置方式 核心注解/标签 加载方式
传统XML <bean>、<context:component-scan> ClassPathXmlApplicationContext
注解+XML @Component、@ComponentScan ClassPathXmlApplicationContext
纯注解 @Configuration、@ComponentScan AnnotationConfigApplicationContext

8.2 核心注解速记

  1. @Configuration:标识当前类为Spring配置类,替代XML文件;
  2. @ComponentScan:配置组件扫描路径,指定Bean的扫描范围;
  3. @Component:通用Bean定义注解,衍生出@Controller/@Service/@Repository;
  4. AnnotationConfigApplicationContext:加载纯注解配置类的核心容器。

8.3 关键注意事项

  • @Component不能用于接口,只能标注在具体实现类上;
  • @ComponentScan是绝对路径扫描,覆盖指定包及其所有子包;
  • 纯注解开发中,配置类的package必须与文件实际路径一致。
相关推荐
凌波粒8 小时前
LeetCode--383.赎金信(哈希表)
java·算法·leetcode·散列表
Rsun045518 小时前
Cursor 快捷键 + 提示词速查卡片
spring
贺小涛8 小时前
VictoriaMetrics深度解析
java·网络·数据库
疯狂打码的少年8 小时前
【Day02 Java转Python】Python的ArrayList: list与tuple的“双面人生
java·python·list
回到原点的码农8 小时前
Spring Boot 3.3.4 升级导致 Logback 之前回滚策略配置不兼容问题解决
java·spring boot·logback
conlin day8 小时前
Spring AI学习(一)
人工智能·学习·spring
小羊在睡觉8 小时前
Go与MySQL锁:索引失效陷阱
数据库·后端·mysql·golang
jwt7939279378 小时前
SpringBoot实现异步调用的方法
java·spring boot·spring