• srping基础——DI(三)


    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>来指定键值对,具体配置如下:

          

        

     

  • 相关阅读:
    RobotFramework下的http接口自动化Get关键字的使用
    通过添加filter过滤器 彻底解决ajax 跨域问题
    TestLink和RedMine的集成
    SonarQube和Maven的集成
    RobotFramework下的http接口自动化Create Http Context关键字的使用
    正负数的源码 反码 补码 转
    SSM的,日常错误
    SSM的 日常错误 之 mybatis
    Eclipse无法启动报An internal error occurred during: "reload maven project". java.lang.NullPointerException
    错误异常
  • 原文地址:https://www.cnblogs.com/ouhouki/p/9692701.html
Copyright © 2020-2023  润新知