spring的注入你真搞懂了吗?你不会一直都是这样错误的理解吧?

本文深入探讨Spring框架中的依赖注入机制,包括IOC与DI的概念,以及XML配置和注解注入的各种方式,如byName、byType、构造器注入等,帮助读者全面理解Spring的注入原理。

说起spring的注入,可能大家都有了解。可你认知几种注入方式呢?你所认知的注入方式是否正确呢?我估计大多数人了解注入方式不全面,并且还会曲解一些概念。今天我们就参考最权威的官网来具体谈谈spring的注入方式。

这里想给大家复习一下Spring的基础知识,对于后续理解有很大帮助

IOC&&DI

想必关于这两个概念大家不会陌生,面试题经常会出现的。这里我们就从官网详细文档看一看。

在这里插入图片描述

详细的翻译具体概念我就不讲了,在这我只说出自己的理解

其实IOC也被称为DI,因为在spring中IOC作用就是不需要程序员来手动创建和管理java类,而是由spring容器自动实例化Bean对象。怎么个IOC呢,其实就是通过DI(依赖注入)实现的。

补充一点关于spring的bean对象(后面简称bean)
其实bean对象就是java对象,不同的在于Bean对象是由spring创建管理对象。
bean对象 是 java对象,但java对象 不一定是 bean对象

在这里插入图片描述
我们的业务类,经过spring容器DI添加源数据从而生成一个Bean对象可以被使用。这也就是spring IOC容器的运行流程图。

Configuration Metadata(IOC)

关于配置元数据,spring官方文档给出了三种配置方式

  1. XML-based metadata 1.0版本
  2. Annotation-based configuration 2.5版本
  3. Java-based configuration 3.0版本
    在这里插入图片描述

虽说三种,但其实我接触也只有前两种。本文也是关于前两种详细介绍。

基于 XML 的 bean 注入方式

xml手动配置bean (自动装配可以通过set方法自动创建pojo类对象加上元数据信息构造成bean对象)

基于 XML 的 bean 注入方式的实现有三种方法:

  1. xml配置ref
  2. 属性 setter方法注入(或设值注入)
  3. 构造方法注入

在基于 XML 的 bean 装配中,我们需要了解一下自动注入模型
在这里插入图片描述

关于自动模型官网上解释说:当使用基于xml的注入方式时,可以指定自动连接模式
也就是说spring 的自动注入的模型 (仅仅针对xml-based配置)
使用java,annotation另外两种注入方式没有注入模型

Autowiring Collaborators(自动注入的模型)

在这里插入图片描述

  1. no :(默认)没有自动装配。Bean引用必须由ref元素定义。
  2. byname:通过名称查找bean自动装配属性。
  3. bytype:通过类型找bean,根据set或者构造方法注入
  4. construct 推断构造方法 通过构造方法自动配置,如果多个构造方法选择多个参数的构造方法

Annotation-based configuration(注解注入)

@Autowired和@Resource两个注解实现。面试题中也经常会问到两个注解的区别,我搜索网上的答案却讲的是@Autowired是通过bytype,@Resource是通过byname。但这种理解显然是错误的,因为关于自动模型文章前面也已经说过 官网:当使用基于xml的注入方式时,可以指定自动连接模式

@Autowired和@Resource只不过是通过type,name去查找,而不是bytype以及byname模式。有什么区别?
区别在于byname通过名字找bean自动填充–>没找到就会报错
bytype通过类型去找bean自动填充–>没找到就会报错

1. @Autowired:type --> name --> error
不仅仅通过type找。type找不到,还可以通过name找。都找不到才会报错
spring包中的AutowiredAnnotationBeanPostProcessor.java中的一个方法实现解析的

2. @Resource:name --> type --> error
不仅仅通过name找。name找不到,还可以通过type找。都找不到才会报错
javax中的 commonAnnotationBeanPostProcessor.java中的一个方法实现解析的

这里再强调一下:
注解 无关注入的模型(no,byname,bytype,constract)

怎么说?还相信网上的知识点吗?不相信我说的?那可以我们还是来用code证明。

Talk is cheap,show you my code

准备阶段

在这里插入图片描述

  1. 创建一个spring项目
  2. 创建Appconfig类以及test类
  3. 创建几个service类

Appconfig.java

package services;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.ComponentScan;

@Configurable
@ComponentScan("com.shadow")
public class Appconfig {
}

Test.java

package test;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class Test {
    public static void main(String[] args) {
//        AnnotationConfigApplicationContext ac =
//                new AnnotationConfigApplicationContext(Appconfig.class);
        ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("web.xml");
    }
}

其余都为空

基于 XML 的 bean 注入方式

no方式配置

GoudanService.java

package services;

public class GoudanService {
    GouService gouService;

    public void setGouService(GouService gouService) {
        this.gouService = gouService;
    }

    public GouService getGouService() {
        return gouService;
    }
}

web.xml

<bean id="goudanBean" class="services.GoudanService">
        <property name="gouService">
            <ref bean="gouBean"></ref>
        </property>
    </bean>
    <bean id="gouBean" class="services.GouService">

    </bean>

Test.java

package test;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import services.GoudanService;


public class Test {
    public static void main(String[] args) {
//        AnnotationConfigApplicationContext ac =
//                new AnnotationConfigApplicationContext(Appconfig.class);
        ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("web.xml");
        System.out.println(cc.getBean(GoudanService.class).getGouService());
    }
}

运行Test.java的result
在这里插入图片描述

想要在GoudanService中配置GouService,我们采用xml配置方式的no注入模型。虽然我们没有配置注入模型参数但是xml默认是no。故所以我们运行Test.java得出结果是配置成功的。
在这里插入图片描述
我把xml这一段注释掉呢?
根据no注入模型的官网解释,大家应该知道这是无法自动配置的,也就是null。
在这里插入图片描述

bytype方式配置

GoudanService.java

package services;

public class GoudanService {
    GouService gouService;

    public void setGouService(GouService gouService) {
        this.gouService = gouService;
    }

    public GouService getGouService() {
        return gouService;
    }
}

web.xml
在这里插入图片描述

	<bean id="goudanBean" class="services.GoudanService">
	
    </bean>
    <bean id="gouBean" class="services.GouService">

    </bean>

Test.java

package test;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import services.GoudanService;


public class Test {
    public static void main(String[] args) {
//        AnnotationConfigApplicationContext ac =
//                new AnnotationConfigApplicationContext(Appconfig.class);
        ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("web.xml");
        System.out.println(cc.getBean(GoudanService.class).getGouService());
    }
}

运行Test.java的result
在这里插入图片描述

我们发现bytype模型注入,与no方式注入想比不需要了bean里配置property的ref,spring容器会自动根据GouService的类型去找bean,结果找到了类型为:services.GouService的bean,所以也是可以配置成功的。

重点:bytype就是如果属性类型与bean的class类型相同那么可以自动配置,否则配置错误。看下面图

在这里插入图片描述
在这里插入图片描述

byname方式配置

GoudanService.java

package services;

public class GoudanService {
    GouService gouService;
    GouService gouBean;


    public GouService getGouBean() {
        return gouBean;
    }

    public void setGouBean(GouService gouBean) {
        this.gouBean = gouBean;
    }

    public void setGouService(GouService gouService) {
        this.gouService = gouService;
    }

    public GouService getGouService() {
        return gouService;
    }
}

web.xml
在这里插入图片描述

	<bean id="goudanBean" class="services.GoudanService">
	
    </bean>
    <bean id="gouBean" class="services.GouService">

    </bean>

Test.java

package test;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import services.GoudanService;


public class Test {
    public static void main(String[] args) {
//        AnnotationConfigApplicationContext ac =
//                new AnnotationConfigApplicationContext(Appconfig.class);
        ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("web.xml");
//        System.out.println(cc.getBean(GoudanService.class).getGouService());
        System.out.println(cc.getBean(GoudanService.class).getGouBean());
    }
}

运行Test.java的result
在这里插入图片描述

我们发现byname模型注入,与no方式注入想比不需要了bean里配置property的ref,spring容器会自动根据GouService定义属性名称去找bean的名称,结果找到了id名称为:gouBean的bean,所以也是可以配置成功的。

byname就是如果属性名称与bean的id名称相同那么可以自动配置,否则配置错误。
在这里插入图片描述
在这里插入图片描述

construct方式配置

GoudanService.java

package services;

public class GoudanService {
    GouService gouService;
    GouService gouBean;

    //构造方法1
    GoudanService(GouService gouBean){
        this.gouBean=gouBean;

    }
//    //构造方法2
//    GoudanService(GouService gouService){
//        this.gouService=gouService;
//    }
//    //构造方法3
//    GoudanService(GouService gouService,GouService gouBean){
//        this.gouService=gouService;
//        this.gouBean=gouBean;
//    }
    
    public GouService getGouBean() {
        return gouBean;
    }
    public GouService getGouService() {
        return gouService;
    }
//    public void setGouBean(GouService gouBean) {
//        this.gouBean = gouBean;
//    }
//    public void setGouService(GouService gouService) {
//        this.gouService = gouService;
//    }
}

web.xml
在这里插入图片描述

	<bean id="goudanBean" class="services.GoudanService">
	
    </bean>
    <bean id="gouBean" class="services.GouService">

    </bean>

Test.java

package test;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import services.GoudanService;


public class Test {
    public static void main(String[] args) {
//        AnnotationConfigApplicationContext ac =
//                new AnnotationConfigApplicationContext(Appconfig.class);
        ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("web.xml");
        System.out.println(cc.getBean(GoudanService.class).getGouService());
        System.out.println(cc.getBean(GoudanService.class).getGouBean());
    }
}

分别运行构造方法1,2,3三次,注释掉其他两种构造方法
运行Test.java的result

构造方法1
在这里插入图片描述
构造方法2
在这里插入图片描述
构造方法3
在这里插入图片描述

我们发现construct模型注入,它既可以通过name注入也可以通过type注入。

Annotaton-based configuration(注解注入方式)

@Autowired

在这里,很多人网上说@Autowired是通过bytype查找,但其实这种说法有些许的不严谨。
1.自动注入模型是基于xml的bean注入方式。
2.bytype模型是只要通过类型查找,如果找不到,或者找到两个相同的类型bean就会报错
3.而@Autowired是通过类型去找,找不到还会通过名称去找

@Resource

而@Resource是通过byname查找,这种说法和@Autowired相似也是不严谨的。
byname模型是只要通过名称查找,如果找不到报错而@Resource是通过名称去找,找不到还会通过类型去找,不单单只限于通过name去查找。

区别
  1. @Autowired type --> name --> error
    spring包中的AutowiredAnnotationBeanPostProcessor.java中的一个方法实现解析的
  2. @Resource name --> type --> error
    javax中的 commonAnnotationBeanPostProcessor.java中的一个方法实现解析的

Spring官方文档

<think>好的,我现在需要回答用户关于Spring依赖注入的问题。用户之前已经问过Java中Set和Map的区别,现在转向Spring框架中的依赖注入,说明他们对Java生态有一定了解,可能需要更深入的框架知识。 首先,我需要明确依赖注入(DI)的定义,确保自己理解正确。依赖注入Spring框架的核心功能之一,用于管理对象之间的依赖关系,减少耦合。接下来,用户可能想知道为什么需要依赖注入,以及如何具体实现。 用户的问题分为两部分:是什么和怎么实现。因此,回答的结构需要先解释概念,再讲解实现方式。记得用户之前的问题回答结构清晰,有分点和示例,可能期望类似的格式。 接下来,我需要考虑依赖注入的三种主要方式:构造器注入、Setter方法注入和字段注入。需要分别说明每种方式的优缺点,以及适用的场景。同时,可能需要提到基于注解的配置和XML配置的区别,因为现代Spring项目多使用注解。 还需要提到相关的注解,比如@Autowired、@Component、@Service等,说明它们的作用。此外,Java配置(@Configuration和@Bean)也是实现依赖注入的一种方式,需要涵盖。 用户可能对为什么使用依赖注入感兴趣,所以需要解释控制反转(IoC)和依赖注入的关系,以及它们带来的好处,如松耦合、易于测试等。 需要注意避免技术术语过于复杂,用简洁的语言和示例说明。例如,构造器注入如何通过构造函数传递依赖,Setter方法如何通过set方法注入,字段注入如何直接使用注解。 可能需要检查是否有遗漏的实现方式,比如接口注入,但Spring主要支持前三种,所以可能不需要深入。同时,确保提到的Spring版本特性,比如推荐使用构造器注入,尤其是在Spring 4.x之后。 最后,总结部分需要对比不同注入方式的优缺点,帮助用户根据实际情况选择合适的方法。可能还需要提到常见的异常,比如多个Bean候选时的问题,以及如何解决,但用户的问题主要集中在实现方式,可能不需要深入异常处理。 在组织内容时,保持每个部分的小标题,如概念、三种注入方式、配置方法、总结对比,这样结构清晰。示例代码要用Java,并且正确展示注解的使用,比如@Component和@Autowired的位置。 需要确保回答准确,没有错误信息,比如构造器注入Spring中的推荐情况,以及字段注入可能存在的缺点,比如不易测试或空指针问题。同时,注意术语的一致性,比如“依赖注入”和“控制反转”的关系。 可能还需要简要提到Spring容器的作用,如ApplicationContext如何管理Bean的生命周期和依赖关系,但不要过于深入,以免偏离用户的问题。 最后,回顾用户之前的回答,使用表格对比可能更直观,所以总结部分可以用表格形式比较三种注入方式的优缺点,帮助用户快速理解。 现在需要将上述思考整理成结构化的回答,确保每个部分清晰,步骤明确,示例正确,并且语言简洁易懂。</think>### Spring依赖注入(Dependency Injection)详解 --- #### **1. 依赖注入是什么?** - **核心定义** 依赖注入(DI)是**控制反转(IoC)**的一种实现方式,由Spring容器**主动管理对象之间的依赖关系**,而非由对象自行创建或查找依赖。 - **目标**:解耦组件,提升代码可维护性和可测试性。 - **类比**:传统方式(对象自己“找依赖”) vs DI(依赖由外部“注入”)。 - **为什么需要DI?** 假设类A依赖类B: ```java // 传统方式:类A内部直接new类B → 紧耦合 class A { private B b = new B(); } // DI方式:类B由外部注入 → 松耦合 class A { private B b; public A(B b) { this.b = b; } // 构造器注入 } ``` --- #### **2. 依赖注入的三种实现方式** ##### **(1) 构造器注入(推荐方式)** - **原理** 通过**构造函数**传递依赖对象,Spring容器在创建Bean时自动注入。 - **代码示例** ```java @Component public class UserService { private final UserRepository userRepo; // 构造器注入(无需@Autowired注解) public UserService(UserRepository userRepo) { this.userRepo = userRepo; } } ``` - **优点** - 强制依赖初始化,避免空指针。 - 适合**必需依赖**,且依赖不可变(final字段)。 - Spring 4.x+ 官方推荐方式。 ##### **(2) Setter方法注入** - **原理** 通过**Setter方法**设置依赖对象,容器调用Setter方法注入。 - **代码示例** ```java @Component public class OrderService { private PaymentService paymentService; // Setter方法注入 @Autowired public void setPaymentService(PaymentService paymentService) { this.paymentService = paymentService; } } ``` - **优点** - 灵活性高,适合**可选依赖**或需要重新配置依赖的场景。 ##### **(3) 字段注入(不推荐)** - **原理** 直接在字段上使用`@Autowired`注解,Spring通过反射注入依赖。 - **代码示例** ```java @Component public class ProductService { @Autowired // 字段注入 private InventoryService inventoryService; } ``` - **缺点** - 隐藏依赖关系,难以追踪依赖来源。 - 无法设置final字段,导致依赖可变。 - 单元测试时需依赖Spring容器或反射工具。 --- #### **3. 依赖注入的配置方式** Spring支持两种配置方式管理依赖: ##### **(1) 注解配置(主流方式)** - **核心注解** - `@Component`:标记类为Spring管理的Bean。 - `@Autowired`:自动注入依赖(可省略,若只有一个构造器)。 - `@Qualifier`:指定注入的Bean名称(解决多个同类型Bean冲突)。 - `@Configuration` + `@Bean`:Java显式配置Bean。 - **示例:使用Java配置类** ```java @Configuration public class AppConfig { @Bean public UserRepository userRepository() { return new UserRepository(); } } ``` ##### **(2) XML配置(传统方式)** - **通过XML文件定义Bean及其依赖** ```xml <beans> <bean id="userService" class="com.example.UserService"> <constructor-arg ref="userRepository"/> </bean> <bean id="userRepository" class="com.example.UserRepository"/> </beans> ``` --- #### **4. 依赖注入的底层实现** - **Spring容器(IoC容器)** - 负责创建、配置、组装Bean,管理其生命周期。 - 核心接口:`ApplicationContext`(如`AnnotationConfigApplicationContext`)。 - **依赖查找流程** 1. 根据类型或名称匹配候选Bean。 2. 解决依赖冲突(如`@Primary`、`@Qualifier`)。 3. 注入到目标对象(构造器/Setter/字段)。 --- #### **5. 三种注入方式对比表** | **特性** | 构造器注入 | Setter注入 | 字段注入 | |------------------|--------------------------|--------------------------|------------------------| | **代码可读性** | 高(明确依赖关系) | 中(需查看Setter) | 低(隐藏依赖) | | **不可变性** | 支持final字段 | 不支持 | 不支持 | | **单元测试** | 易(直接传参) | 易(调用Setter) | 难(需反射或容器) | | **Spring推荐度** | 推荐(尤其强制依赖) | 可选(可选依赖) | 不推荐 | --- #### **6. 实际应用建议** - **优先使用构造器注入**:确保必需依赖且不可变。 - **Setter注入用于可选依赖**:如配置参数、动态替换实现。 - **避免字段注入**:除非在简单原型或遗留代码中。 --- ### 总结 Spring依赖注入通过**解耦对象创建与使用**,使代码更灵活、易维护。掌握构造器注入、Setter注入的原理与适用场景,结合注解配置,能高效构建松耦合的Spring应用。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值