2025/2/28 下午《尚硅谷》——基于注解管理bean之功能分析、注解和扫描、扫描组件、bean的id---含代码示例详解

目录

        项目结构总览

1. 注解和扫描的概念

2. 标识组件的常用注解

3. 实例演示一:使用注解和扫描

3.1 配置文件

3.2 标识组件

3.3 测试代码

3.4 输出结果

4. 实例演示二:验证 context:exclude-filter 的使用

4.1 context:exclude-filter 的作用

4.2 type 的取值

4.3 验证 type="annotation" 的排除方式

4.4 验证 type="assignable" 的排除方式

4.5 验证默认扫描行为

4.6 验证 use-default-filters="false" 的使用

4.7.实例演示二---总结

4.7.1 context:exclude-filter 的使用场景

4.7.2 use-default-filters 的作用

5. 实例演示三:Bean 的 ID 默认规则与自定义 ID

5.1 Bean 的 ID 默认规则

5.2 自定义 Bean 的 ID

5.3 实例演示三---总结

5.3.1 Bean 的 ID 默认规则

5.3.2 自定义 Bean 的 ID

5.3.3  注意事项


项目结构---摆烂......摆烂......

spring_ioc_annotation(Spring IOC注解项目)
│
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── atguigu
│   │   │           └── spring
│   │   │               ├── controller
│   │   │               │   └── UserController.java       # 控制层组件
│   │   │               ├── service
│   │   │               │   └── impl
│   │   │               │       └── UserServiceImpl.java  # 服务层实现
│   │   │               └── dao
│   │   │                   └── impl
│   │   │                       └── UserDaoImpl.java      # 数据访问层实现
│   │   │
│   │   └── resources
│   │       └── spring-ioc-annotation.xml                 # Spring IOC配置文件
│   │
│   └── test
│       └── java
│           └── com
│               └── atguigu
│                   └── spring
│                       └── test
│                           └── IOCByAnnotationTest.java  # 单元测试类
│
├── target                                                # 构建输出目录
│   ├── classes                                           # 编译后的字节码
│   └── test-classes                                      # 测试类编译结果
│
├── pom.xml                                               # Maven项目配置
└── .idea                                                 # IDE配置文件(可忽略)


1. 注解和扫描的概念

注解

  • 定义

    • 注解是一种标记,用于告诉 Spring 框架如何处理被标记的类或方法。

    • 注解本身不会执行任何操作,具体的功能由 Spring 框架实现。

  • 本质

    • 所有操作都是由 Java 代码完成的,注解只是告诉框架如何执行这些操作。

扫描

  • 定义

    • Spring 通过扫描类路径,检测哪些类被注解标记,并根据注解的功能进行处理。

  • 作用

    • 让 Spring 知道哪些类需要被管理,以及如何管理这些类。


2. 标识组件的常用注解

Spring 提供了以下常用注解来标识不同类型的组件:

注解 说明
@Component 将类标识为普通组件
@Controller 将类标识为控制器组件(用于 MVC)
@Service 将类标识为业务层组件
@Repository 将类标识为持久层组件(用于 DAO)

注解的关系和区别

  • 关系

    • @Controller@Service@Repository 都是 @Component 的派生注解。

    • 它们的源码中都包含 @Component 注解。

  • 区别

    • 功能上没有区别,Spring 对它们的处理方式相同。

    • 语义上有区别,用于标识组件的作用层次(控制器、业务层、持久层)。

    • 目的是提高代码的可读性和结构严谨性。


3. 实例演示一:使用注解和扫描

3.1 配置文件

在 spring-ioc-annotation.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"
       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">

    <!-- 扫描组件 -->
    <context:component-scan base-package="com.atguigu.spring"></context:component-scan>
</beans>

3.2 标识组件

在类上使用注解标识组件:

  • UserController 类

package com.atguigu.spring.controller;

import org.springframework.stereotype.Controller;

@Controller
public class UserController {
    // 控制器逻辑
}
  • UserServiceImpl 类
package com.atguigu.spring.service.impl;

import com.atguigu.spring.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
    // 业务逻辑
}
  • UserDaoImpl 类
package com.atguigu.spring.dao.impl;

import com.atguigu.spring.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImpl implements UserDao {
    // 数据访问逻辑
}

3.3 测试代码

package com.atguigu.spring.test;

import com.atguigu.spring.controller.UserController;
import com.atguigu.spring.dao.UserDao;
import com.atguigu.spring.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class IOCByAnnotationTest {
    @Test
    public void test() {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
        UserController userController = ioc.getBean(UserController.class);
        System.out.println(userController);
        UserService userService = ioc.getBean(UserService.class);
        System.out.println(userService);
        UserDao userDao = ioc.getBean(UserDao.class);
        System.out.println(userDao);
    }
}

3.4 输出结果

com.atguigu.spring.controller.UserController@475e586c
com.atguigu.spring.service.impl.UserServiceImpl@657c8ad9
com.atguigu.spring.dao.impl.UserDaoImpl@436a4e4b

4. 实例演示二:验证 context:exclude-filter 的使用

4.1 context:exclude-filter 的作用

  • 功能:用于在组件扫描时排除特定的组件。

  • 属性

    • type:设置排除扫描的方式。

    • expression:设置排除的具体内容(注解的全类名或类的全类名)。

4.2 type 的取值

  • annotation:根据注解的类型进行排除。

    • expression 需要设置为排除的注解的全类名。

  • assignable:根据类的类型进行排除。

    • expression 需要设置为排除的类的全类名。


4.3 验证 type="annotation" 的排除方式

配置文件

<!-- 扫描组件 -->
<context:component-scan base-package="com.atguigu.spring">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
  • 作用:排除所有带有 @Controller 注解的组件。

测试代码

@Test
public void test() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
    UserController userController = ioc.getBean(UserController.class); // 报错
    System.out.println(userController);
    UserService userService = ioc.getBean(UserService.class);
    System.out.println(userService);
    UserDao userDao = ioc.getBean(UserDao.class);
    System.out.println(userDao);
}
  • 测试结果

    • UserController 无法被找到,抛出 NoSuchBeanDefinitionException 异常。

    • UserService 和 UserDao 正常加载。

  • 结论type="annotation" 成功排除了带有 @Controller 注解的组件。


4.4 验证 type="assignable" 的排除方式

配置文件

<!-- 扫描组件 -->
<context:component-scan base-package="com.atguigu.spring">
    <context:exclude-filter type="assignable" expression="com.atguigu.spring.controller.UserController"/>
</context:component-scan>
  • 作用:排除 UserController 类。

测试代码

@Test
public void test() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
    UserController userController = ioc.getBean(UserController.class); // 报错
    System.out.println(userController);
    UserService userService = ioc.getBean(UserService.class);
    System.out.println(userService);
    UserDao userDao = ioc.getBean(UserDao.class);
    System.out.println(userDao);
}
  • 测试结果

    • UserController 无法被找到,抛出 NoSuchBeanDefinitionException 异常。

    • UserService 和 UserDao 正常加载。

  • 结论type="assignable" 成功排除了 UserController 类。


4.5 验证默认扫描行为

配置文件

<!-- 扫描组件 -->
<context:component-scan base-package="com.atguigu.spring">
    <!-- 无排除过滤器 -->
</context:component-scan>
  • 作用:扫描 com.atguigu.spring 包下的所有组件。

测试代码

@Test
public void test() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
    UserController userController = ioc.getBean(UserController.class);
    System.out.println(userController);
    UserService userService = ioc.getBean(UserService.class);
    System.out.println(userService);
    UserDao userDao = ioc.getBean(UserDao.class);
    System.out.println(userDao);
}
  • 测试结果

    • UserControllerUserService 和 UserDao 均正常加载。

  • 结论:默认情况下,Spring 会扫描并注册所有带有 @Component 及其派生注解的组件。


4.6 验证 use-default-filters="false" 的使用

配置文件

<!-- 扫描组件 -->
<context:component-scan base-package="com.atguigu.spring" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
  • 作用:禁用默认过滤器,仅扫描带有 @Controller 注解的组件。

测试代码

@Test
public void test() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
    UserController userController = ioc.getBean(UserController.class);
    System.out.println(userController);
    UserService userService = ioc.getBean(UserService.class); // 报错
    System.out.println(userService);
    UserDao userDao = ioc.getBean(UserDao.class); // 报错
    System.out.println(userDao);
}
  • 测试结果

    • UserController 正常加载。

    • UserService 和 UserDao 无法被找到,抛出 NoSuchBeanDefinitionException 异常。

  • 结论use-default-filters="false" 禁用了默认过滤器,仅加载了 @Controller 注解的组件。


4.7.实例演示二---总结

4.7.1 context:exclude-filter 的使用场景
  • 当需要排除某些特定组件时,可以使用 context:exclude-filter

  • 通过 type 属性指定排除方式:

    • annotation:根据注解类型排除。

    • assignable:根据类类型排除。

4.7.2 use-default-filters 的作用
  • 默认值为 true,表示启用默认过滤器(扫描所有 @Component 及其派生注解的组件)。

  • 设置为 false 时,禁用默认过滤器,仅加载通过 <context:include-filter> 指定的组件。


5. 实例演示三:Bean 的 ID 默认规则与自定义 ID

5.1 Bean 的 ID 默认规则

  • 默认规则:通过注解扫描配置的 Bean,其 ID 默认为类名的小驼峰形式(即首字母小写)。

    • 例如:UserController 类的默认 Bean ID 为 userController

    • 例如:UserServiceImpl 类的默认 Bean ID 为 userServiceImpl

配置文件

<!-- 扫描组件 -->
<context:component-scan base-package="com.atguigu.spring"></context:component-scan>

测试代码

@Test
public void test() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
    UserController userController = ioc.getBean("userController", UserController.class);
    System.out.println(userController);
    UserService userService = ioc.getBean("userServiceImpl", UserService.class);
    System.out.println(userService);
    UserDao userDao = ioc.getBean("userDaoImpl", UserDao.class);
    System.out.println(userDao);
}

输出结果

com.atguigu.spring.controller.UserController@143640d5
com.atguigu.spring.service.impl.UserServiceImpl@6295d394
com.atguigu.spring.dao.impl.UserDaoImpl@475e586c
  • 结论:Bean 的 ID 默认为类名的小驼峰形式。


5.2 自定义 Bean 的 ID

  • 方法:通过在标识组件的注解(如 @Controller@Service 等)中设置 value 属性,可以自定义 Bean 的 ID。

    • 例如:@Controller("controller") 将 UserController 的 Bean ID 设置为 controller

修改 UserController 类

@Controller("controller") // 自定义 Bean ID 为 "controller"

package com.atguigu.spring.controller;

import org.springframework.stereotype.Controller;

@Controller("controller") // 自定义 Bean ID 为 "controller"
public class UserController {
}

测试代码(未修改 Bean ID 获取方式)

@Test
public void test() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
    UserController userController = ioc.getBean("userController", UserController.class); // 报错
    System.out.println(userController);
    UserService userService = ioc.getBean("userServiceImpl", UserService.class);
    System.out.println(userService);
    UserDao userDao = ioc.getBean("userDaoImpl", UserDao.class);
    System.out.println(userDao);
}

测试结果

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userController' available
  • 原因UserController 的 Bean ID 已自定义为 controller,而测试代码中仍尝试通过默认 ID userController 获取 Bean,导致异常。


修改测试代码(使用自定义 Bean ID)

UserController userController = ioc.getBean("controller", UserController.class); // 使用自定义 ID

@Test
public void test() {
    ApplicationContext ioc = new ClassPathXmlApplicationContext("spring-ioc-annotation.xml");
    UserController userController = ioc.getBean("controller", UserController.class); // 使用自定义 ID
    System.out.println(userController);
    UserService userService = ioc.getBean("userServiceImpl", UserService.class);
    System.out.println(userService);
    UserDao userDao = ioc.getBean("userDaoImpl", UserDao.class);
    System.out.println(userDao);
}

输出结果

com.atguigu.spring.controller.UserController@6646153
com.atguigu.spring.service.impl.UserServiceImpl@21507a04
com.atguigu.spring.dao.impl.UserDaoImpl@143640d5
  • 结论:通过自定义 Bean ID,可以灵活地指定 Bean 的名称。


5.3 实例演示三---总结

5.3.1 Bean 的 ID 默认规则
  • 默认情况下,Spring 会将类名转换为小驼峰形式作为 Bean 的 ID。

  • 例如:UserController -> userControllerUserServiceImpl -> userServiceImpl

5.3.2 自定义 Bean 的 ID
  • 通过在注解中设置 value 属性,可以自定义 Bean 的 ID。

  • 例如:@Controller("controller") 将 Bean ID 设置为 controller

5.3.3  注意事项---个屁
  • 如果自定义了 Bean 的 ID,获取 Bean 时必须使用自定义的 ID,否则会抛出 NoSuchBeanDefinitionException 异常。

又臭又长又难学,还难理解....... 

2025/2/28-18:05:56