Spring IoC相关内容总结

152 阅读7分钟

1. 什么是IoC?

IoC提出背景:在开发中代码间的耦合度太高,修改一处代码往往要修改很多与之关联的代码,为降低代码间的耦合度,提出了IoC思想。

IoC(Inversion of Control:控制反转) 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。 IoC 并非 Spring 特有,在其他语言中也有应用。

  • 控制:指的是对象创建(实例化、管理)的权力。
  • 反转:控制权交给外部环境(Spring 框架、IoC 容器)。

IoC 图解

IoC图解

将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的

在 Spring 中, IoC 容器是 Spring 用来实现 IoC 的载体,IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。


2. 什么是Bean?

一个Spring IoC容器管理着一个或多个Bean,简单来说,Bean 代指的就是那些被 IoC 容器所管理的对象

我们需要告诉 IoC 容器帮助我们管理哪些对象,这个是通过配置元数据来定义的。配置元数据可以是xml文件、注解或者 Java 配置类。

Spring 时代我们一般通过 XML 文件来配置 Bean,后来开发人员觉得 XML 文件来配置不太好,于是 SpringBoot 注解配置就慢慢开始流行起来。

使用xml文件配置

通过基于xml的配置元数据,你可以按以下方式指定你的bean类。

<!-- id和name都是xml中的一个属性,用来标识。
     id必须唯一,name可以重复
     id不能含有特殊字符,name可以
     id只能有一个值,name可以有多个值,逗号或分号隔开即可-->
<!-- class:引入的类-->
<bean id="exampleBean" class="examples.ExampleBean" scope=""/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

使用注解配置

xml文件配置元数据十分麻烦,使用注解配置能极大简化配置过程,想要添加哪个bena对象,就在那个类上加上注解。

spring常用的类注解:

  • @Service 将业务层(Service层)的类标识为Spring容器中的Bean
  • @Controller 将控制层的类标识为Spring容器中的Bean
  • @Repository 将数据访问层(Dao层)的类标识为Spring容器中的Bean
  • @Component 将一个类标识为Spring容器中的Bean

前三个是专用的,@Component是通用的,能用专用的就尽量用专用的。

注解配置示例

@Component("userDao")
public class userDao{......}

与之等效的xml配置

<bean id="userDao" class="com.qing.***.userDao"/>

使用java类配置

基于Java类定义Bean配置元数据,其实就是通过Java类定义Spring配置元数据,且直接消除XML配置文件。 首先让我们看一下基于Java类如何定义Bean配置元数据,具体步骤如下:

  1. 使用**@Configuration**注解需要作为配置的类,表示该类将定义Bean的元数据
  2. 使用**@Bean**注解相应的方法,该方法名默认就是Bean的名称,该方法返回值就是Bean的对象。
  3. AnnotationConfigApplicationContext或子类进行加载基于java类的配置。

java类配置实例:

@Configuration
public class ApplicationContextConfig {
    @Bean
    public String message() {
        return "hello";
    }
}

与之等效的xml配置:

<bean id="message" class="java.lang.String">
    <constructor-arg index="0" value="hello"/>
</bean>

三种配置方法对比

基于XML配置基于注解配置基于Java类配置
Bean定义在XML文件中通过元素定义Bean在Bean实现类处通过标注@Component等定义Bean在标注了@Configuration的Java类中,在类方法上标注@Bean定义Bean
Bean名称通过的id或name属性定义通过注解的value属性定义,如@Component(userDao”)通过@Bean的name属性定义,如@Bean(userDao”)
Bean注入通过子元素或通过p命名空间的动态属性注入通过标出@Autowired,按类型匹配自动注入,配合使用@qualifier按名称匹配注入1.方法处通过@Autowired是方法入参绑定Bean2.通过调用配置类的@Bean方法进行注入
Bean的生命过程方法通过的init-method和destroy-method属性指定Bean实现类方法名。通过在目标方法上标注@PostConstruct和@PreDestroy注解指定通过@Bean的initMethod或destoryMethod指定相应方法
Bean作用范围通过的scope属性指定通过在类定义出标注@Scope指定通过Bean方法定义出标注@Scope指定
Bean延迟初始化通过的lazy-init属性指定默认为default通过在类定义出标注@Lazy指定,如@Lazy (true)通过在Bean方法定义出标注@Lazy指定

基于XML的配置主要使用场景:第三方类库,如DataSource、JdbcTemplate等;命名空间,如aop、context等

基于注解的配置主要使用场景:Bean的实现类是当前项目开发的,可直接在Java类中使用注解配置

基于Java类的配置主要使用场景:对于实例化Bean的逻辑比较复杂,则比较适合用基于Java类配置的方式;在日常的开发中我们主要是使用XML配置和注解配置方式向结合的开发方式,一般不推荐使用基于Java类的配置方式


3. 什么是DI?

依赖注入(Dependency Injection, DI)是一种设计模式,也是Spring框架的核心概念之一。其作用是去除Java类之间的依赖关系,实现松耦合,以便于开发测试。

  • 依赖:是一个对象中将要使用到的另一个对象,如UserService中的DbDriver对象,对于UserService来说,DbDriver是它的一个依赖对象(dependency),依赖于这个对象完成searchUsers操作,而UserService可以看作为需依赖对象(dependent)
  • 注入:依赖对象传递到需依赖对象

在spring中实现依赖注入的常见方式有三种:属性注入、setter方法注入、构造器注入

在这里,暂不讨论通过xml的注入方式,主要讨论基于注解的注入方式。

属性注入

属性注入是我们最熟悉,也是开发中最常用的一种注入方式。

示例如下:

@Service
public class UserService {
    @Autowired
    private QingBean qingBean;//通过属性注入
}

优点:属性注入最大的优点就是实现简单、使用简单,只需要给变量上添加一个注解(@Autowired),就可以在不 new 对象的情况下,直接获得注入的对象了(这就是 DI 的功能和魅力所在)。

缺点:

  1. 功能性问题:无法注入一个不可变的对象(final 修饰的对象);

         2.  通用性问题:只能适应于 IoC 容器;
         2.  设计原则问题:更容易违背单一设计原则。
    

setter方法注入

示例如下:

@Service
public class UserService {
    private QingBean qingBean;
    
    @Autowired  //通过setter方法实现注入
    public void setQingBean(QingBean qingBean) {
        this.qingBean = qingBean;
    }
}

优点:完全符合单一职责的设计原则,每一个 Setter 只针对一个对象。

缺点:

  1. 不能注入不可变对象(final 修饰的对象);
  2. 注入的对象可被修改。

构造器注入

当两个类属于强关联时,我们也可以通过构造器的方式来实现注入

示例如下:

@Service
public class UserService {
     private QingBean qingBean;
    
     @Autowired //通过构造器注入
    public UserService(QingBean qingBean) {
        this.qingBean = qingBean;
    }
}

优点:

  1. 可注入不可变对象;
  2. 注入对象不会被修改;
  3. 注入对象会被完全初始化;
  4. 通用性更好。

4. IoC与DI之间的关系

IoC和DI其实是同一概念的不同角度描述。

IoC强调的是将对象实例的创建控制权由spring容器来统一管理,需要的时候从容器中取出,而不是由调用者自身去创建,从而达到降低代码耦合性与硬代码的目的。依赖注入强调的是当调用者需要使用对象实例时,spring容器为调用者提供对象实例这个过程

本质上应该是属于同一技术实现方式,但是在不同的情况叫法不同。