写点什么

Spring 系列:自动注入(autowire,redis 笔记

作者:Java高工P7
  • 2021 年 11 月 10 日
  • 本文字数:7435 字

    阅读完需:约 24 分钟

@3:定义 diAutowireByName 需要将 autowire 的值置为 byName,表示按名称进行自动注入。


spring 容器创建 diAutowireByName 对应的 bean 时,会遍历 DiAutowireByName 类中的所有 set 方法,然后得到 set 对应的属性名称列表:{“service1”,“service2”},然后遍历这属性列表,在容器中查找和属性同名的 bean 对象,然后调用属性对应的 set 方法,将 bean 对象注入进去


测试用例


package com.javacode2018.lesson001.demo6;


import com.javacode2018.lesson001.demo5.IocUtils;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;


import java.util.ArrayList;import java.util.Collection;import java.util.List;


public class DiAutowireTest {


/**


  • 按照名称进行注入*/@Testpublic void diAutowireByName() {String beanXml = "classpath:/com/javacode2018/lesson001/demo6/diAutowireByName.xml";ClassPathXmlApplicationContext context = IocUtils.context(beanXml);System.out.println(context.getBean("diAutowireByName"));}}


效果


运行 diAutowireByName 输出:


setService1->Service1{desc='service1'}setService2->Service2{desc='service2'}setService2->Service2{desc='service2-1'}setService1->Service1{desc='service1'}DiAutowireByName{service1=Service1{desc='service1'}, service2=Service2{desc='service2'}}DiAutowireByName{service1=Service1{desc='service1'}, service2=Service2{desc='service2-1'}}

优缺点

按名称进行注入的时候,要求名称和 set 属性的名称必须同名,相对于硬编码的方式注入,确实节省了不少代码。

按照类型进行自动注入

用法

autowire 设置为 byType


<bean id="" class="X 类" autowire="byType"/>


spring 容器会遍历 x 类中所有的 set 方法,会在容器中查找和 set 参数类型相同的 bean 对象,将其通过 set 方法进行注入,未找到对应类型的 bean 对象则 set 方法不进行注入。


需要注入的 set 属性的类型和被注入的 bean 的类型需要满足 isAssignableFrom 关系。


按照类型自动装配的时候,如果按照类型找到了多个符合条件的 bean,系统会报错。


set 方法的参数如果是下面的类型或者下面类型的数组的时候,这个 set 方法会被跳过注入:


Object,Boolean,boolean,Byte,byte,Character,char,Double,double,Float,float,Integer,int,Long,Short,shot,Enum,CharSequence,Number,Date,java.time.temporal.Temporal,java.net.URI,java.net.URI,java.util.Locale,java.lang.Class


来看看案例吧。

案例

DiByType.java


package com.javacode2018.lesson001.demo6;


/**


  • 按照类型自动注入*/public class DiAutowireByType {public static class Service1 {private String desc;


public String getDesc() {return desc;}


public void setDesc(String desc) {this.desc = desc;}


@Overridepublic String toString() {return "Service1{" +"desc='" + desc


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


  • ''' +'}';}}


public static class Service2 {private String desc;


public String getDesc() {return desc;}


public void setDesc(String desc) {this.desc = desc;}


@Overridepublic String toString() {return "Service2{" +"desc='" + desc + ''' +'}';}}


private Service1 service1;private Service2 service2;


public Service1 getService1() {return service1;}


public void setService1(Service1 service1) {System.out.println("setService1->" + service1); //@1this.service1 = service1;}


public Service2 getService2() {return service2;}


public void setService2(Service2 service2) {System.out.println("setService2->" + service2); //@2this.service2 = service2;}


@Overridepublic String toString() {return "DiByType{" +"service1=" + service1 +", service2=" + service2 +'}';}}


DiAutowireByType 类中有 2 个 set 方法分别来注入 Service1 和 Service2,两个 set 方法中都输出了一行文字,一会执行的时候可以通过这个输出可以看出 set 方法是否被调用了。


diAutowireByName.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/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsd">


<bean id="service1" class="com.javacode2018.lesson001.demo6.DiAutowireByNameService2"><property name="desc" value="service2"/></bean><bean id="service2-1" class="com.javacode2018.lesson001.demo6.DiAutowireByName$Service2"><property name="desc" value="service2-1"/></bean>


</beans>


上面注释认真看一下。


@1:定义了一个名称为 service1 的 bean


@2:定义了一个名称为 service2 的 bean


@3:定义 diAutowireByName 需要将 autowire 的值置为 byName,表示按名称进行自动注入。


spring 容器创建 diAutowireByName 对应的 bean 时,会遍历 DiAutowireByName 类中的所有 set 方法,然后得到 set 对应的属性名称列表:{“service1”,“service2”},然后遍历这属性列表,在容器中查找和属性同名的 bean 对象,然后调用属性对应的 set 方法,将 bean 对象注入进去


测试用例


DiAutowireTest 类中添加一个方法


/**


  • 按照 set 方法参数类型进行注入*/@Testpublic void diAutowireByType() {String beanXml = "classpath:/com/javacode2018/lesson001/demo6/diAutowireByType.xml";ClassPathXmlApplicationContext context = IocUtils.context(beanXml);System.out.println(context.getBean("diAutowireByType1"));}


效果


运行 diAutowireByType 输出:


setService1->Service1{desc='service1'}setService2->Service2{desc='service2'}DiByType{service1=Service1{desc='service1'}, service2=Service2{desc='service2'}}

优缺点

相对于手动注入,节省了不少代码,新增或者删除属性,只需要增减对应的 set 方法就可以了,更容易扩展了。

注入类型匹配的所有 bean(重点)

按照类型注入还有 2 中比较牛逼的用法:


一个容器中满足某种类型的 bean 可以有很多个,将容器中某种类型中的所有 bean,通过 set 方法注入给一个 java.util.List<需要注入的 Bean 的类型或者其父类型或者其接口>对象


将容器中某种类型中的所有 bean,通过 set 方法注入给一个 java.util.Map<String,需要注入的 Bean 的类型或者其父类型或者其接口>对象


来看个案例就懂了。


DiAutowireByTypeExtend.java


package com.javacode2018.lesson001.demo6;


import java.util.List;import java.util.Map;


/**


  • 满足条件的 bean 注入到集合中(重点)*/public class DiAutowireByTypeExtend {


//定义了一个接口 public interface IService1 {}


public static class BaseServie {private String desc;


public String getDesc() {return desc;}


public void setDesc(String desc) {this.desc = desc;}


@Overridepublic String toString() {return "BaseServie{" +"desc='" + desc + ''' +'}';}}


//Service1 实现了 IService1 接口 public static class Service1 extends BaseServie implements IService1 {


}


//Service1 实现了 IService1 接口 public static class Service2 extends BaseServie implements IService1 {}


private List<IService1> serviceList;//@1private List<BaseServie> baseServieList;//@2private Map<String, IService1> service1Map;//@3private Map<String, BaseServie> baseServieMap;//@4


public List<IService1> getServiceList() {return serviceList;}


public void setServiceList(List<IService1> serviceList) {//@5this.serviceList = serviceList;}


public List<BaseServie> getBaseServieList() {return baseServieList;}


public void setBaseServieList(List<BaseServie> baseServieList) {//@6this.baseServieList = baseServieList;}


public Map<String, IService1> getService1Map() {return service1Map;}


public void setService1Map(Map<String, IService1> service1Map) {//@7this.service1Map = service1Map;}


public Map<String, BaseServie> getBaseServieMap() {return baseServieMap;}


public void setBaseServieMap(Map<String, BaseServie> baseServieMap) {//@8this.baseServieMap = baseServieMap;}


@Overridepublic String toString() { //9return "DiAutowireByTypeExtend{" +"serviceList=" + serviceList +", baseServieList=" + baseServieList +", service1Map=" + service1Map +", baseServieMap=" + baseServieMap +'}';}}


@1,@2,@3,@4:定义了 4 个属性,都是泛型类型的,都有对应的 set 方法。


@5:参数类型是 List,这个集合集合中元素的类型是 BaseServie,spring 会找到容器中所有满足 BaseServie.isAssignableFrom(bean 的类型)的 bean 列表,将其通过 @5 的 set 方法进行注入。


@6:同 @5 的代码


@7:这个参数类型是一个 map 了,map 的 key 是 string 类型,value 是 IService1 类型,spring 容器会将所有满足 IService1 类型的 bean 找到,按照 name->bean 对象这种方式丢到一个 map 中,然后调用 @7 的 set 方法进行注入,最后注入的这个 map 就是 bean 的名称和 bean 对象进行映射的一个 map 对象。


@8:同 @7 的代码


@9:重写了 toString 方法,输出的时候好看一些


测试用例


DiAutowireTest 新增一个方法:


/**


  • 按照类型注入集合*/@Testpublic void diAutowireByTypeExtend() {String beanXml = "classpath:/com/javacode2018/lesson001/demo6/diAutowireByTypeExtend.xml";ClassPathXmlApplicationContext context = IocUtils.context(beanXml);//从容器中获取 DiAutowireByTypeExtendDiAutowireByTypeExtend diAutowireByTypeExtend = context.getBean(DiAutowireByTypeExtend.class);//输出 diAutowireByTypeExtend 中的属性看一下 System.out.println("serviceList:" + diAutowireByTypeExtend.getServiceList());System.out.println("baseServieList:" + diAutowireByTypeExtend.getBaseServieList());System.out.println("service1Map:" + diAutowireByTypeExtend.getService1Map());System.out.println("baseServieMap:" + diAutowireByTypeExtend.getBaseServieMap());}


效果


运行 diAutowireByTypeExtend 输出:


serviceList:[BaseServie{desc='service1-1'}, BaseServie{desc='service1-2'}, BaseServie{desc='service2-1'}]baseServieList:[BaseServie{desc='service1-1'}, BaseServie{desc='service1-2'}, BaseServie{desc='service2-1'}]service1Map:{service1-1=BaseServie{desc='service1-1'}, service1-2=BaseServie{desc='service1-2'}, service2-1=BaseServie{desc='service2-1'}}baseServieMap:{service1-1=BaseServie{desc='service1-1'}, service1-2=BaseServie{desc='service1-2'}, service2-1=BaseServie{desc='service2-1'}}


下面我们来介绍另外一种自动注入方式。

按照构造函数进行自动注入

用法

autowire 设置为 constructor


<bean id="" class="X 类" autowire="constructor"/>


spring 会找到 x 类中所有的构造方法(一个类可能有多个构造方法),然后将这些构造方法进行排序(先按修饰符进行排序,public 的在前面,其他的在后面,如果修饰符一样的,会按照构造函数参数数量倒叙,也就是采用贪婪的模式进行匹配,spring 容器会尽量多注入一些需要的对象)得到一个构造函数列表,会轮询这个构造器列表,判断当前构造器所有参数是否在容器中都可以找到匹配的 bean 对象,如果可以找到就使用这个构造器进行注入,如果不能找到,那么就会跳过这个构造器,继续采用同样的方式匹配下一个构造器,直到找到一个合适的为止。


来看看案例吧。

案例

DiAutowireByConstructor.java


package com.javacode2018.lesson001.demo6;


/**


  • 构造函数的方式进行自动注入*/public class DiAutowireByConstructor {


public static class BaseServie {private String desc;


public String getDesc() {return desc;}


public void setDesc(String desc) {this.desc = desc;}


@Overridepublic String toString() {return "BaseServie{" +"desc='" + desc + ''' +'}';}}


//Service1 实现了 IService1 接口 public static class Service1 extends BaseServie {}


//Service1 实现了 IService1 接口 public static class Service2 extends BaseServie {}


private Service1 service1;private Service2 service2;


public DiAutowireByConstructor() { //@0}


public DiAutowireByConstructor(Service1 service1) { //@1System.out.println("DiAutowireByConstructor(Service1 service1)");this.service1 = service1;}


public DiAutowireByConstructor(Service1 service1, Service2 service2) { //@2System.out.println("DiAutowireByConstructor(Service1 service1, Service2 service2)");this.service1 = service1;this.service2 = service2;}


public Service1 getService1() {return service1;}


public void setService1(Service1 service1) {this.service1 = service1;}


public Service2 getService2() {return service2;}


public void setService2(Service2 service2) {this.service2 = service2;}


@Overridepublic String toString() {return "DiAutowireByConstructor{" +"service1=" + service1 +", service2=" + service2 +'}';}}


@1:1 个参数的构造函数


@2:2 个参数的构造函数


2 个有参构造函数第一行都打印了一段文字,一会在输出中可以看到代码是调用了那个构造函数创建对象。


diAutowireByConstructor.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/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsd">


<bean class="com.javacode2018.lesson001.demo6.DiAutowireByConstructor$Service1"><property name="desc" value="service1"/></bean>


<bean id="diAutowireByConstructor" class="com.javacode2018.lesson001.demo6.DiAutowireByConstructor"autowire="constructor"/>


</beans>


测试用例


DiAutowireTest 新增一个方法


/**


  • 构造函数的方式进行自动注入*/@Testpublic void diAutowireByConstructor() {String beanXml = "classpath:/com/javacode2018/lesson001/demo6/diAutowireByConstructor.xml";ClassPathXmlApplicationContext context = IocUtils.context(beanXml);System.out.println(context.getBean("diAutowireByConstructor"));}


效果


运行 diAutowireByConstructor 输出:


DiAutowireByConstructor(Service1 service1)DiAutowireByConstructor{service1=BaseServie{desc='service1'}, service2=null}


从输出中可以看到调用的是 DiAutowireByConstructor 类中的第一个构造函数注入了 service1 bean。


构造函数匹配采用贪婪匹配,多个构造函数结合容器找到一个合适的构造函数,最匹配的就是第一个有参构造函数,而第二个有参构造函数的第二个参数在 spring 容器中找不到匹配的 bean 对象,所以被跳过了。


我们在 diAutowireByConstructor.xml 加入 Service2 的配置:


<bean class="com.javacode2018.lesson001.demo6.DiAutowireByConstructor$Service2"><property name="desc" value="service2"/></bean>


再来运行一下 diAutowireByConstructor 输出:


DiAutowireByConstructor(Service1 service1, Service2 service2)DiAutowireByConstructor{service1=BaseServie{desc='service1'}, service2=BaseServie{desc='service2'}}


此时可以看到第二个有参构造函数被调用了,满足了贪婪方式的注入原则,最大限度的注入所有依赖的对象。

autowire=default

用法

bean xml 的根元素为 beans,注意根元素有个 default-autowire 属性,这个属性可选值有(no|byName|byType|constructor|default),这个属性可以批量设置当前文件中所有 bean 的自动注入的方式,bean 元素中如果省略了 autowire 属性,那么会取 default-autowire 的值作为其 autowire 的值,而每个 bean 元素还可以单独设置自己的 autowire 覆盖 default-autowire 的配置,如下:


<?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/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsd"default-autowire="byName">


</beans>

案例

diAutowireByDefault.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/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsd"default-autowire="byName"> //@1


<bean id="service1" class="com.javacode2018.lesson001.demo6.DiAutowireByNameService2"><property name="desc" value="service2"/></bean><bean id="service2-1" class="com.javacode2018.lesson001.demo6.DiAutowireByName$Service2"><property name="desc" value="service2-1"/></bean>


</beans>


注意上面的 @1 配置的 default-autowire=“byName”,表示全局默认的自动注入方式是:按名称注入


@2 和 @3 的 autowire=default,那么注入方式会取 default-autowire 的值。


测试用例


DiAutowireTest 中新增一个方法


/**


  • autowire=default*/@Testpublic void diAutowireByDefault() {String beanXml = "classpath:/com/javacode2018/lesson001/demo6/diAutowireByDefault.xml";ClassPathXmlApplicationContext context = IocUtils.context(beanXml);System.out.println(context.getBean("diAutowireByDefault1"));System.out.println(context.getBean("diAutowireByDefault2"));}


效果

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
Spring系列:自动注入(autowire,redis笔记