Spring系列之依赖注入的三种方式

    xiaoxiao2022-06-26  118

    目录

    一、依赖注入方式

    1.使用属性的setXXX方法注入

     2.构造函数注入

    (1)按类型匹配入参type

    (2)按索引匹配入参index

    (3)联合使用类型和索引匹配入参[type和index一起使用]     有时需要联合使用type和index才能确定匹配项和构造函数入参的对应关系,看下面的代码。

    二、依赖注入的本质就是装配——自动装配:spring可以使用xml和注解来进行自动装配。自动装配就是开发人员不必知道具体要装配哪个bean的引用,这个识别的工作会由spring来完成,自动装配就是为了将依赖注入“自动化”的一个简化配置的操作

    2.1 自动装配的概念

    2.2 通过xml配置的方式实现自动装配

    2.2.1 装配分为四种:byName, byType, constructor, autodetect。

    2.3 spring2.5之后提供了注解方式的自动装配。

    2.3.1 常用的自动装配注解有以下几种:@Autowired,@Qualifier,@Named,@Resource,@Inject。

    ,base-package属性指定要自动检测扫描的包。-toc" style="margin-left:80px;">2.4  自动检测配置,也是springmvc中最牛的一项功能:只要一个配置,base-package属性指定要自动检测扫描的包。


    转载:Spring系列之依赖注入的方式

    转载:傻傻分不清:Spring IoC注入,自动装配与循环依赖

    一、依赖注入方式

          对于spring配置一个bean时,如果需要给该bean提供一些初始化参数,则需要通过依赖注入方式,所谓的依赖注入就是通过spring将bean所需要的一些参数传递到bean实例对象的过程(将依赖关系注入到对象中),spring的依赖注入有3种方式:

    ·使用属性的setter方法注入 ,这是最常用的方式;·使用构造器注入;

     最好的解决方案是用构造器参数实现强制依赖,setter 方法实现可选依赖。

    1.使用属性的setXXX方法注入

         属性注入即通过setXxx()方法注入Bean的属性值或依赖对象,由于属性注入方式具有可选择性和灵活性高的优点,因此属性注入是实际应用中最常采用的注入方式。

    <bean id=”……” class=”……”> <property name=”属性1” value=”……”/> <property name=”属性2” ref=”……”/> …… </bean>

          属性注入要求Bean提供一个默认的构造函数,并为需要注入的属性提供对应的Setter方法。Spring先调用Bean的默认构造函数实例化Bean对象,然后通过反射的方式调用Setter方法注入属性值。来看一个简单的例子。

    package com.spring.model; public class Car { private int maxSpeed; private String brand; private double price; public int getMaxSpeed() { return maxSpeed; } //一定要写被注入对象的set方法 public void setMaxSpeed(int maxSpeed) { this.maxSpeed = maxSpeed; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public void run(){ System.out.println("brand:"+brand+",maxSpeed:"+maxSpeed+",price:"+price); } }

       Car类中定义了3个属性,并分别提供了对应的Setter方法。(注:默认构造函数是不带参的构造函数。Java语言规定如果类中没有定义任何构造函数,则JVM自动为其生成一个默认的构造函数。反之,如果类中显示定义了构造函数,则JVM不会为其生成默认的构造函数。所以假设Car类中显示定义了一个带参的构造函数,如public Car(String brand),则需要同时提供一个默认构造函数public Car(),否则使用属性注入时将抛出异常。)

    下面在Spring配置文件中对Car进行属性注入:

    <!-- 属性注入 --> <bean id="car" class="com.spring.model.Car"> <property name="maxSpeed" value="200"></property> <property name="brand" value="红旗CA72"></property> <property name="price" value="200000.00"></property> </bean>

         在上述代码中配置了一个Bean,并为该Bean的3个属性提供了属性值。具体来说,Bean的每一个属性对应一个<property>标签,name为属性的名称,在Bean实现类中拥有与其对应的Setter方法:maxSpeed对应setMaxSpeed(),brand对应setBrand()。      需要指出的是:Spring只会检查Bean中是否有对应的Setter方法,至于Bean中是否有对应的属性变量则不做要求。例如配置文件中<property name="brand"/>的属性配置项仅要求Car类中拥有setBrand()方法,但Car类不一定要拥有brand成员变量。

     2.构造函数注入

    构造函数注入是除属性注入之外的另一种常用的注入方式,它保证一些必要的属性在Bean实例化时就得到设置(construct是bean生命周期的第一步,实例化bean),并且确保了Bean实例在实例化后就可以使用。

    使用方式:

    第一,在类中,不用为属性设置setter方法(但是可以有),但是需要生成该类带参的构造方法。

    第二,在配置文件中配置该类的bean,并配置构造器,在配置构造器中用到了<constructor-arg>节点,该节点有四个属性:

    · index是索引,指定注入的属性位置,从0开始;

    · type是指该属性所对应的类型;

    · ref 是指引用的依赖对象;

    · value 当注入的不是依赖对象,而是基本数据类型时,就用value;

    (1)按类型匹配入参type

          如果任何可用的Car对象都必须提供maxSpeed、brand和price的值,使用属性注入方式只能人为在配置时提供保证,而无法在语法级提供保证,这时通过构造函数注入就可以很好地满足这一要求。使用构造函数注入的前提是Bean必须提供带参的构造函数,下面为Car提供一个可设置maxSpeed、brand和price属性的构造函数。

    package com.spring.model; public class Car { private int maxSpeed; private String brand; private double price; //带参构造方法 public Car(int maxSpeed,String brand, double price){ this.maxSpeed=maxSpeed; this.brand=brand; this.price=price; } public void run(){ System.out.println("brand:"+brand+",maxSpeed:"+maxSpeed+",price:"+price); } }

    构造函数注入的配置方式和属性注入方式的配置有所不同,在spring配置文件中使用构造函数注入装配这个Car Bean。

    <!-- 构造函数注入(按类型匹配) --> <bean id="car1" class="com.spring.model.Car"> <constructor-arg type="int" value="300"></constructor-arg> <constructor-arg type="java.lang.String" value="宝马"></constructor-arg> <constructor-arg type="double" value="300000.00"></constructor-arg> </bean>

    在<constructor-arg>的元素中有一个type属性,它表示构造函数中参数的类型,为spring提供了判断配置项和构造函数入参对应关系的“信息”。

    (2)按索引匹配入参index

          我们知道,Java语言通过入参的类型及顺序区分不同的重载方法,对于上面代码中的Car类,Spring仅通过type属性指定的参数类型就可以知道“宝马”对应String类型的brand入参,而“300000.00”对应double类型的price入参。但是,如果Car构造函数3个入参的类型相同,仅通过type就无法确定对应关系了,这时需要通过入参索引的方式进行确定。 为了更好地演示按索引匹配入参的配置方式,特意对Car构造函数进行一下调整。

    public Car(String brand, String corp,double price){ this.brand=brand; this.corp=corp; this.price=price; }

    brand和corp的入参类型都是String,所以String将无法确定type为String的<constructor-arg>到底对应的是brand还是corp。但是,通过显示指定参数的索引能够消除这种不确定性,如下所示。

    <!-- 构造函数注入(按索引匹配) --> <bean id="car2" class="com.spring.model.Car"> <!-- 注意索引从0开始 --> <constructor-arg index="0" value="宝马"></constructor-arg> <constructor-arg index="1" value="中国一汽"></constructor-arg> <constructor-arg index="2" value="300000.00"></constructor-arg> </bean>

    构造函数第一个参数索引为0,第二个为1,以此类推,因此很容易知道“宝马”对应brand入参,而“中国一汽”对应corp入参。

    (3)联合使用类型和索引匹配入参[type和index一起使用]      有时需要联合使用type和index才能确定匹配项和构造函数入参的对应关系,看下面的代码。

    public Car(String brand, String corp,double price){ this.brand=brand; this.corp=corp; this.price=price; } public Car(String brand, String corp,int maxSpeed){ this.brand=brand; this.corp=corp; this.maxSpeed=maxSpeed; }

         这里,Car拥有两个重载的构造函数,它们都有三个入参。针对这种情况,按照入参索引的配置方式又难以满足要求了,这时需要联合使用<constructor-arg>的type和index才能解决问题,看下面代码。

    <!-- 构造函数注入(通过入参类型和位置索引确定对应关系) --> <!-- 对应public Car(String brand, String corp,int maxSpeed)构造函数 --> <bean id="car3" class="com.spring.model.Car"> <constructor-arg index="0" type="java.lang.String" value="奔驰"></constructor-arg> <constructor-arg index="1" type="java.lang.String" value="中国一汽"></constructor-arg> <constructor-arg index="2" type="int" value="200"></constructor-arg> </bean>

          对于上面的两个构造函数,如果仅通过index进行配置,Spring将无法确定第3个入参配置项究竟是对应int的maxSpeed还是double的price,采用索引匹配时,真正引起歧义的地方在于第3个入参,因此仅需要明确指定第3个入参的类型就可以取消歧义了。所以在上面的代码中,第1个和第2个<constructor-arg>元素的type属性可以去除。       对于由于参数数目相同而类型不同所引起的潜在配置歧义问题,Spring容器可以正确启动且不会给出报错信息,它将随机采用一个匹配的构造函数实例化Bean,而被选择的构造函数可能并不是用户所希望的。因此,必须特别谨慎,以避免潜在的错误。

    二、依赖注入的本质就是装配——自动装配:spring可以使用xml和注解来进行自动装配。自动装配就是开发人员不必知道具体要装配哪个bean的引用,这个识别的工作会由spring来完成,自动装配就是为了将依赖注入“自动化”的一个简化配置的操作

    2.1 自动装配的概念

    大家可以看到用xml装配bean是一件很繁琐的事情,而且我们还要找到对应类型的bean才能装配。

    创建应用对象之间协作关系的行为称为装配。也就是说当一个对象的属性是另一个对象时,实例化时,需要为这个对象属性进行实例化,这就是装配。

    如果一个对象只通过接口来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行切换。但是这样会存在一个问题,在传统的依赖注入配置中,我们必须要明确要给属性装配哪一个bean的引用,一旦bean很多,就不好维护了。基于这样的场景,spring使用注解来进行自动装配,解决这个问题。自动装配就是开发人员不必知道具体要装配哪个bean的引用,这个识别的工作会由spring来完成。

    与自动装配配合的还有“自动检测”,这 个动作会自动识别哪些类需要被配置成bean,进而来进行装配。这样我们就明白了,自动装配是为了将依赖注入“自动化”的一个简化配置的操作。

    2.2 通过xml配置的方式实现自动装配

    2.2.1 装配分为四种:byName, byType, constructor, autodetect。

    具体选择哪一种装配方式,需要配置<bean>标签的autowire属性,如果没有配置,默认是byName类型,就是会根据属性的名字来进行自动装配。

    1)byName就是会将与属性的名字一样的bean进行装配。

    2)byType就是将同属性一样类型的bean进行装配。

    3)constructor就是通过构造器来将类型与参数相同的bean进行装配。

    4)autodetect是constructor与byType的组合,会先进行constructor,如果不成功,再进行byType。

    上面最常用的还是byName和byType。自动装配时,装配的bean必须是唯一与属性进行吻合的,不能多也不能少,有且只有一个可以进行装配的bean,才能自动装配成功。否则会抛出异常。如果要统一所有bean的自动装配类型,可以在<beans>标签中配置default-autowire属性。当然如果配置了autowire属性,我们依然可以手动装配属性,手动装配会覆盖自动装配。

    2.3 spring2.5之后提供了注解方式的自动装配。

    但是要使用这些注解,需要在配置文件中配置<context:annotation-config />。只有加上这一配置,才可以使用注解进行自动装配,默认情况下基于注解的装配是被禁用的。

    2.3.1 常用的自动装配注解有以下几种:@Autowired,@Qualifier,@Named,@Resource,@Inject。

    1)@Autowired注解是byType类型的,这个注解可以用在属性上面,setter方面上面以及构造器上面。

    使用这个注解时,就不需要在类中为属性添加setter方法了。但是这个属性是强制性的,也就是说必须得装配上,如果没有找到合适的bean能够装配上,就会抛出异常NoSuchBeanDefinitionException。这时可以使用required=false来允许可以不被装配上,如果required=false时,则不会抛出异常。另一种情况是同时有多个bean是一个类型的,也会抛出这个异常。此时需要进一步明确要装配哪一个Bean,这时可以组合使用@Qualifier注解,值为Bean的名字即可。

    2)@Qualifier注解使用byName进行装配,这样可以在多个类型一样的bean中,明确使用哪一个名字的bean来进行装配。@Qualifier注解起到了缩小自动装配候选bean的范围的作用。

    注意:@Autowired注解是spring提供的,所以会依赖spring的包。还有一个byType的注解@Inject,与@Autowired注解作用一样,也是byType类型,而且是java ee提供的,完全可以代替@Autowired注解,但是@Inject必须是强制装配的,没有required属性,也就是不能为null,如果不存在匹配的bean,会抛出异常。@Autowired与@Qualifier可以组合使用,@Inject也有一个组合的注解,就是@Named注解,与@Qualifier作用一样,也是byName,但是不是spring的,是java ee标准的。这样就出现了两套自动装配的注解组合,@Autowired与@Qualifier是spring提供的,@Inject与@Named是java ee的。但是@Qualifier注解在java ee中也有一样,作用与spring的@Qualifier注解一模一样,只是所在的包不一样。不过建议大家使用spring的。最后还有一个@Resouce注解, 这个注解也是java ee的,也是byName类型的,原理同@Qualifier和@Named是一样的。

    ,base-package属性指定要自动检测扫描的包。">2.4  自动检测配置,也是springmvc中最牛的一项功能:只要一个配置<context:component-scan base-package="">,base-package属性指定要自动检测扫描的包。

    该配置会自动扫描指定的包及其子包下面被构造型注解标注的类,并将这些类注册为spring bean,这样就不用在配置文件一个一个地配置成bean标签。构造型注解包括:@Controller,@Component,@Service,@Repositoryt标注的自定义注解。

    生成的bean的ID默认为类的非限定名,也就是把类的名字的首字母换成小写。可以在这些注解的值中写名bean id的值,如@Controller("helloworld")。如果你想细化包被扫描的范围,可以使用<context:include-filter>和<context:exclude-filter>。具体使用方法这里不再详说。注意,没有被扫描到的类是不能注册为bean,也就不能被用来装配其他类。所以这个配置的base-package的范围非常重要。

     

     


    最新回复(0)