Spring控制反转和依赖注入

      引言:学习过Spring框架的人一定都会听过Spring的IoC(控制反转) 、DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC 、DI这两个概念是模糊不清的,是很难理解的下面这篇文章就是从原理到实现方式各个角度分析了IOC和DI的区别,谨以此作为交流学习。

一、概念解释

IOC(控制反转)是对组件对象控制权的转移,从代码本身转移到了外部容器,通过容器来实现对组件的装配和管理。而Spring是IOC的有一个容器。也就是说,将对象的管理(创建,维护、销毁)交给Spring管理,但在使用对象的时候将对象反转给使用方

           在Java SE部分,我们通常的做法是在对象内部new一个新的对象,是程序主动去创建依赖对象,这样做会导致类与类之间的高耦合,很难测试,且程序一旦修改,改动面很大。Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。降低了程序间的耦合性,方便测试,利于功能复用,使得系统的结构体系更加灵活。

 依赖注入(DI)即在运行期由容器将依赖关系注入到组件之中,就是在运行期,由Spring根据配置文件,将其他对象的引用通过组件提供的setter方法进行设定。

          spring创建对象A时,会将对象A所依赖的对象B也创建出来,并自动注入到对象A中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。

 

IoC和DI什么关系呢?

          其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”

      以下是具体区分:

      控制反转:创建对象实例的控制权从代码控制剥离到IOC容器控制,实际就是你在xml文件控制,侧重于原理。

      依赖注入:创建对象实例时,为这个对象注入属性值或其它对象实例,侧重于实现。

      依赖注入和控制反转是同一概念,是对同一件事情的不同描述,它们描述的角度不同。依赖注入是从应用程序的角度在描述:应用程序依赖容器创建并注入它所需要的外部资源;而控制反转是从容器的角度在描述:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源(对象、文件等)。

 

二、具体解释IOC和DI的关系和区别

要理解IOC,先要理解Spring的工作原理,以下为Spring的使用步骤

              1、向相应的软件引入Spring核心jar包

              2、创建Spring全局配置文件

              3、创建类

              4、将第三步创建好的类交给Spring管理,在第二步创建的Spring全局配置文件中添加相关配置信息

              5、创建主函数使用类。步骤:利用ClassPathXmlApplicationContext 获取Spring实例,利用getBean()方法获取第三步创建好的类的实例,调用里面方法实现。

 

Spring实例过程理解:

        Spring容器支持两种格式的配置文件,分别为Properties文件格式和xml文件格式,而在实际的开发当中,最常使用的是xml文件格式,下面将以xml文件格式的配置方式进行说明。XML配置文件的根元素是<beans>,其可以包含多个子元素<bean>,每个子元素定义一个Bean,并描述了Bean该如何被装配到Spring容器中。

         <bean>标签元素中的属性如下:

            bean标签:管理bean

           id属性:必填Bean的唯一标识符,Spring对Bean的配置、管理都通过该属性来完成;

           name属性:作用和id属性一致,名字可以包含特殊的字符

          class属性:指定了Bean的具体实现类,必须使用类的全路径名

          scope属性:bean的范围。设定Bean实例的作用域,其属性有singleton(单例)[在同一个jvm中只存在一个实例]、    prototype(原型)[多例]、request、session、和global Session,默认值为singleton

          constructor-arg:<bean>元素的子元素,可以使用此元素传入构造参数进行实例化,该元素的index属性指定构造参数    的序号(从0开始)。

          property:<bean>元素的子元素,通过调用Bean实例中的setter方法完成属性赋值,从而完成依赖注入;

          ref:property、constructor-arg等元素的子元素,该元素中的bean属性用于指定对Bean工厂中某个Bean实例的引用;

          value:property、constructor-arg等元素的子元素,用来直接指定一个常量值;

          list:用于封装List或数组类型的依赖注入;

          set:用于封装Set或数组类型的依赖注入;

          map:用于封装Map或数组类型的依赖注入;

          entry:map元素的子元素,用于设定一个键值对,其key属性指定字符串类型的键值,ref或value子元素指定其值。

 

了解了Bean标签,再来了解下Bean的实例化过程。

在Spring中,Bean的实例化一般分为三种:无参构造、静态工厂、普通工厂

1、无参构造

         配置文件:  

  <!--无参构造函数-->

    <bean id="user" class="com.tulun.bean6.User"/>

   注意:    使用该方式实现bean的实例化,必须保证实例化的bean具有无参构造函数

                  不显性写构造函数则会生成默认的无参构造函数

                   指定了构造函数,需要指定一个无参构造函数

2、静态工厂

静态工厂类实现

public class StaticFactory {

    public static User getBean() {

        return new User();

    }

}    

 

spring配置文件: 

 <!--

  静态工厂实例化bean

  class属性:指定的是静态工厂类的全路径

  factory-method属性:创建user对象的方法名

  -->

  <bean id="user1" class="com.tulun.factory6.StaticFactory" factory-method="getBean"/>

3、普通工厂

普通工厂实现

public class CommonFactory {

    public User getBean() {

        return new User();

    }

}

 

spring的配置文件:  

<!--普通工厂实例化bean-->

    <bean id="factoty" class="com.tulun.factory6.CommonFactory"/>

    <bean id="user2" factory-bean="factoty" factory-method="getBean"/>

 

要理解DI,首先需要理解Spring中依赖注入的过程:

首先,先介绍一下在Java中的依赖注入方式3种)

                 1、通过set方法

                 2、通过有参构造

                 3、通过接口方式

Spring依赖注入的方式(2种):

 1、通过有参构造

 public class User {

     private String name;



     //有参构造函数

     public User(String name) {

         this.name = name;

     }

  }

  spring配置      

<!--依赖注入方式1:有参构造-->

      <bean id="user3" class="com.tulun.bean6.User">

          <!--name属性注入-->

          <constructor-arg name="name" value="zhansan"/>

      </bean>

 2、通过set方法

 public class User {

     private String name;

     //set方法

     public void setName(String name) {

         this.name = name;

     }

 }

 spring配置:    

 <!--依赖注入方式:set方法-->

     <bean id="user4" class="com.tulun.bean6.User">

         <!--name属性注入-->

         <property name="name" value="lisi"/>

     </bean>

 

说到这里,介绍一下Spring配置中,类型注入的几种方式(4种)

第一种: 

<!--引用类型注入-->

    <bean id="factory" class="com.tulun.factory6.CommonFactory"/>

    <bean id="user5" class="com.tulun.bean6.User">

        <property name="factory" ref="factory"/>

    </bean>

第二种:

<!--注入list类型属性-->

    <bean id="user6" class="com.tulun.bean6.User">

       <property name="li">

           <list>

               <value>1000</value>

               <value>2000</value>

               <value>1000</value>

           </list>

       </property>

    </bean>

 

第三种:    

<!--注入数组类型属性-->

    <bean id="user7" class="com.tulun.bean6.User">

        <property name="in">

            <array>

                <value>356</value>

            </array>

        </property>

</bean>

第四种:  

  <!--注入map类型属性-->

    <bean id="user8" class="com.tulun.bean6.User">

       <property name="mp">

           <map>

               <entry key="A" value="aa"/>

               <entry key="B" value="bb"/>

           </map>

       </property>

    </bean>  

 

注意,通常情况下,注入普通类型,用到的是value   

                  注入引用类型,用到的是ref  

 

三、Spring以注解的方式进行Bean的实例化和属性注入

Bean的实例化:

1、全局配置文件中增加约束条件

 <?xml version="1.0" encoding="UTF-8"?>

 <beans xmlns="http://www.springframework.org/schema/beans"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xmlns:context="http://www.springframework.org/schema/context"

        xsi:schemaLocation="http://www.springframework.org/schema/beans

     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

     http://www.springframework.org/schema/context

     http://www.springframework.org/schema/context/spring-context-3.0.xsd">



     <!--通过扫描方式,扫描指定包路径下的所有注解-->

  <context:component-scan base-package="com.tulun"/>

 </beans>

2、对类添加注解 @Component

@Component(value = "user")

public class User {}

3、直接调用spring容器获取对象       

 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("springconfig1.xml");

        User user = (User) context.getBean("user");

        System.out.println(user);

     注意: 

             @Component注解是bean的实例化提供注解方式(四种)

             @Component衍生出三个注解

             @Controller web层

             @Service    业务层

             @Repository  dao层

其余三个注解作用和 @Component一样的只是为了编写业务流程代码的规范性和可读性、区分性更强一点才衍生出来的。      

 

 

依赖注入:

依赖注入的注解是@Autowired也可以使用@Resource

例子:使用注解:@Autowired

 public class UserController {

     @Autowired

     private UserService userService;

     public void add() {

         //调用service提供add方法

         userService.add();

         System.out.println("UserController.add...");

     }

 }

 

@Autowired和@Resource的区别

 用途:做bean的注入时使用

历史:@Autowired    属于Spring的注解                

org.springframework.beans.factory.annotation.Autowired

@Resource   不属于Spring的注解,JDK1.6支持的注解   

javax.annotation.Resource

 

共同点

  装配bean. 写在字段上,或写在setter方法

不同点:

@Autowired  默认按类型装配

      依赖对象必须存在,如果要允许null值,可以设置它的required属性为false   @Autowired(required=false)

      也可以使用名称装配,配合@Qualifier注解

 public class TestServiceImpl {

    @Autowired

    @Qualifier("userDao")

     private UserDao userDao;

 }

 

@Resource  默认按名称进行装配,通过name属性进行指定

public class TestServiceImpl {

    // 下面两种@Resource只要使用一种即可

    @Resource(name="userDao")

    private UserDao userDao; // 用于字段上

    

    @Resource(name="userDao")

    public void setUserDao(UserDao userDao) { // 用于属性的setter方法上

        this.userDao = userDao;

    }

}

 

总结:大白话解释,@Autowired自动注解,举个例子吧,一个类,俩个实现类,Autowired就不知道注入哪一个实现类,而Resource有name属性,可以区分。

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值