Spring IoC 注入方式探究

2020-05-04  本文已影响0人  苏wisdom

1 先说问题

Spring IoC有哪几种注入方式?

网上的资料真的很多,但也真啥样的都有,我也不保证自己说的全部都是对的,但是提供一种思考的角度也是好的,望斧正。

在犹豫不决的时刻,是时候祭出官方文档和源码啦!带着问题看文档和源码,才能真正吃透一个知识点。

2 配置方式

首先IoC的容器配置方式主要分为两种:

  1. XML配置 - XML-based configuration
  2. 注解配置 - Annotation-based Container Configuration

配置方式只是我们使用的时候,告诉Spring去哪找资源而已,并不是所谓的“IoC注入方式”。去看文档,在Annotation-based Container Configuration章节,开篇就对比两种配置方式的优劣。

image.png

翻译过来,简单点说,就是Spring对两种方式都可以容纳,不是说一个项目里非此即彼,只能使用其中一种。注释方式在用起来更短、更简洁,但是让人觉得配置分散;XML擅长在不接触源代码或不重新编译它们的情况下连接组件。

注意:注释注入会在XML注入之前执行。因此,如果同一个类的依赖通过两种方法都配置了注入,XML配置会覆盖注释配置的。

3 注入方式

注入方式我们可以说有以下四种,但实际上XML配置方式只实现前两种,后面两种用注解实现去做更方便。

3.1 构造器注入

Constructor-based Dependency Injection, 这是官方最推崇的注入方式,也是最便于进行单元测试的方式。但是当构造器中的参数过多,就显得很难受了,要考虑重构。

3.1.1 XML 配置的构造器注入

package x.y;
public class SimpleMovieLister {

    // SimpleMovieLister 依赖于 MovieFinder
    private MovieFinder movieFinder;

    // 提供给 Spring container 注入 MovieFinder 的构造器
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // 省略其他业务逻辑
}

xml配置的时候大概长这样,在xml里, ref 标签表示你想要向一个对象传递一个引用。

<beans>
    <bean id="simpleMovieLister" class="x.y.SimpleMovieLister">
        <constructor-arg ref="movieFinder"/>
    </bean>

    <bean id="movieFinder" class="x.y.MovieFinder"/>
</beans>

3.1.2 注解配置的构造器注入

注解我们就拿@Autowire来举例,其实还可以使用@Resource@Inject等注解。@Autowire注解写在构造方法上,是在doCreateBean方法中的createBeanInstance时候注入的。

@Service
public class MovieRecommenderService {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommenderService(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

MovieRecommenderService 类一般情况下我们都会在类名上写@Service交由容器作为单例储存起来,不用自己去XML里费劲写<bean>了。
非SpringBoot项目还需要在XML配置文件中使用<context:annotation-config/>标签开启自动注解。

3.2 Setter注入

3.2.1 XML配置的Setter注入

package x.y;
public class SimpleMovieLister {

    // SimpleMovieLister 依赖于 MovieFinder
    private MovieFinder movieFinder;

    // 提供给容器进行注入的 setter 方法
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // 其他业务逻辑
}

xml的配置大概长这样

   <bean id="simpleMovieLister" class="x.y.SimpleMovieLister">
      <property name="movieFinder" ref="movieFinder"/>
   </bean>

   <bean id="movieFinder" class="x.y.MovieFinder">
   </bean>

3.2.2 注解配置的Setter注入

@Autowired放在Setter方法上时,是装配属性的时候才注入,即调用doCreateBean方法中的populateBean

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

3.3 Field注入

这里进行Field与构造函数混合注入,事实上我们看到很多的项目,都是只使用的Field注入,这样做的问题在于,单元测试的时候想要注入Mock的依赖就很麻烦。

@Autowired放在Field上时,是装配属性的时候才注入,即调用doCreateBean方法中的populateBean

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    private MovieCatalog movieCatalog;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

3.4 自定义的方法注入

emmm...暂时还没见过哪个项目里是这么写的

如果@Autowire写在自定义的方法上,也是装配属性的时候才注入,doCreateBean方法中的populateBean

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}
上一篇下一篇

猜你喜欢

热点阅读