Spring IOC 的 @Autowired、@Resource、@Inject 的用法和区别。

Autowired、Resource、Inject 这几个都是用来做依赖注入的,我们挨个的来看一下。

Autowired

Autowired可以加在构造函数、方法、方法参数、成员变量、注解上,从Spring4.3开始,如果bean只有一个构造函数,或者只有一个primary/default的构造函数,那么构造函数上的Autowired可以不加,否则还是要在某个构造函数上明确的加上Autowired。

源码如下:

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
  boolean required() default true;
}

举个例子:

public class MovieRecommender {
    private final CustomerPreferenceDao customerPreferenceDao;
    //成员变量注入
    @Autowired
    private MovieCatalog movieCatalog;
    //构造函数注入,此处可以省略
    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
      this.customerPreferenceDao = customerPreferenceDao;
    }
}

还可以注入到集合里面:

public class MovieRecommender {
  //所有的MovieCatalog类型的bean
  @Autowired
  private MovieCatalog[] movieCatalogs;
  }
}

当然也可以注入到map:

public class MovieRecommender {
    //map的key是bean的名字,value是bean:
    private Map<String, MovieCatalog> movieCatalogs;
    @Autowired
    public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
      this.movieCatalogs = movieCatalogs;
    }
}

默认Autowired是不允许为空的,如果允许为空可以设置Autowired(required = false):

public class SimpleMovieLister {
    private MovieFinder movieFinder;
    //此时,MovieFinder 这个bean可以不存在
    @Autowired(required = false)
    public void setMovieFinder(MovieFinder movieFinder) {
      this.movieFinder = movieFinder;
    }
}

此外还可使用java8里面提供的java.util.Optional:

public class SimpleMovieLister {
    @Autowired
    public void setMovieFinder(Optional<MovieFinder> movieFinder) {
    }
}

在Spring5.0中还可以使用JSR305的javax.annotation.Nullable:

public class SimpleMovieLister {
    @Autowired
    public void setMovieFinder(@Nullable MovieFinder movieFinder) {
    }
}

此外,可以用Autowired直接注入BeanFactory, ApplicationContext, Environment, ResourceLoader, ApplicationEventPublisher, and MessageSource,还有他们的扩展接口:ConfigurableApplicationContext、ResourcePatternResolver。

需要注意的是:Autowired, Inject, Value, 和 Resource 都是由Spring的BeanPostProcessor来处理的(其中Autowired和Value是由AutowiredAnnotationBeanPostProcessor来处理的),因此,在你自己定义的BeanPostProcessor或者BeanFactoryPostProcessor里面是不可以使用这些注解的,要么使用xml要么使用Bean。

那么构造函数注入与setter方法注入该如何选择呢?

一般来说,如果不能为空必须要注入的,使用构造函数注入,允许为空可以不注入的则可以选择setter方法注入。

Primary

当多个候选bean都满足注入条件的时候,Primary标记的bean的优先级更高,比如:

@Configuration
public class DemoApp {
  @Bean
  public IService service1(){
    return new Service1();
  }
  @Bean
  public IService service2(){
    return new Service2();
  }
  @Bean
  public Service3 service3(IService iService){
    return new Service3(iService);
  }
}

Service3在注入IService的时候就会报错,因为容器中有2个IService:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type 'com.github.xjs.iocdemo.injectanno.IService' available:
expected single matching bean but found 2: service1,service2

可以在service1()或者service2()任意一个上添加Primary得以解决,如下:

@Bean
@Primary
public IService service2(){
  return new Service2();
}

Qualifier

可以用一个标识符在多个候选的bean中选择注入的bean,默认的标识符是bean的名字。

因此前一个例子也可以这样来解决:

public Service3 service3(@Qualifier("service2") IService iService){
    return new Service3(iService);
}

需要说明的是,Qualifier标识符不需要全局唯一,因为Autowired主要还是按照类型来进行注入的,如果多个Qualifier都满足,还是可以继续使用Primary之类的注解的,并且这多个Qualifier还可以一块注入到集合中:

@Configuration
@ComponentScan
public class DemoApp {
  @Bean
  @Qualifier("s1")
  public IService service1(){
    return new Service1();
  }
  @Bean
  @Qualifier("s1")
  @Primary
  public IService service2(){
    return new Service2();
  }
  @Bean
  public Service3 service3(@Qualifier("s1") IService iService){
    return new Service3(iService);
  }
}
@Service
public class Service4 {
  @Autowired
  @Qualifier("s1")
  private Set<IService> serviceSet;
  }
}

还可以把泛型类型当成是一种限定符:

public interface IService<T> {
}
public class IntegerService implements IService<Integer> {
}
public class StringService implements IService<String> {
}
@Component
public class Service6 {
  /**这里注入的是StringService*/
  @Autowired
  private IService<String> s1;
  /**这里注入的是IntegerService*/
  @Autowired
  private IService<Integer> s2;
  public IService<String> getS1(){
    return s1;
  }
  public IService<Integer> getS2(){
    return s2;
  }
}

Resource

JSR250注解,它是优先按照bean的名字进行注入,由CommonAnnotationBeanPostProcessor进行处理。

展开阅读全文

本文系作者在时代Java发表,未经许可,不得转载。

如有侵权,请联系nowjava@qq.com删除。

编辑于

关注时代Java

关注时代Java