一文精通控制反转(IOC)和依赖注入(DI)

        作为一名java攻城狮,ioc是一个老生常谈的技术。每个程序员都能说上5分钟。但你是真正的掌握了核心精髓,深入理解其中的原理,还是只是在网上找了篇文章,背了一下概念呢?今天我们一起深入底层源码,彻底将IOC撕烂,吃透。

控制反转IOC

首先我们理解一下什么是控制反转?

        控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

        举个例子来说明一下,比如你要修理汽车,在修理的过程中你需要螺丝刀、扳手、钳子。每次要用得时候你都要自己去找。而容器就相当于一个助手,他会帮你管理螺丝刀、扳手、钳子,需要的时候你只要喊一下给我一把十字的螺丝刀,助手就会准备好并交到你手上。这就是控制反转,对于工具的控制交给了助手(容器),接触了你和工具之间的耦合。

        在代码实现上如果没有容器在创建对象时你需要自己去new.

        User user = new User();

        有了容器你不在需要自己管理对象的创建了,只需要告诉这个媒婆,我要一个肤白貌美大长腿的对象,它就帮你创建好了。

    public class UserService {

          @Autowired     

          private User user;

    }

控制反转的作用

        我们在思考一下为什么我们要使用ioc,换句话说ioc给我们带来了什么好处?

        我们在撸码的过程中,经常会使用到接口。如果没有使用容器,那么在使用的时候需要通过new 来为接口赋值。例如下面这段代码,在a使用时必须要给a指定实现。

classA
{
    AInterface a;
 
    A(){}
     
    AMethod()//一个方法
    {
        a = new AInterfaceImp();
    }
}

        为了进一步优化,我们可以借助工厂来管理AInterface的实现类。

InterfaceImplFactory
{
   AInterface create(Object condition)
   {
      if(condition == condA)
      {
          return new AInterfaceImpA();
      }
      else if(condition == condB)
      {
          return new AInterfaceImpB();
      }
      else
      {
          return new AInterfaceImp();
      }
    }
}

        然而这并没有真正的做到代码解耦合。有没有更好的方式呢?通过IoC模式可以彻底解决这种耦合,它把耦合从代码中移出去,放到统一的XML 文件中(或注解),通过一个容器在需要的时候把这个依赖关系形成,即把需要的接口实现注入到需要它的类中。由IoC容器来管理对象的生命周期、依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分离。

        需要注意IOC并不是spring独有的设计,Pico Container、Avalon 、Spring、JBoss、HiveMind、EJB都使用了IOC。

IOC的实现

        IOC经常会和DI一起提起,有些同学还会把这两者混为一谈。实际上IOC和DI并不是同一个概念。IOC是一种设计模式,DI是IOC的实现方式。IOC主要有两种实现方式

  • 依赖查找:容器提供回调接口和上下文条件给组件。EJB和Apache Avalon 都使用这种方式。这样一来,组件就必须使用容器提供的API来查找资源和协作对象,仅有的控制反转只体现在那些回调方法上。容器将调用这些回调方法,从而让应用代码获得相关资源。
  • 依赖注入(DI):组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。容器全权负责的组件的装配,它会把符合依赖关系的对象通过JavaBean属性或者构造函数传递给需要的对象。通过JavaBean属性注射依赖关系的做法称为设值方法注入(Setter Injection);将依赖关系作为构造函数参数传入的做法称为构造器注入(Constructor Injection)

        本文将对Spring的DI进行深度解析。

依赖注入(Dependency Injection)

        Spring中的依赖注入可以分为 手动注入和自动注入两种。

手动注入

        手动注入即我们通过xml的方式,手动对bean设定使用哪个Bean对依赖进行注入。 手动注入又可以分为方法注入和构造器注入。

        方法注入

        在xml中指定了userService中的orderService属性将通过ref指定的bean进行注入。底层会通过set方法进行注入。

<bean name="userService" class="com.mtb.service.UserService">
        <property name="orderService" ref="orderService"/>
</bean>

        构造器注入

        这种配置会使用构造方法进行注入。

<bean name="userService" class="com.mtb.service.UserService">
        <constructor-arg index="0" ref="orderService"/>
</bean>

自动注入

        自动注入也可以分为两种:@Autowired注解注入 和 XML中配置autowire注入

        XML的autowire自动注入

        前面我们看到了在xml中使用手动注入的方式。其实xml中也可以进行自动注入。写法如下:

<bean id="userService" class="com.mtb.service.UserService" autowire="byType"/>

        autowire支持5中注入模式:        

  • byType: 需要属性必须要有对应的set方法。注入时获取到set方法中的唯一参数的参数类型,并且根据该类型去容器中获取bean。如果找到多个,会报错。
  • byName: 需要属性必须要有对应的set方法。注入时会先根据set方法找到对应的XXX部分的名字。然后使用XXX作为名字去容器中获取Bean进行注入。
  • constructor: 不需要set方法。会通过构造方法的入参从spring容器中查找Bean。先根据Type查找,如果找到多了在通过Name筛选。相当于byType+byName。
  • default:用s>标签中设置的autowire
  • no: 表示关闭autowire

        byType、byName 对应 方法注入,constructor对应构造器注入

        @Autowired注解的自动注入

        这也是我们现在最常用的方式了。@Autowired的效果相当于先byType,再byName。相比XML中的autowire注入,@Autowired注解注入拥有更细粒度的控制和更广泛的适用性。

        @Autowired注解可以写在:

  1. 属性上:先根据属性类型去找Bean,如果找到多个再根据属性名确定一个
  2. 构造方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个
  3. set方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个

        底层分别对应了:属性注入、方法注入、构造器注入

学习了以上内容,已经可以说清楚什么是控制反转,什么是依赖注入了。然而对于一个有追求的攻城狮,从来不满足于只知道结果。既然标题是“深入理解”,那当然要看下源码了。下一篇将通过源码对spring 依赖注入进行深入分析。水深危险,新手深入!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
控制反转(Inversion of Control,简称IoC)和依赖注入(Dependency Injection,简称DI)是两个相关的概念,用于解耦和管理对象之间的依赖关系。 控制反转IoC)是一种设计原则,它将对象的创建和管理权从应用程序代码中转移到容器中。传统的程序设计中,对象之间的依赖关系由对象自己创建和管理,而在IoC中,容器负责创建和管理对象,并将它们注入到需要它们的地方。 依赖注入DI)是实现IoC的一种方式。它通过将依赖关系作为参数传递给对象,或者通过使用容器来自动注入依赖关系,来实现对象之间的解耦。依赖注入可以通过构造函数注入、属性注入或者接口注入等方式来实现。 下面是一个简单的示例来说明IoCDI的概念: 假设我们有一个UserService类,它依赖于一个UserRepository类来获取用户数据。在传统的程序设计中,UserService需要自己创建UserRepository对象并管理它的生命周期: ```java public class UserService { private UserRepository userRepository; public UserService() { userRepository = new UserRepository(); } public User getUserById(int id) { return userRepository.getUserById(id); } } ``` 而在使用IoCDI的方式下,我们可以将UserRepository的创建和管理交给容器来处理,UserService只需要声明它所依赖的UserRepository,并通过构造函数或者属性注入的方式接收它: ```java public class UserService { private UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } public User getUserById(int id) { return userRepository.getUserById(id); } } ``` 在这个示例中,UserService不再负责创建UserRepository对象,而是通过构造函数接收一个UserRepository对象。这样,我们可以通过容器来创建UserService,并将一个UserRepository对象注入到它中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值