NO.9 Spring IOC 依赖注入—自动扫描

本文介绍Spring框架中IOC容器自动扫描与依赖注入的基本原理和实现方式,涵盖@Component、@Service等注解的使用,以及<context:component-scan>标签的配置方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NO.9 Spring IOC 依赖注入—自动扫描

之前,我们都是将所有的配置都配置到配置文件中,假如一个工程中有成百上千个配置文件,那么找寻每个bean的配置就很难,怎么办呢?

假如一个业务逻辑,需要3-4个JavaBean,如果一个系统中需要几十个或上百个业务逻辑,需要300-400个Java文件。例如一个学生信息的维护,控制层stuAction,Service层stuService,Dao层stuDao,一个业务逻辑中最低要向配置文件中配置这三个,那么有没有一种比较好的方式?Spring提供了一种通过注解的方式让Spring进行扫描。
自动扫描,就是采用注解的形式。

特定组件包括:
@Component: 基本注解, 标识了一个受 Spring 管理的组件,通常标识在一些插件或过滤器上。
@Respository: 标识持久层组件,注解Dao层
@Service: 标识服务层(业务层)组件
@Controller: 标识控制层组件
@Component包含后三个,只要是JavaBean都可以用@Component去注解,后三个则是为了使程序有一定的分层能力。

首先,了解Spring中的自动扫描器.
context:component-scan 标签表示spring bean组件自动扫描器,通过属性base-package扫描对应包下的javabean或者子包中被注解的javaBean文件 。

<?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.xt.spring.ioc.anno"></context:component-scan>
</beans>

业务层

package com.xt.spring.ioc.anno.service;

import org.springframework.stereotype.Service;

@Service
public class StuService {

}

Dao层

package com.xt.spring.ioc.anno.dao;

import org.springframework.stereotype.Repository;

@Repository
public class StuDao {

}

IOCTest

package com.xt.spring.ioc.anno;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.xt.spring.ioc.anno.dao.StuDao;

public class IOCTest {
    private ApplicationContext ioc;

    @Before
    public void iocInit(){
        ioc = new ClassPathXmlApplicationContext("spring/applicationContext-anno.xml");
    }
    @Test
    public void iocTest(){
        StuDao stuDao = ioc.getBean("stuDao",StuDao.class);
    }
}

综上,不用在配置文件中配置bean,只需要标注需要扫描的bean。

当然注解也有自己的作用,倘若想要改名的话,可以直接在注解后面加上括号(stuDao1)@Repository("stuDao1"),这时就获取不到stuDao,想要获取就要采取stuDao1进行获取,如果没有(”“),默认采用类名首字母小写。

倘若不想在bean上面加注解,能否扫描呢?
stuAction没有注解,那么如何获取到呢?配置文件:

<context:component-scan base-package="com.xt.spring.ioc.anno">
    <context:include-filter type="regex" expression=".*Action"/>
    <!-- 比方说想要扫描类名结尾是action的,采用.*Action,*代表前面的字母,action代表以action结尾 
    include表示扫描进来,exclude表示去除-->
</context:component-scan>

annotation是采用注解的方式,regex表示采用正则表达式方式,类似模糊查询。
这种方式只作了解。

怎么将Dao,Service注入到Action中?之前可通过配置文件,那么现在怎么办?
——使用 @Autowired 自动装配 Bean

@Autowired
private StuDao stuDao;

配置文件中不必再配置

<context:component-scan base-package="com.xt.spring.ioc.anno">
    <context:include-filter type="regex" expression=".*Action"/>
</context:component-scan>

倘若Dao有两个实现类StudentDaoJdbcImpl,StudentDaoMybatisImpl,这时就会报错,那该如何解决呢?
使用两个注解@Autowired,@Qualifier,名称加类型结合使用

@Autowired
@Qualifier("stuJdbcDao")//注入的bean的id
private StuDao stuDao;

目前,是通过声明属性的形式向bean中注入一个bean,还可以通过set方法,通过参数注入进去。

    private StuDao stuDao;
    @Autowired @Qualifier("stuJdbcDao")
    public void setStuDao(StuDao stuDao) {
        this.stuDao = stuDao;
    }

如果想要有就注入,没有就不注入不报错,这个时候可设置Autowired中的属性required=false

综上:
1)@Autowired 注解自动装配具有兼容类型的单个 Bean属性
2)构造器, 普通字段(即使是非 public), 一切具有参数的方法都可以应用@Authwired 注解
3)默认情况下, 所有使用 @Authwired 注解的属性都需要被设置。 当 Spring 找不到匹配的 Bean 装配属性时, 会抛出异常, 若某一属性允许不被设置, 可以设置 @Authwired 注解的 required 属性为 false。
4)默认情况下, 当 IOC 容器里存在多个类型兼容的 Bean 时(有多个实现类), 通过类型的自动装配将无法工作. 此时可以在 @Qualifier 注解里提供 Bean 的名称. Spring 允许对方法的入参标注 @Qualifiter 已指定注入 Bean 的名称。
5) @Authwired 注解也可以应用在数组类型的属性上, 此时 Spring 将会把所有匹配的 Bean 进行自动装配。
6)@Authwired 注解也可以应用在集合属性上, 此时 Spring 读取该集合的类型信息, 然后自动装配所有与之兼容的 Bean.。
7)@Authwired 注解用在 java.util.Map 上时, 若该 Map 的键值为 String, 那么 Spring 将自动装配与之 Map 值类型兼容的 Bean, 此时 Bean 的名称作为键值

<think>好的,我现在需要回答用户的问题:“spring依赖注入必须两个对象都处于IOC容器中吗”。首先,我需要回忆Spring依赖注入的基本概念。依赖注入(DI)的核心是由Spring容器管理对象的创建和依赖关系,所以通常情况下,被注入的对象(依赖)和注入目标对象都需要在IOC容器中,这样容器才能完成依赖的装配。 首先,用户的问题是问是否两个对象都必须在IOC容器中。我需要明确,依赖注入的前提是目标对象(需要被注入依赖的对象)本身由IOC容器管理,即由容器创建和初始化。而被注入的依赖对象,通常也需要在容器中,这样容器才能将其注入到目标对象中。但可能存在例外情况吗?比如,是否可以注入一个不在容器中的对象? 接下来,我需要考虑Spring的几种依赖注入方式,比如构造器注入、setter注入、字段注入。这些方式都要求目标对象由容器管理,否则容器无法进行注入。例如,如果一个对象不是由Spring创建的(比如通过new关键字手动创建),那么Spring不会自动注入其依赖,因为容器不知道这个对象的存在。 然后,被注入的依赖是否必须在容器中?通常是的,因为Spring需要从容器中获取依赖实例来注入。但如果有特殊情况,比如使用@Bean方法创建一个外部对象,或者通过FactoryBean、@Inject等方式,可能可以注入非容器管理的对象吗?或者,是否可以通过@Autowired的required属性设置为false,允许依赖为null,但这并不改变依赖需要在容器中的要求。 另外,需要考虑Spring的依赖查找(Dependency Lookup)和依赖注入的区别。依赖注入更强调容器主动注入,而查找是对象主动向容器获取。但用户的问题是关于依赖注入的,所以重点在注入过程。 可能的例外情况:如果目标对象在容器中,而依赖对象是通过其他方式提供的,比如使用@Autowired注解一个静态工厂方法创建的对象,或者使用@Resource的lookup属性指定JNDI查找,这时候依赖可能不在Spring容器中,但这种情况属于特殊配置,不是常规的依赖注入场景。 总结:通常情况下,依赖注入的两个对象(目标对象和依赖对象)都需要在IOC容器中,这样容器才能完成依赖的装配。如果目标对象不在容器中(未被Spring管理),则无法进行依赖注入;如果依赖对象不在容器中,Spring无法找到该依赖实例,会导致注入失败(除非有特殊配置,如提供@Bean方法或使用其他外部资源)。 现在需要验证这些思路是否正确。例如,假设目标对象由Spring管理(如用@Component注解),而依赖对象没有被Spring管理(没有注解或未被扫描),那么@Autowired会失败,抛出NoSuchBeanDefinitionException。这说明依赖对象必须在容器中。如果目标对象没有被Spring管理(比如手动new的),那么其依赖不会被注入,因为Spring不知道这个对象的存在。 因此,结论是:在Spring依赖注入中,**目标对象(被注入依赖的对象)必须由IOC容器管理**(即处于容器中),否则容器无法进行注入;而**被注入的依赖对象通常也需要在IOC容器中**,这样容器才能找到并注入它。特殊情况下(如通过工厂方法、JNDI等外部资源),依赖对象可能不在容器中,但这需要额外配置,并非常规场景。 </think>在Spring依赖注入(DI)中,**目标对象(被注入依赖的对象)必须由IOC容器管理**(即处于容器中),否则容器无法主动注入依赖;而**被注入的依赖对象通常也需要在IOC容器中**,以便容器能找到并完成注入。但存在特殊场景允许依赖对象不在容器中(需额外配置)。以下是详细分析: ### 一、核心规则:目标对象必须在IOC容器中 依赖注入的本质是**Spring容器主动为对象设置依赖**。若目标对象未被容器管理(如通过`new`手动创建),容器无法感知其存在,自然无法注入依赖。 **示例验证**: ```java // 目标对象(未被容器管理) public class UserService { @Autowired // 不会生效! private UserRepository userRepository; } // 手动创建对象 UserService userService = new UserService(); // userRepository 为 null(未被注入) ``` **结论**:目标对象必须通过`@Component`、`@Service`等注解标记,或通过`@Bean`方法注册到容器中,Spring才会为其注入依赖。 ### 二、依赖对象通常需在IOC容器中 依赖对象(如上述`UserRepository`)通常需要在容器中,否则Spring无法找到匹配的Bean完成注入,会抛出`NoSuchBeanDefinitionException`异常。 **示例验证**: ```java // 目标对象(被容器管理) @Service public class UserService { @Autowired private UserRepository userRepository; // 依赖对象 } // 依赖对象(未被容器管理) public class UserRepository { ... } // 无@Component等注解 // 启动Spring容器时,会报错: // No qualifying bean of type &#39;UserRepository&#39; available ``` **结论**:依赖对象需通过`@Component`、`@Repository`等注解,或`@Bean`方法注册到容器中,Spring才能注入。 ### 三、特殊场景:依赖对象可不在容器中(需额外配置) 通过以下方式,可让依赖对象不在容器中但仍被注入: #### 1. 工厂方法(FactoryBean) 通过实现`FactoryBean`接口,可自定义依赖对象的创建逻辑。此时依赖对象本身无需在容器中,但`FactoryBean`实例需注册到容器。 **示例**: ```java // 自定义FactoryBean(注册到容器) @Component public class ExternalUserRepositoryFactory implements FactoryBean<UserRepository> { @Override public UserRepository getObject() { // 返回一个不在容器中的UserRepository实例(如外部API客户端) return new UserRepository("外部数据库连接"); } @Override public Class<?> getObjectType() { return UserRepository.class; } } // 目标对象(依赖注入生效) @Service public class UserService { @Autowired private UserRepository userRepository; // 注入FactoryBean创建的实例 } ``` #### 2. JNDI查找(@Resource) 通过`@Resource`的`lookup`属性指定JNDI名称,可从外部资源(如应用服务器的JNDI上下文)获取依赖对象,无需在Spring容器中注册。 **示例**: ```java @Service public class UserService { @Resource(lookup = "java:comp/env/jdbc/ExternalDB") // 从JNDI获取数据源 private DataSource externalDataSource; // 依赖对象不在Spring容器中 } ``` #### 3. @Autowired(required = false) 若依赖对象不存在,设置`@Autowired(required = false)`可避免异常,但此时依赖为`null`(无实际注入效果),仅用于可选依赖场景。 ### 四、总结 - **目标对象必须在容器中**:否则Spring无法感知并注入依赖。 - **依赖对象通常需在容器中**:常规场景下,依赖对象需注册到容器,否则注入失败。 - **特殊场景例外**:通过`FactoryBean`、JNDI等方式,可注入不在容器中的依赖对象(需额外配置)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值