Spring之IOC的注入方式

Spring之IOC的注入方式

在java中,要使用一个对象,必须先创建一个实例,但是有了IOC之后,对象的创建与销毁都交给了IOC容器,不用我们手动创建,而是直接从IOC容器中获取,达到了解耦的效果。IOC是一种思想,在Spring中,实现IOC的方式是DI(依赖注入),本文会介绍Spring依赖注入的几种方式。

Spring的依赖注入

对象,在Spring中叫做bean,即使是最简单的应用,也需要多个bean共同协作。依赖注入是指对象之间的依赖关系,一起协作的其他对象,通过构造器的参数、工厂方法的参数创建的对象,或者构造函数、工厂方法创建的对象来设置属性。所以容器的工作实际上就是创建bean并注入依赖关系。Spring中的DI方式主要有两种,构造器注入和Setter注入。

项目准备

新建一个maven项目,JDK版本1.8,引入Spring的核心依赖

<dependencies>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-core</artifactId>
           <version>$&#123;spring.version&#125;</version>
       </dependency>

       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-context</artifactId>
           <version>$&#123;spring.version&#125;</version>
       </dependency>

       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-beans</artifactId>
           <version>$&#123;spring.version&#125;</version>
       </dependency>
</dependencies>

构造器注入

  1. 新建一个User类作为注入的例子,添加get,set方法,重写toString方法,添加两个参数不同的构造器。

    public class User &#123;
     private String id;
    
     private String name;
    
     private Integer age;
    
     public User(String id, String name) &#123;
         this.id = id;
         this.name = name;
     &#125;
    
     public User(String name, Integer age) &#123;
         this.name = name;
         this.age = age;
     &#125;
    
     public String getId() &#123;
         return id;
     &#125;
    
     public void setId(String id) &#123;
         this.id = id;
     &#125;
    
     public String getName() &#123;
         return name;
     &#125;
    
     public void setName(String name) &#123;
         this.name = name;
     &#125;
    
     public Integer getAge() &#123;
         return age;
     &#125;
    
     public void setAge(Integer age) &#123;
         this.age = age;
     &#125;
    
     @Override
     public String toString() &#123;
         return "User&#123;" +
                 "id='" + id + '\'' +
                 ", name='" + name + '\'' +
                 ", age=" + age +
                 '&#125;';
     &#125;
    &#125;
    
  2. xml配置如下,有三种不同的写法
    在resource下面新建application-constructor.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
     <!--构造方法注入-->
     <!--使用index属性来显式指定构造参数的索引-->
     <bean id="user1" class="org.spring.ioc.entity.User">
         <constructor-arg index="0" value="1234"/>
         <constructor-arg index="1" value="spring"/>
     </bean>
    
     <!--使用type属性显式指定简单类型的构造器参数类型,这里对应的是User类中传入name,age的构造器-->
     <bean id="user2" class="org.spring.ioc.entity.User">
         <constructor-arg type="java.lang.String" value="spring"/>
         <constructor-arg type="java.lang.Integer" value="20"/>
     </bean>
    
     <!--也可以使用构造器参数命名来指定值的类型-->
     <bean id="user3" class="org.spring.ioc.entity.User">
         <constructor-arg name="id" value="1234"/>
         <constructor-arg name="name" value="spring"/>
     </bean>
    </beans>
    

    在bean的constructor-arg元素下进行指定,constructor-arg顾名思义就是构造器参数的意思,其中包括了三个属性配置

  • index 是一个索引顺序,对应构造器参数的索引,根据索引进行注入
  • type 构造器的参数类型,可以通过类型进行匹配注入
  • name 构造器参数名,根据名称进行匹配注入
  1. 验证
    现在可以启动Spring容器来验证bean是否注入成功

    public class Main &#123;
     private final static String APPLICATION = "classpath:application-*.xml";
    
     public static void main(String[] args) &#123;
       //加载xml配置文件
       ApplicationContext context = new ClassPathXmlApplicationContext(APPLICATION);
       constructorInject(context);
     &#125;
    
     private static void constructorInject(ApplicationContext context) &#123;
         //获取bean实例,传入的参数值为xml中配置的id
         User user1 = (User) context.getBean("user1");
         System.out.println(user1.toString());
         User user2 = (User) context.getBean("user2");
         System.out.println(user2.toString());
         User user3 = (User) context.getBean("user3");
         System.out.println(user3.toString());
     &#125;
    &#125;
    

输出结果如下:

User&#123;id='1234', name='spring', age=null&#125;
User&#123;id='null', name='spring', age=20&#125;
User&#123;id='1234', name='spring', age=null&#125;

可以看出我们定义的User对象已经成功交给Spring容器管理

Setter注入

Setter注入也需要在xml中进行配置,在调用了无参的构造方法或者无参的静态工厂方法实例化bean之后,容器通过回调bean的setter方法来完成setter注入。

  1. 接下来新建Blog,Author两个类,添加get,set方法,重写toString方法:
    Blog类:

    public class Blog &#123;
     private String name;
    
     private String content;
    
     private Long date;
    
     private Author author;
    
     public String getName() &#123;
         return name;
     &#125;
    
     public void setName(String name) &#123;
         this.name = name;
     &#125;
    
     public String getContent() &#123;
         return content;
     &#125;
    
     public void setContent(String content) &#123;
         this.content = content;
     &#125;
    
     public Long getDate() &#123;
         return date;
     &#125;
    
     public void setDate(Long date) &#123;
         this.date = date;
     &#125;
    
     public Author getAuthor() &#123;
         return author;
     &#125;
    
     public void setAuthor(Author author) &#123;
         this.author = author;
     &#125;
    
     @Override
     public String toString() &#123;
         return "Blog&#123;" +
                 "name='" + name + '\'' +
                 ", content='" + content + '\'' +
                 ", date=" + date +
                 ", author=" + author +
                 '&#125;';
     &#125;
    &#125;
    

    Author类:

    public class Author &#123;
     private String name;
    
     private Integer age;
    
     private String url;
    
     public String getName() &#123;
         return name;
     &#125;
    
     public void setName(String name) &#123;
         this.name = name;
     &#125;
    
     public Integer getAge() &#123;
         return age;
     &#125;
    
     public void setAge(Integer age) &#123;
         this.age = age;
     &#125;
    
     public String getUrl() &#123;
         return url;
     &#125;
    
     public void setUrl(String url) &#123;
         this.url = url;
     &#125;
    
     @Override
     public String toString() &#123;
         return "Author&#123;" +
                 "name='" + name + '\'' +
                 ", age=" + age +
                 ", url='" + url + '\'' +
                 '&#125;';
     &#125;
    &#125;  
    
  2. xml配置
    setter注入是通过在bean下面配置property元素来完成的,在resource下面新建application-setter.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
     <!--setter注入-->
     <bean id="blog" class="org.spring.ioc.entity.Blog">
         <property name="name" value="spring-ioc"/>
         <property name="content" value="spring"/>
         <property name="date" value="1520232449944"/>
         <property name="author" ref="author"/>
     </bean>
    
     <bean id="author" class="org.spring.ioc.entity.Author">
         <property name="name" value="luoliang"/>
         <property name="age" value="18"/>
         <property name="url" value="https://luoliangdsga.github.io"/>
     </bean>
    </beans>  
    

    配置很简单,通过制定property元素的name和value属性,设置变量名对应的值,其中author属性引用的另一个bean,所以使用了ref属性。

  3. 验证

    public class Main &#123;
     private final static String APPLICATION = "classpath:application-*.xml";
    
     public static void main(String[] args) &#123;
         //加载xml配置文件
         ApplicationContext context = new ClassPathXmlApplicationContext(APPLICATION);
         setterInject(context);
     &#125;
    
     private static void setterInject(ApplicationContext context) &#123;
         Blog blog = (Blog) context.getBean("blog");
         System.out.println(blog.toString());
     &#125;
    &#125;
    

    结果如下:

    Blog&#123;name='spring-ioc', content='spring', date=1520232449944, author=Author&#123;name='luoliang', age=18, url='https://luoliangdsga.github.io'&#125;&#125;
    

    所有我们配置的属性都注入成功。

总结

我们可以混合使用构造器注入和Setter注入,最佳实践是强制性依赖关系时使用构造器注入,可选的依赖关系时使用Setter注入,在setter注入中可以使用@Required注解让属性成为必须的依赖项。
有很多小伙伴会觉得很奇怪,明明使用注解进行配置依赖更加的简单。不否认,SpringBoot推出之后,Spring已经不再推荐xml配置,而是提倡java配置和注解,但是xml配置是Spring的基础。正是因为传统的Spring应用xml配置太过于复杂,才会出现SpringBoot这门技术来解决这些问题,一个技术的兴起是有各种原因的。SpringBoot虽然解决了配置复杂的问题,但是对于刚入门的人来说,不知道其中的细节,这可能并不是一个好的开始。
上面用到的ApplicationContext,它所管理的beans支持构造函数注入和setter注入,在一些依赖已经使用构造器注入之后它还支持setter注入。我们也可以用BeanDefinition的形式配置依赖,它能根据指定的PropertyEditor实现将属性从一种格式转化为另外一种格式。但是,在日常的开发中我们不会直接以编程的方式去创建bean,而是采用上面所讲的xml配置创建bean,或者是通过注解(即@Component,@Service等注解类),或者基于@Configuration类的@Bean方法。本质上这些资源会转换成BeanDefinition的实例并且用于加载整个Spring IoC容器实例。所以,不管我们在传统Spring应用还是SpringBoot中,使用Spring的IOC,它的原理都是不会变的。

源码

上面所用到的代码我已放在我的github上,欢迎star,一起学习,共同进步,如有不对之处,欢迎指出。


   转载规则

本文不允许转载。
 上一篇
SpringBoot中使用Redis的实践 SpringBoot中使用Redis的实践
SpringBoot中使用Redis的实践 Redis是一个高性能的内存数据库,在日常开发中运用非常的广泛,主要用作缓存。Redis提供了非常丰富的数据结构,有String,List,Set,ZSet,Hash,Redis为这些数据结构提供
2018-05-22
下一篇 
Java设计模式-代理模式 Java设计模式-代理模式
设计模式之代理模式 代理模式是设计模式的一种,简单解释就是不直接访问目标对象,通过访问代理对象就可以实现对目标对象的访问。就像现在买火车票,不用直接去火车站买,可以直接去各个代售点或者APP上购买,这里的代售点或者APP就是火车站的代理,这
2018-05-08
  目录