srping基础——DI(三)

走远了吗. 提交于 2020-03-22 21:15:13

1、依赖和依赖注入

  传统应用程序设计中所说的依赖一般指的是“类与类之间的关系”,那么首先让我们复习一下类之间的关系:

  泛化:表示类与类之间的继承关系,表示接口与接口之间的继承关系;

  

  实现:表示类对接口的实现;

  

  依赖:当类与类之间有使用关系时就属于依赖关系,不同于关联关系,依赖不具有“拥有关系”,而是一种相识关系,只在某个特定的地方才有关系。

  

  关联:表示接口与接口或类与类之间的依赖关系,表现为“拥有关系”;具体代码可以用实例变量来表示。

  

  组合:属于关联的特殊情况,体现了部分整体的关系,是一种“强拥有关系”;整体与部分拥有相同的生命周期,是一种强关联;

  

  聚合:属于关联的特殊情况,也体现了部分整体的关系,是一种“弱拥有关系”;整体和部分可以有不一样的生命周期。是一种弱关联;

  

  它们关系的强弱顺序:泛化= 实现> 组合> 聚合> 关联> 依赖 

  spring IoC容器依赖有两层含义:bean依赖容器容器注入bean的依赖资源

  • bean依赖容器:这里的依赖是指容器负责创建bean,并管理bean的生命周期,正式由于容器来控制创建bean并注入依赖,也就是控制值被反转了,这也正式IoC名字的由来,此处的依赖指的是bean和容器之间的依赖关系。
  • 容器注入bean的依赖资源:依赖资源可以是bean、外部文件、常量数据等,在Java中都反应为对象,并且有容器负责组装bean之间的依赖关系,此处的依赖指的是bean之间的关系可以认为是传统类与类之间的关联、组合、聚合关系。

2、为什么要依赖注入

  • 动态替换bean依赖对象,程序更灵活:替换bean对象无需更改源文件,直接修改配置文件即可。
  • 更好实践面向接口编程,代码更清晰:在bean中只需指定依赖对象的接口,接口定义依赖对象完成的功能,通过容器注入依赖实现。
  • 更好实践优先使用组合,而不是继承:因为IoC容器采用注入依赖,也就是组合对象,从而更好的实现对象的组合。 
  • 增加bean的复用性:依赖于对象组合,bean可复用且复用更简单
  • 降低bean之间的耦合:由于我们完全面向接口编程,在代码中没有直接引用bean依赖实现,全部引用接口,而且不会显示创建依赖对象代码。
  • 代码结构更清晰:要应用依赖注入,代码结构要按照规约方式进行书写,从而更好的应用一些最佳实践,因此代码结构更清晰。

  如何设计好类结构才是关键,依赖注入只是装配对象的一种手段。上一篇我们已经了解了bean依赖容器,spring IoC容器注入依赖资源主要有以下三种形式:

  构造器注入:就是容器实例化bean时注入那些依赖,通过在bean定义中指定构造器参数注入依赖,包括实例工厂方法参数注入依赖。

  setter注入:通过setter方法注入依赖。

  方法注入:通过配置方式替换掉bean方法,也就是通过配置改变bean方法功能。

3、依赖注入具体配置

  3.1 构造器注入:

    3.1.1 使用构造器注入通过配置构造器参数实现,构造器参数就是依赖。除了构造器方式还有实例工厂,静态工厂可以进行构造器注入。

    

    构造器注入可以根据参数的索引注入,参数的类型注入或参数名注入(参数名注入有限制:编译程序时打开调试模式或在构造器上使用@ConstructorProperties注解指定参数名)

 1 public class HelloImpl3 implements HelloApi {
 2    private String message;
 3    private int index;
 4    //@java.beans.ConstructorProperties({"message", "index"})
 5    public HelloImpl3(String message, int index) {
 6      this.message = message;
 7      this.index = index;
 8    }
 9    @Override
10    public void sayHello() {
11    System.out.println(index + ":" + message);
12  }
 1 <!-- 通过构造器参数索引方式依赖注入 -->
 2 <bean id="byIndex" class="cn.javass.spring.chapter3.HelloImpl3">
 3   <constructor-arg index="0" value="Hello World!"/>
 4    <constructor-arg index="1" value="1"/>
 5 </bean>
 6 <!-- 通过构造器参数类型方式依赖注入 -->
 7 <bean id="byType" class="cn.javass.spring.chapter3.HelloImpl3">
 8    <constructor-arg type="java.lang.String" value="Hello World!"/>
 9    <constructor-arg type="int" value="2"/>
10 </bean>
11 <!-- 通过构造器参数名称方式依赖注入 -->
12 <bean id="byName" class="cn.javass.spring.chapter3.HelloImpl3">
13    <constructor-arg name="message" value="Hello World!"/>
14    <constructor-arg name="index" value="3"/>
15 </bean>
 1 @Test
 2 public void testConstructorDependencyInjectTest() {
 3   BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter3/constructorDependencyInject.xml");
 4   //获取根据参数索引依赖注入的Bean
 5   HelloApi byIndex = beanFactory.getBean("byIndex", HelloApi.class);
 6   byIndex.sayHello();
 7   //获取根据参数类型依赖注入的Bean
 8   HelloApi byType = beanFactory.getBean("byType", HelloApi.class);
 9   byType.sayHello();
10   //获取根据参数名字依赖注入的Bean
11   HelloApi byName = beanFactory.getBean("byName", HelloApi.class);
12   byName.sayHello();
13 }

     3.1.2 静态工厂方法注入和实例工厂方法注入

 1 <!--静态工厂方法-->
 2 <bean id="byIndex" class="cn.javass.spring.chapter3.DependencyInjectByStaticFactory" factory-method="newInstance">
 3   <constructor-arg index="0" value="Hello World!"/>
 4   <constructor-arg index="1" value="1"/>
 5 </bean>
 6 <bean id="byType" class="cn.javass.spring.chapter3.DependencyInjectByStaticFactory" factory-method="newInstance">
 7   <constructor-arg type="java.lang.String" value="Hello World!"/>
 8   <constructor-arg type="int" value="2"/>
 9 </bean>
10 <bean id="byName" class="cn.javass.spring.chapter3.DependencyInjectByStaticFactory" factory-method="newInstance">
11   <constructor-arg name="message" value="Hello World!"/>
12   <constructor-arg name="index" value="3"/>
13 </bean>
24 <!--实例工厂方法-->
25 <bean id="instanceFactory" class="cn.javass.spring.chapter3.DependencyInjectByInstanceFactory"/>
26 <bean id="byIndex" factory-bean="instanceFactory" factory-method="newInstance">
27    <constructor-arg index="0" value="Hello World!"/>
28   <constructor-arg index="1" value="1"/>
29 </bean>
30 <bean id="byType" factory-bean="instanceFactory" factory-method="newInstance">
31   <constructor-arg type="java.lang.String" value="Hello World!"/>
32   <constructor-arg type="int" value="2"/>
33 </bean>
34 <bean id="byName" factory-bean="instanceFactory" factory-method="newInstance">
35   <constructor-arg name="message" value="Hello World!"/>
36   <constructor-arg name="index" value="3"/>
37 </bean> 
 1 //静态工厂类
 2 package cn.javass.spring.chapter3;
 3 import cn.javass.spring.chapter2.helloworld.HelloApi;
 4 public class DependencyInjectByStaticFactory {
 5   public static HelloApi newInstance(String message, int index) {
 6   return new HelloImpl3(message, index);
 7   }
 8 }
 9 //实例工厂类
10 package cn.javass.spring.chapter3;
11 import cn.javass.spring.chapter2.helloworld.HelloApi;
12 public class DependencyInjectByInstanceFactory {
13    public HelloApi newInstance(String message, int index) {
14    return new HelloImpl3(message, index);
15    }
16 }

   因为参数名需做额外配置,因此不建议使用根据参数名进行构造器注入

  3.2 setter注入

  setter注入,是通过构造器、静态工厂、实例工厂实例化好bean中,再调用bean类的setter方法注入依赖。

  

1 <!-- 通过setter方式进行依赖注入 -->
2  <bean id="bean" class="cn.javass.spring.chapter3.HelloImpl4">
3    <property name="message" value="Hello World!"/>
4      <property name="index">
5      <value>1</value>
6    </property>
7  </bean>
 1 package cn.javass.spring.chapter3;
 2 import cn.javass.spring.chapter2.helloworld.HelloApi;
 3 public class HelloImpl4 implements HelloApi {
 4    private String message;
 5    private int index;
 6   //setter方法
 7    public void setMessage(String message) {
 8    this.message = message;
 9    }
10    public void setIndex(int index) {
11    this.index = index;
12    }
13    @Override
14    public void sayHello() {
15    System.out.println(index + ":" + message);
16    }
17 }
18 
19 @Test
20 public void testSetterDependencyInject() {
21    BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter3/setterDependencyInject.xml");
22    HelloApi bean = beanFactory.getBean("bean", HelloApi.class);
23    bean.sayHello();
24 }

  JavaBean本质就是一个pojo,具体有如下限制:

    该类必须有公共的无惨构造器

    属性为private访问级别

    属性必要时通过一组setter和getter方法来访问

  3.3 常量注入

    注入常量是依赖注入中最简单的,配置方式如下:

1 <property name="message" value="Hello World!"/>
2 或
3 <property name="index"><value>1</value></property>

    以上两种方式都可以,从配置来看第一种更简洁,此处的value中指定的全是字符串,由spring容器将此字符串转换成属性所需的类型,如果转换出错则抛出相应的异常。spring容器目前能对各种基本类型把配置的字符串参数转换为所需的类型。

 1 //测试类
 2 public class BooleanTestBean {
 3    private boolean success;
 4    public void setSuccess(boolean success) {
 5    this.success = success;
 6    }
 7    public boolean isSuccess() {
 8    return success;
 9    }
10 }
 1 <!-- boolean参数值可以用on/off -->
 2 <bean id="bean2" class="cn.javass.spring.chapter3.bean.BooleanTestBean">
 3  <property name="success" value="on"/>
 4 </bean>
 5 <!-- boolean参数值可以用yes/no -->
 6 <bean id="bean3" class="cn.javass.spring.chapter3.bean.BooleanTestBean">
 7  <property name="success" value="yes"/>
 8 </bean>
 9 <!-- boolean参数值可以用1/0 -->
10 <bean id="bean4" class="cn.javass.spring.chapter3.bean.BooleanTestBean">
11  <property name="success" value="1"/>
12 </bean>

  3.4 注入集合、数组和字典

    spring IoC容器不仅能注入简单的数据类型,还能注入集合(Collection、Set、List)类型、数组类型、字典数据类型、Properties类型。

    3.4.1 注入集合类型:

  • List类型:需要使用list标签注入,其具体配置如下:

  

1 <bean id="listBean" class="cn.javass.spring.chapter3.bean.ListTestBean">
2  <property name="values">
3    <list>
4      <value>1</value>
5      <value>2</value>
6      <value>3</value>
7    </list>
8  </property>
9 </bean>
 1 package cn.javass.spring.chapter3.bean;
 2 import java.util.List;
 3 public class ListTestBean {
 4    private List<String> values;
 5    public List<String> getValues() {
 6      return values;
 7    }
 8    public void setValues(List<String> values) {
 9      this.values = values;
10    }
11 }
12 @Test
13 public void testListInject() {
14  BeanFactory beanFactory = new ClassPathXmlApplicationContext("chapter3/listInject.xml");
15   ListTestBean listBean = beanFactory.getBean("listBean", ListTestBean.class);
16   System.out.println(listBean.getValues().size());
17   Assert.assertEquals(3, listBean.getValues().size());
18 }
  • set类型:需要使用set标签来配置注入,其配置参数含义与list相同
1 <bean id="setBean" class="cn.javass.spring.chapter3.bean.SetTestBean">
2   <property name="values">
3     <set>
4       <value>1</value>
5       <value>2</value>
6       <value>3</value>
7     </set>
8   </property>
9 </bean>
 1 package cn.javass.spring.chapter3.bean;
 2 import java.util.Set;
 3 public class SetTestBean {
 4    private Set<String> values;
 5    public void setValues(Set<String> values) {
 6      this.values = values;
 7    }
 8    public Set<String> getValues() {
 9      return values;
10    }
11 }
  • Collection类型:由于Collection类型是List和Set类型的基类型,所以使用list和set标签都可以进行注入,配置方式和以上配置方式相同。

    3.4.2 注入数组类型

      需要使用array标签来配置注入,其中标签属性“value-type”和“merge”与list标签含义完全相同。具体配置如下:

      

    3.4.3 注入字典类型:

      字典类型是包含键值对数据的数据结构,需要使用map、entry标签来配置注入,其属性key-type和value-type分别指定键值数据类型,其含义同list标签相同。使用key、value字标签分别指定键值数据。具体配置如下:

      

    3.4.4 Properties注入:

      spring能注入java.util.Properties类型数据,需要使用props标签来配置注入,键值类型必须是String,不能变。字标签<prop key=“键”>值<prop>来指定键值对,具体配置如下:

      

    

 

 

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!