Java:学习@Autowired

概要

本文记录一下对于Java的@Autowired注解的学习。

博客

原贴收藏在IT老兵驿站

前言

使用SpringMVC,现在不可避免要接触注解,会遇到@Autowired,查询了google,找到这篇文章,摘录下来,做一些笔记。为什么这样呢?因为至少来说,以当前的理解,自己来写,不太可能超越这篇文章,我也不想像很多人那样搞所谓“二次原创”,其实不还是抄袭,只是针对了判定抄袭的规则来做文章,不耍这样的小聪明,没有意义。

本篇选取主要的部分翻译一下,具体的过程可以参考代码,本文的英文难度不高,可以尝试着读一读。

正文

  1. Overview
    Starting with Spring 2.5, the framework introduced a new style of Dependency Injection driven by @Autowired Annotations. This annotation allows Spring to resolve and inject collaborating beans into your bean.


    In this tutorial, we will look at how to enable autowiring, various ways to wire in beans, making beans optional, resolving bean conflicts using @Qualifier annotation along with potential exception scenarios.

Spring2.5之后,可以使用@Autowired 注解来实现DI(依赖注入),这个词本身的英文意思就是自动装配。

  1. Enabling @Autowired Annotations
    If you are using Java based configuration in your application you can enable annotation-driven injection by using AnnotationConfigApplicationContext to load your spring configuration as below:
@Configuration    
@ComponentScan("com.baeldung.autowire.sample")
public class AppConfig {}

As an alternative, in Spring XML, it can be enabled by declaring it in
Spring XML files like so: context:annotation-config/

想使用注解,现需要配置Spring可以支持注解,有两种方式,一个是在代码中,一个是在XML中,这个涉及到另外一些知识点,作者这里是假设读者是知道这些的,不明白的话,需要去查一查。

  1. Using @Autowired Once annotation injection is enabled, autowiring can be used on properties, setters, and constructors.

可以用在属性、setter方法和构造器上。

3.1. @Autowired on Properties The annotation can be used directly on properties, therefore eliminating the need for getters and setters:

@Component("fooFormatter")
public class FooFormatter {
    public String format() {
        return "foo";
    }
}

@Component
public class FooService {

    @Autowired
    private FooFormatter fooFormatter;
}

In the above example, Spring looks for and injects fooFormatter when FooService is created.

上面的例子介绍了@Autowired如何用在属性上。

3.2. @Autowired on Setters
The @Autowired annotation can be used on setter methods. In the below example, when the annotation is used on the setter method, the setter method is called with the instance of FooFormatter when FooServiceis created:

public class FooService {

    private FooFormatter fooFormatter;

    @Autowired
    public void setFooFormatter(FooFormatter fooFormatter) {
            this.fooFormatter = fooFormatter;
    }
}

上面讲了如何用在方法上。

3.3. @Autowired on Constructors
The @Autowired annotation can also be used on constructors. In the below example, when the annotation is used on a constructor, an instance of FooFormatter is injected as an argument to the constructor when FooService is created:

public class FooService {

    private FooFormatter fooFormatter;

    @Autowired
    public FooService(FooFormatter fooFormatter) {
        this.fooFormatter = fooFormatter;
    }
}

如何用在构造器方法上。

  1. @Autowired and Optional Dependencies
    Spring expects @Autowired dependencies to be available when the dependent bean is being constructed. If the framework cannot resolve a bean for wiring, it will throw the below-quoted exception and prevent the Spring container from launching successfully:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.autowire.sample.FooDAO] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this dependency.

Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true)}
To avoid this from happening, a bean can optional be specified as below:

public class FooService {
    @Autowired(required = false)
    private FooDAO dataAccessor;
}

如果没有找到响应的bean,又不想系统停止加载,参考上面的写法。

  1. Autowire Disambiguation
    By default, Spring resolves @Autowired entries by type. If more than one beans of the same type are available in the container, the framework will throw a fatal exception indicating that more than one bean is available for autowiring.

@Autowired是根据类型来进行装备的。但是会存在同一类型内有多个备选bean,这个时候,框架会抛出一个致命错误—-这种问题倒是暂时还没有遇到过,下面讲述了三种解决方案。

5.1. Autowiring by @Qualifier
The @Qualifier annotation can be used to hint at and narrow down the required bean:

@Component("fooFormatter")
public class FooFormatter implements Formatter {
    public String format() {
        return "foo";
    }
}

@Component("barFormatter")
public class BarFormatter implements Formatter {
    public String format() {
        return "bar";
    }
}

public class FooService {

    @Autowired
    private Formatter formatter;
}

Since there are two concrete implementations of Formatter available for the Spring container to inject, Spring will throw a NoUniqueBeanDefinitionException exception when constructing the FooService:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type [com.autowire.sample.Formatter] is defined:
expected single matching bean but found 2: barFormatter,fooFormatter

This can be avoided by narrowing the implementation using a @Qualifier annotation:

public class FooService {

    @Autowired
    @Qualifier("fooFormatter")
    private Formatter formatter;
}

By specifying the @Qualifier with the name of the specific implementation, in this case as fooFormatter, we can avoid ambiguity when Spring finds multiple beans of the same type.


Please note that the value of the @Qualifier annotation matches with the name declared in the @Component annotation of our FooFormatter implementation.

使用@Qualifier 注解来标识谁是合格的。

5.2. Autowiring by Custom Qualifier
Spring allows us to create our own @Qualifier annotation. To create a custom Qualifier, define an annotation and provide the @Qualifier annotation within the definition as below:

@Qualifier
@Target({ElementType.FIELD, ElementType.METHOD,ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface FormatterType {
    String value();
}

Once defined, the FormatterType can be used within various implementations to specify custom value:

@FormatterType("Foo")
@Component
public class FooFormatter implements Formatter {

    public String format() {
        return "foo";
    }
}

@FormatterType("Bar")
@Component
public class BarFormatter implements Formatter {
    public String format() {
        return "bar";
    }
}

Once the implementations are annotated, the custom Qualifier annotation can be used as below:

@Component
public class FooService {

    @Autowired
    @FormatterType("Foo")
    private Formatter formatter;
}

The value specified in the @Target annotation restrict where the qualifier can be used to mark injection points.


In the above code snippet, the qualifier can be used to disambiguate the point where Spring can inject the bean into a field, a method, a type, and a parameter.

5.3. Autowiring by Name
As a fallback Spring uses the bean name as a default qualifier value.


So by defining the bean property name, in this case as fooFormatter, Spring matches that to the FooFormatter implementation and injects that specific implementation when FooService is constructed:

public class FooService {

    @Autowired
    private Formatter fooFormatter;
}

使用名字来自动装配。

  1. Conclusion
    Although both @Qualifier and bean name fallback match can be used to narrow down to a specific bean, autowiring is really all about injection by type and this is how best to use this container feature.


    The source code of this tutorial can be found in the GitHub project – this is an Eclipse based project, so it should be easy to import and run as it is.

总结

老外的文章讲的真清楚,他们会对读者负责,很认真地把所有问题讲清楚,不像很多国人,哪怕是很多好像挺有名的教授出的书,都是“言简意赅”,看着好费劲,给人一种高高在上的感觉。当年读清华严蔚敏出的《数据结构》,读着就非常费劲,后来看大师出的《算法导论》,反而容易理解,中国人怎么总就是这么高傲呢。

参考

https://www.baeldung.com/spring-autowire

https://stackoverflow.com/questions/1018797/can-you-use-autowired-with-static-fields 介绍了静态域能不能自动装配