spring框架官网直译IOC容器(1.9-1.12)

1.9基于注解的容器配置

对于配置Spring,注释是否优于XML ?

基于注释的配置的引入提出了这样一个问题:这种方法是否优于XML。简而言之,这要视情况而定。长话短说,每种方法都有其优缺点,通常由开发人员决定哪种策略更适合他们。由于它们的定义方式,注释在声明中提供了大量上下文,从而导致更短、更简洁的配置。然而,XML擅长在不接触组件源代码或重新编译它们的情况下连接组件。一些开发人员喜欢将连接靠近源,而另一些开发人员则认为带注释的类不再是pojo,而且配置变得分散,更难控制。
无论选择哪一种,Spring都可以同时容纳两种风格,甚至可以将它们混合在一起。值得指出的是,通过它的JavaConfig选项,Spring允许以一种非侵入性的方式使用注释,而不需要接触目标组件源代码,而且就工具而言,Spring工具套件支持所有配置样式。

XML设置的另一种替代方法是基于注释的配置,它依赖于字节码元数据来连接组件,而不是角括号声明。开发人员不使用XML描述bean连接,而是通过使用相关类、方法或字段声明上的注释将配置移动到组件类本身。正如在示例中提到的:RequiredAnnotationBeanPostProcessor中,结合使用BeanPostProcessor和注释是扩展Spring IoC容器的常见方法。例如,Spring 2.0引入了使用@Required注释强制执行所需属性的可能性。Spring 2.5使得使用相同的通用方法来驱动Spring的依赖注入成为可能。本质上,@Autowired注释提供了与Autowiring协作者中描述的相同的功能,但是具有更细粒度的控制和更广泛的适用性。Spring 2.5还添加了对JSR-250注释的支持,比如@PostConstruct和@PreDestroy。Spring 3.0增加了对javax中包含的JSR-330 (Java依赖注入)注释的支持。注入包,如@Inject和@Named。有关这些注释的详细信息可以在相关部分中找到。

注释注入在XML注入之前执行。因此,XML配置覆盖了通过这两种方法连接的属性的注释。

与往常一样,您可以将它们注册为单独的bean定义,但是也可以通过在基于xml的Spring配置中包含以下标记来隐式注册它们(请注意包含了上下文名称空间):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

</beans>

(隐式注册的后处理器包括AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor和前面提到的RequiredAnnotationBeanPostProcessor)。
<context:annotation-config/>只在定义bean的应用程序上下文中查找bean上的注释。这意味着,如果将<context:annotation-config/>放在DispatcherServlet的WebApplicationContext中,它只检查控制器中的@Autowired bean,而不检查服务。有关更多信息,请参见DispatcherServlet。

1.9.1@ required
@Required注释应用于bean属性设置器方法,如下面的示例所示:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    // ...
}

该注释指出,必须在配置时通过bean定义中的显式属性值或自动装配填充受影响的bean属性。如果未填充受影响的bean属性,容器将引发异常。这允许出现紧急而显式的故障,避免稍后出现NullPointerException实例或类似的情况。我们仍然建议将断言放入bean类本身(例如,放入init方法)。即使在容器外部使用该类,这样做也会强制执行那些必需的引用和值。
从Spring Framework 5.1开始,@Required注释就被正式弃用,而倾向于为所需的设置使用构造函数注入(或InitializingBean.afterPropertiesSet()和bean属性设置器方法的自定义实现)。
1.9.2. Using @Autowired
在本节包含的示例中,JSR 330的@Inject注释可以代替Spring的@Autowired注释。详情请看这里。
You can apply the @Autowired annotation to constructors, as the following example shows:

 public class MovieRecommender {
    
        private final CustomerPreferenceDao customerPreferenceDao;
    
        @Autowired
        public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
            this.customerPreferenceDao = customerPreferenceDao;
        }
    
        // ...
    }

从Spring Framework 4.3开始,如果目标bean只定义一个构造函数,那么就不再需要在这样的构造函数上使用@Autowired注释。但是,如果有多个构造函数可用,则必须至少注释一个构造函数,以便教会容器使用哪个构造函数。
You can also apply the @Autowired annotation to “traditional” setter methods, as the following example shows:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    // ...
}

您还可以将注释应用于具有任意名称和多个参数的方法,如下例所示:

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

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

    // ...
}

You can apply @Autowired to fields as well and even mix it with constructors, as the following example shows:

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    private MovieCatalog movieCatalog;

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

    // ...
}

确保您的目标组件(例如MovieCatalog或CustomerPreferenceDao)是由@ autowire -annotated注入点使用的类型一致声明的。否则,由于在运行时没有找到类型匹配,注入可能会失败。
对于通过类路径扫描找到的xml定义的bean或组件类,容器通常预先知道具体的类型。但是,对于@Bean工厂方法,您需要确保声明的返回类型具有足够的表现力。对于实现多个接口的组件或可能由其实现类型引用的组件,请考虑在工厂方法上声明最特定的返回类型(至少与引用bean的注入点所要求的一样具体)。
您还可以通过向期望该类型数组的字段或方法添加注释来从ApplicationContext提供特定类型的所有bean,如下面的示例所示:

public class MovieRecommender {

    @Autowired
    private MovieCatalog[] movieCatalogs;

    // ...
}

类型化集合也是如此,如下例所示:

public class MovieRecommender {

    private Set<MovieCatalog> movieCatalogs;

    @Autowired
    public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }

    // ...
}

您的目标bean可以实现org.springframework.core。如果希望数组或列表中的项按照特定的顺序排序,可以使用@Order或标准的@Priority注释。否则,它们的顺序遵循容器中相应目标bean定义的注册顺序。
您可以在目标类级别和@Bean方法上声明@Order注释,这可能是通过单个bean定义实现的(如果多个定义使用相同的bean类)。@Order值可能会影响注入点的优先级,但是要注意它们不会影响单例启动顺序,这是由依赖关系和@DependsOn声明决定的正交关系。
注意,标准javax.annotation.Priority注释不能在@Bean级别上使用,因为它不能在方法上声明。它的语义可以通过@Order值与针对每种类型的单个bean上的@Primary组合来建模。
即使是类型化的Map实例,只要预期的键类型是String,也可以自动生成。Map值包含所有期望类型的bean,键包含相应的bean名称,如下例所示:

public class MovieRecommender {

    private Map<String, MovieCatalog> movieCatalogs;

    @Autowired
    public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }

    // ...
}

默认情况下,当给定注入点没有匹配的候选bean可用时,自动装配将失败。对于声明的数组、集合或映射,至少需要一个匹配的元素。
默认行为是将带注释的方法和字段视为指示所需依赖项。你可以改变这个行为,如下面的例子所示,让框架跳过一个不满足的注入点,通过标记它为非必需的:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    // ...
}

如果非必需方法的依赖项(或者多个参数的依赖项之一)不可用,则根本不会调用该方法。在这种情况下,完全不会填充非必需字段,只保留其默认值。
注入构造函数和工厂方法参数是一种特殊情况,因为@Autowired上的“required”标志有一些不同的含义,因为Spring的构造函数解析算法可能要处理多个构造函数。默认情况下,构造函数和工厂方法参数实际上是必需的,但是在单构造函数场景中有一些特殊规则,比如多元素注入点(数组、集合、映射)在没有匹配bean可用时解析为空实例。这允许一种通用的实现模式,其中所有依赖项都可以在一个惟一的多参数构造函数中声明,例如声明为一个没有@Autowired注释的公共构造函数。

**每个类只能标记一个带注释的构造函数,但是可以标记多个非必需的构造函数。在这种情况下,每个参数都在候选函数中考虑,Spring使用最贪婪的构造函数,它的依赖关系可以得到满足,也就是说,构造函数的参数数量最多。构造函数解析算法与使用重载构造函数的非带注释类相同,只是将候选项缩小到带注释的构造函数。
与setter方法上的@Required注释相比,推荐使用@Autowired的’required’属性。“required”属性表示该属性不用于自动装配目的。如果不能自动生成,则忽略该属性。另一方面,@Required更强大,因为它强制使用容器支持的任何方法来设置属性。如果没有定义值,则引发相应的异常。

或者,您可以通过Java 8的Java .util来表示特定依赖项的非必需性质。可选,如下例所示:

public class SimpleMovieLister {

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

从Spring Framework 5.0开始,还可以使用@Nullable注释(任何包中的任何类型的注释——例如javax.annotation.Nullable)from jsr - 305 :

public class SimpleMovieLister {

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

您还可以将@Autowired用于众所周知的可解析依赖关系的接口:BeanFactory、ApplicationContext、Environment、ResourceLoader、ApplicationEventPublisher和MessageSource。这些接口及其扩展接口(如ConfigurableApplicationContext或ResourcePatternResolver)将自动解析,无需特殊设置。下面的示例自动拖拽一个ApplicationContext对象:

public class MovieRecommender {

    @Autowired
    private ApplicationContext context;

    public MovieRecommender() {
    }

    // ...
}

@Autowired、@Inject、@Value和@Resource注释由Spring BeanPostProcessor实现处理。这意味着您不能在自己的BeanPostProcessor或BeanFactoryPostProcessor类型(如果有的话)中应用这些注释。必须使用XML或Spring @Bean方法显式地“连接”这些类型。

1.9.3 使用@Primary微调基于注释的自动装配
由于按类型自动装配可能导致多个候选项,因此通常需要对选择过程有更多的控制。实现这一点的一种方法是使用Spring的@Primary注释。@Primary表示,当多个bean是自动生成单值依赖项的候选bean时,应该优先考虑特定bean。如果在候选bean中只存在一个主bean,那么它将成为自动获取的值。
考虑下面的配置,它将firstMovieCatalog定义为主MovieCatalog:

@Configuration
public class MovieConfiguration {

    @Bean
    @Primary
    public MovieCatalog firstMovieCatalog() { ... }

    @Bean
    public MovieCatalog secondMovieCatalog() { ... }

    // ...
}

使用前面的配置,下面的MovieRecommender将与第一个moviecatalog自动同步:

public class MovieRecommender {

    @Autowired
    private MovieCatalog movieCatalog;

    // ...
}

对应的bean定义如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog" primary="true">
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

1.9.4使用限定符微调基于注释的自动装配
@Primary是一种根据类型使用自动装配的有效方法,当可以确定一个主要候选对象时,可以在多个实例中使用自动装配。当需要对选择过程进行更多控制时,可以使用Spring的@Qualifier注释。您可以将限定符值与特定的参数关联起来,缩小类型匹配集,以便为每个参数选择特定的bean。在最简单的情况下,这可以是一个简单的描述性值,如下例所示:

public class MovieRecommender {

    @Autowired
    @Qualifier("main")
    private MovieCatalog movieCatalog;

    // ...
}

您还可以在单个构造函数参数或方法参数上指定@Qualifier注释,如下面的示例所示:

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

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

    // ...
}

The following example shows corresponding bean definitions.
<?xml version="1.0" encoding="UTF-8"?>

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="main"/> 

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="action"/> 

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

具有main限定符值的bean与构造函数参数连接,构造函数参数使用相同的值进行限定。
带有action限定符值的bean与构造函数参数连接,构造函数参数使用相同的值进行限定。

对于回退匹配,bean名称被认为是默认的限定符值。因此,您可以使用main的id来定义bean,而不是使用嵌套的qualifier元素,从而得到相同的匹配结果。然而,尽管您可以使用这个约定通过名称引用特定的bean,但是@Autowired基本上是关于类型驱动的注入,带有可选的语义限定符。这意味着,即使使用bean名称回退,限定符值在类型匹配集中也总是具有收缩语义。他们没有语义表达独特的bean的引用id。良好的限定符值主要EMEA或持久,表达某个特定组件的特点是独立于bean id,这可能是在匿名的情况下自动生成的bean定义等在前面的一个例子。
限定符也适用于类型化集合,如前所述,用于设置。在这种情况下,根据声明的限定符,所有匹配的bean都作为一个集合注入。这意味着限定符不必是惟一的。相反,它们构成过滤标准。例如,可以使用相同的限定符值action定义多个MovieCatalog bean,所有这些bean都被注入到一个集合中,该集合使用@Qualifier(“action”)注释。

**在类型匹配的候选项中,让限定符值根据目标bean名称进行选择,不需要在注入点使用@Qualifier注释。如果没有其他解析指示器(例如限定符或主标记),对于非惟一依赖关系,Spring将针对目标bean名称匹配注入点名称(即字段名称或参数名称),并选择同名的候选项(如果有的话)。也就是说,如果您打算通过名称来表示注释驱动的注入,那么不要主要使用@Autowired,即使它能够通过bean名称在类型匹配的候选项中进行选择。相反,使用JSR-250 @Resource注释,它的语义定义是通过特定的名称来标识特定的目标组件,声明的类型与匹配过程无关。@Autowired具有相当不同的语义:在按类型选择候选bean之后,指定的字符串限定符值只在那些类型选择的候选对象中被考虑(例如,将一个帐户限定符与标记有相同限定符标签的bean相匹配)。对于本身定义为集合、映射或数组类型的bean, @Resource是一个很好的解决方案,它通过惟一的名称引用特定的集合或数组bean。也就是说,从4.3开始,只要元素类型信息保存在@Bean返回类型签名或集合继承层次结构中,就可以通过Spring’s @Autowired类型匹配算法匹配映射和数组类型。在本例中,可以使用限定符值在相同类型的集合中进行选择,如上一段所述。在4.3中,@Autowired还考虑了注入的自引用(也就是说,返回到当前注入的bean的引用)。注意,自我注入是一种退路。对其他组件的常规依赖始终具有优先级。从这个意义上说,自我参照不参与常规的候选人选择,因此也就不是主要的。相反,它们总是以最低的优先级结束。在实践中,您应该只使用self引用作为最后的手段(例如,通过bean的事务代理调用同一实例上的其他方法)。考虑在这种情况下将受影响的方法分解到单独的委托bean。或者,您可以使用@Resource,它可以通过当前bean的唯一名称获得一个代理。@Autowired适用于字段、构造函数和多参数方法,允许通过参数级别的限定符注释来缩小范围。相反,@Resource只支持具有单个参数的字段和bean属性设置器方法。因此,如果您的注入目标是构造函数或多参数方法,则应该坚持使用限定符。

您可以创建自己的自定义限定符注释。为此,定义一个注释并在定义中提供@Qualifier注释,如下面的示例所示:

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {

    String value();
}

然后,您可以在自动获取的字段和参数上提供自定义限定符,如下面的示例所示:

public class MovieRecommender {

    @Autowired
    @Genre("Action")
    private MovieCatalog actionCatalog;

    private MovieCatalog comedyCatalog;

    @Autowired
    public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
        this.comedyCatalog = comedyCatalog;
    }

    // ...
}

接下来,您可以为候选bean定义提供信息。您可以添加<qualifier/> 作为<bean/>标记的子元素,然后指定类型和值来匹配您的自定义限定符注释。类型与注释的完全限定类名匹配。另外,如果不存在名称冲突的风险,为了方便起见,可以使用简短的类名。下面的例子演示了这两种方法:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="Genre" value="Action"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="example.Genre" value="Comedy"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

在类路径扫描和托管组件中,您可以看到一个基于注释的替代方法,以XML提供限定符元数据。具体地说,请参见使用注释提供限定符元数据。
在某些情况下,使用没有值的注释就足够了。当注释服务于更一般的目的,并且可以跨几种不同类型的依赖项应用时,这将非常有用。例如,您可以提供一个离线目录,当没有Internet连接可用时,可以搜索该目录。首先,定义简单的注释,如下例所示:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {

}

然后将注释添加到要自动装配的字段或属性中,如下例所示:

public class MovieRecommender {

    @Autowired
    @Offline 
    private MovieCatalog offlineCatalog;

    // ...
}

现在bean定义只需要限定符类型,如下面的例子所示:

<bean class="example.SimpleMovieCatalog">
    <qualifier type="Offline"/>  此元素指定限定符。
    <!-- inject any dependencies required by this bean -->
</bean>
您还可以定义自定义限定符注释,除简单VALUE属性外,这些注释还接受已命名的属性。如果在要自动装配的字段或参数上指定了多个属性值,则bean定义必须匹配所有这些属性值,才能被视为自动装配候选。例如,考虑以下注释定义:
    @Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {

    String genre();

    Format format();
}

In this case Format is an enum, defined as follows:

public enum Format {
    VHS, DVD, BLURAY
}

要自动装配的字段用自定义限定符标注,并包含两个属性的值:genre and format, as the following example shows:

public class MovieRecommender {

    @Autowired
    @MovieQualifier(format=Format.VHS, genre="Action")
    private MovieCatalog actionVhsCatalog;

    @Autowired
    @MovieQualifier(format=Format.VHS, genre="Comedy")
    private MovieCatalog comedyVhsCatalog;

    @Autowired
    @MovieQualifier(format=Format.DVD, genre="Action")
    private MovieCatalog actionDvdCatalog;

    @Autowired
    @MovieQualifier(format=Format.BLURAY, genre="Comedy")
    private MovieCatalog comedyBluRayCatalog;

    // ...
}

最后,bean定义应该包含匹配的限定符值。这个例子还演示了您可以使用bean元属性来代替<qualifier/> elements. 如果可用,则优先使用元素及其属性,但如果不存在标记中提供的限定符,则自动装配机制将返回到标记中提供的值,如下例中的最后两个bean定义所示:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="MovieQualifier">
            <attribute key="format" value="VHS"/>
            <attribute key="genre" value="Action"/>
        </qualifier>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier type="MovieQualifier">
            <attribute key="format" value="VHS"/>
            <attribute key="genre" value="Comedy"/>
        </qualifier>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <meta key="format" value="DVD"/>
        <meta key="genre" value="Action"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <meta key="format" value="BLURAY"/>
        <meta key="genre" value="Comedy"/>
        <!-- inject any dependencies required by this bean -->
    </bean>

</beans>

1.9.5使用泛型作为自动装配限定符
In addition to the @Qualifier annotation, 您可以使用Java泛型类型作为一种隐式的限定形式。例如,假设您有以下配置:

@Configuration
public class MyConfiguration {

    @Bean
    public StringStore stringStore() {
        return new StringStore();
    }

    @Bean
    public IntegerStore integerStore() {
        return new IntegerStore();
    }
}

假设前面的bean实现了一个泛型接口(即Store和Store),您可以@Autowire Store接口,并将泛型用作限定符,如下面的示例所示:

@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean

@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean

通用限定符也适用于自动装配列表、MAP实例和数组。下面的示例自动拖拽一个泛型列表:

// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;

1.9.6. Using CustomAutowireConfigurer 使用 自定义自动装配装配器
customautowirefigurer是一个BeanFactoryPostProcessor,它允许您注册自己的自定义限定符注释类型,即使它们没有使用Spring s @Qualifier注释进行注释。下面的示例展示了如何使用customautowirefigurer:

<bean id="customAutowireConfigurer"
        class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
    <property name="customQualifierTypes">
        <set>
            <value>example.CustomQualifier</value>
        </set>
    </property>
</bean>

AutowireCandidateResolver决定自动装配的候选人:
每个bean定义的autowire-candidate价值
在< bean / >元素可用的任何default-autowire-candidates模式
@ qualifier注解的存在和用CustomAutowireConfigurer注册的任何自定义注释

当多个bean符合自动装配候选人时,一个 primary的确定如下:如果候选项中的一个bean定义的 primary属性设置为true,则选择它。

1.9.7. Injection with @Resource
Spring还通过在字段或bean属性设置器方法上使用JSR-250 @Resource注释(javax.annotation.Resource)支持注入。这是Java EE中的一个常见模式:例如,在jsf管理的bean和JAX-WS端点中。Spring也支持Spring管理对象的这种模式。
@Resource接受name属性。默认情况下,Spring将该值解释为要注入的bean名称。换句话说,它遵循名称语义,如下例所示:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource(name="myMovieFinder") 
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

如果没有显式指定名称,则默认名称派生自字段名称或setter方法。对于字段,它接受字段名。对于setter方法,它采用bean属性名。下面的例子将把名为movieFinder的bean注入到它的setter方法中:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

**注释提供的名称由CommonAnnotationBeanPostProcessor知道的ApplicationContext解析为bean名称。如果显式配置Spring的SimpleJndiBeanFactory,则可以通过JNDI解析这些名称。但是,我们建议您依赖缺省行为并使用Spring的JNDI查找功能来保持间接级别。

在没有指定显式名称的@Resource usage(类似于@Autowired)的独占情况下,@Resource发现一个主类型匹配,而不是一个特定的命名bean,并解决众所周知的可解析依赖关系:BeanFactory、ApplicationContext、ResourceLoader、ApplicationEventPublisher和MessageSource接口。因此,在下面的示例中,customerPreferenceDao字段首先查找名为“customerPreferenceDao”的bean,然后返回到customerPreferenceDao类型的主类型匹配:

public class MovieRecommender {

    @Resource
    private CustomerPreferenceDao customerPreferenceDao;

    @Resource
    private ApplicationContext context; 

    public MovieRecommender() {
    }

    // ...
}

The context field is injected based on the known resolvable dependency type: ApplicationContext.
1.9.8. Using @PostConstruct and @PreDestroy
CommonAnnotationBeanPostProcessor不仅可以识别@Resource注释,还可以识别JSR-250生命周期注释:javax.annotation。PostConstruct javax.annotation.PreDestroy。在Spring 2.5中引入的对这些注释的支持为初始化回调和销毁回调中描述的生命周期回调机制提供了一种替代方法。如果CommonAnnotationBeanPostProcessor是在Spring ApplicationContext中注册的,则在生命周期的同一点上调用一个带有这些注释之一的方法,该方法与相应的Spring生命周期接口方法或显式声明的回调方法处于同一位置。在下面的示例中,缓存在初始化时被预填充,在销毁时被清除:

 public class CachingMovieLister {

    @PostConstruct
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
    }

    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }
}

有关组合各种生命周期机制的效果的详细信息see Combining Lifecycle Mechanisms.

与@Resource一样,@PostConstruct和@PreDestroy注释类型也是JDK 6到8标准Java库的一部分。但是,整个javax。注释包与JDK 9中的核心Java模块分离,最终在JDK 11中被删除。如果需要,可以使用javax.annotation-api组件,现在需要通过Maven Central获得,只需像添加任何其他库一样添加到应用程序的类路径。

1.10. Classpath Scanning and Managed Components

本章中的大多数示例使用XML指定配置元数据,这些元数据在Spring容器中生成每个bean定义。上一节(基于注释的容器配置)演示了如何通过源级注释提供大量配置元数据。然而,即使在这些示例中,基本bean定义也是在XML文件中显式定义的,而注释只驱动依赖项注入。本节描述通过扫描类路径隐式检测候选组件的选项。候选组件是与筛选条件匹配的类,并且具有与容器注册的相应bean定义。这样就不需要使用XML来执行bean注册。相反,您可以使用注释(例如@Component)、AspectJ类型表达式或您自己的自定义筛选条件来选择哪些类具有在容器中注册的bean定义。
1.10.1. @Component and Further Stereotype Annotations进一步构造型注解
@Repository注释是任何满足存储库角色或原型(也称为数据访问对象或DAO)的类的标记。该标记的用途之一是自动翻译异常,如异常翻译中所述。
Spring提供了更多的原型注解:@Component, @Service和@Controller。@Component是任何spring管理组件的通用构造型。@Repository、@Service和@Controller是@Component针对更特定用例的专门化(分别在持久性层、服务层和表示层)。因此,您可以使用@Component来注释组件类,但是,通过使用@Repository、@Service或@Controller来注释它们,您的类更适合通过工具进行处理或与方面关联。例如,这些原型注释是切入点的理想目标。在Spring框架的未来版本中,@Repository、@Service和@Controller还可以包含额外的语义。因此,如果您在使用@Component或@Service作为服务层之间进行选择,@Service显然是更好的选择。类似地,如前所述,@Repository已经被支持作为持久层中自动异常转换的标记。
1.10.2. Using Meta-annotations and Composed Annotations
使用元注释和组合注释
Spring提供的许多注释都可以在您自己的代码中用作元注释。元注释是可以应用于其他注释的注释。例如,前面提到的@Service注释是用@Component进行元注释的,如下例所示:

   @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component 
public @interface Service {

    // ....
}

The Component 导致@Service被以与@Component相同的方式对待。

您还可以组合元注释来创建组合注释。例如,Spring MVC中的@RestController注释由@Controller和@ResponseBody组成。此外,组合注释可以选择性地从元注释重新声明属性,以允许自定义。当您只想公开元注释属性的子集时,这尤其有用。例如,Spring s @SessionScope注释将范围名称硬编码到会话,但仍然允许定制proxyMode。下面的清单显示了SessionScope注释的定义:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {

/**
 * Alias for {@link Scope#proxyMode}.
 * <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
 */
@AliasFor(annotation = Scope.class)
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;

}
然后可以使用@SessionScope,而无需声明proxyMode,如下所示:

@Service
@SessionScope
public class SessionScopedService {
    // ...
}
您还可以覆盖proxyMode的值,如下面的示例所示:
    @Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {
    // ...
}

1.10.3. Automatically Detecting Classes and Registering Bean Definitions自动检测类和注册bean定义
Spring可以自动检测原型类,并在ApplicationContext中注册相应的bean定义实例。例如,以下两个类可以进行自动检测:

@Service
public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

@Repository
public class JpaMovieFinder implements MovieFinder {
    // implementation elided for clarity
}

要自动检测这些类并注册相应的bean,需要将@ComponentScan添加到@Configuration类中,其中basePackages属性是这两个类的公共父包。(或者,您可以指定一个逗号或分号或空格分隔的列表,其中包含每个类的父包):

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
    ...
}

下面的替代方法使用XML:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="org.example"/>

</beans>

The use of context:component-scan implicitly enables the functionality of 隐式启用context:annotation-config的功能. There is usually no need to include the context:annotation-config element when using context:component-scan.

** 扫描类路径包需要类路径中存在相应的目录条目。当您使用Ant构建JAR时,请确保您没有激活JAR任务的file -only开关。此外,在某些环境中,类路径目录可能不会基于安全策略公开,例如,JDK 1.7.0_45或更高版本上的独立应用程序(这需要在清单中设置“可信库”,请参见https://stackoverflow.com/questions/19394570/java-jre-7u45-break -classloader-getresources)。
在JDK 9的模块路径(Jigsaw)上,Spring的类路径扫描通常按照预期工作。不过,请确保在模块信息module-info描述符中导出了组件类。如果您希望Spring调用类的非公共成员,请确保它们是“打开的”(也就是说,它们在您的module-info描述符中使用open声明而不是export声明)。

此外,在使用组件扫描元素时,AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor都是隐式包含的。这意味着这两个组件是自动检测和连接在一起的——所有这些都不需要用XML提供任何bean配置元数据。
You can disable the registration of AutowiredAnnotationBeanPostProcessor and CommonAnnotationBeanPostProcessor by including the annotation-config attribute with a value of false
1.10.4. Using Filters to Customize Scanning使用过滤器自定义扫描
默认情况下,使用@Component注释的类、使用@Repository注释的类、使用@Service注释的类、使用@Controller注释的类或使用@Component注释的自定义注释是唯一检测到的候选组件。但是,您可以通过应用自定义过滤器来修改和扩展此行为。将它们添加为@ComponentScan注释的includeFilters或excludeFilters参数(或作为component-scan元素的include-filter或excludefilter子元素)。每个筛选器元素都需要类型和表达式属性。下表描述了过滤选项:
在这里插入图片描述
@Configuration
@ComponentScan(basePackages = “org.example”,
includeFilters = @Filter(type = FilterType.REGEX, pattern = “.*Stub.*Repository”),
excludeFilters = @Filter(Repository.class))
public class AppConfig {

}
The following listing shows the equivalent XML:

<beans>
    <context:component-scan base-package="org.example">
        <context:include-filter type="regex"
                expression=".*Stub.*Repository"/>
        <context:exclude-filter type="annotation"
                expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
</beans>

ou can also disable the default filters by setting useDefaultFilters=false on the annotation or by providing use-default-filters=“false” as an attribute of the <component-scan/> element. This, in effect, disables automatic detection of classes annotated with @Component, @Repository, @Service, @Controller, or @Configuration.
1.10.5. Defining Bean Metadata within Components在组件中定义bean元数据
Spring组件还可以向容器提供bean定义元数据。您可以使用@Configuration注释类中用于定义bean元数据的@Bean注释来实现这一点。下面的例子说明了如何做到这一点:

@Component
public class FactoryMethodComponent {

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    public void doWork() {
        // Component method implementation omitted
    }
}
前面的类是一个Spring组件,它的doWork()方法中有特定于应用程序的代码。但是,它还提供了一个bean定义,其中包含一个引用publicInstance()方法的工厂方法。@Bean注释标识工厂方法和其他bean定义属性,例如通过@Qualifier注释标识限定符值。可以指定的其他方法级注释有@Scope、@Lazy和定制限定符注释。

除了用于组件初始化之外,还可以将@Lazy注释放在用@Autowired或@Inject标记的注入点上。在这种情况下,它会导致延迟解析代理的注入。

除了用于组件初始化之外,还可以将@Lazy注释放在用@Autowired或@Inject标记的注入点上。在这种情况下,它会导致延迟解析代理的注入。

@Component
public class FactoryMethodComponent {

    private static int i;

    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }

    // use of a custom qualifier and autowiring of method parameters
    @Bean
    protected TestBean protectedInstance(
            @Qualifier("public") TestBean spouse,
            @Value("#{privateInstance.age}") String country) {
        TestBean tb = new TestBean("protectedInstance", 1);
        tb.setSpouse(spouse);
        tb.setCountry(country);
        return tb;
    }

    @Bean
    private TestBean privateInstance() {
        return new TestBean("privateInstance", i++);
    }

    @Bean
    @RequestScope
    public TestBean requestScopedInstance() {
        return new TestBean("requestScopedInstance", 3);
    }
}

该示例将String方法参数country自动拖拽到另一个名为privateInstance的bean上的age属性的值。Spring表达式语言元素通过符号#{< Expression >}定义属性的值。对于@Value注释,表达式解析器预先配置为在解析表达式文本时查找bean名称。从Spring Framework 4.3开始,您还可以声明一个类型为InjectionPoint的工厂方法参数(或其更具体的子类:DependencyDescriptor)来访问触发当前bean创建的请求注入点。注意,这只适用于bean实例的实际创建,而不适用于现有实例的注入。因此,该特性对于原型范围内的bean最有意义。对于其他范围,工厂方法只看到在给定范围内触发创建新bean实例的注入点(例如,触发创建惰性单例bean的依赖项)。在这些场景中,您可以使用提供的具有语义关怀的注入点元数据。下面的例子展示了如何使用InjectionPoint:

@Component
public class FactoryMethodComponent {

    @Bean @Scope("prototype")
    public TestBean prototypeInstance(InjectionPoint injectionPoint) {
        return new TestBean("prototypeInstance for " + injectionPoint.getMember());
    }
}

常规Spring组件中的@Bean方法与Spring @Configuration类中的对应方法处理方式不同。不同之处在于,@Component类没有使用CGLIB增强来拦截方法和字段的调用。CGLIB代理是通过调用@Configuration类中的@Bean方法中的方法或字段来创建对协作对象的bean元数据引用的方法。这些方法不是用普通的Java语义调用的,而是通过容器来提供Spring bean通常的生命周期管理和代理,即使在通过对@Bean方法的编程调用引用其他bean时也是如此。相反,在普通@Component类中调用@Bean方法中的方法或字段具有标准的Java语义,不需要使用特殊的CGLIB处理或其他约束。

**您可以将@Bean方法声明为静态方法,这样就可以调用它们,而不需要创建包含它们的配置类作为实例。这在定义后处理器bean(例如,类型为BeanFactoryPostProcessor或BeanPostProcessor)时特别有意义,因为这样的bean在容器生命周期的早期初始化,并且应该避免在那时触发配置的其他部分。由于技术限制,对静态@Bean方法的调用永远不会被容器拦截,即使在@Configuration类中也不会被拦截(如本节前面所述):CGLIB子类化只能覆盖非静态方法。因此,直接调用另一个@Bean方法具有标准的Java语义,从而直接从工厂方法本身返回一个独立的实例。@Bean方法的Java语言可视性对Spring s容器中生成的bean定义没有直接影响。您可以自由地声明您认为适合于非@ configuration类的工厂方法,也可以在任何地方声明静态方法。但是,@Configuration类中的常规@Bean方法需要被覆盖,也就是说,它们不能被声明为私有或final。@Bean方法还可以在给定组件或配置类的基类上发现,也可以在组件或配置类实现的接口中声明的Java 8缺省方法上发现。这在组合复杂的配置安排时提供了很大的灵活性,从Spring 4.2开始,甚至可以通过Java 8缺省方法进行多重继承。最后,一个类可能包含同一个bean的多个@Bean方法,这是根据运行时可用的依赖关系使用的多个工厂方法的一种安排。这与在其他配置场景中选择最贪婪的构造函数或工厂方法的算法相同:在构建时选择具有最大数量可满足依赖关系的变体,类似于容器如何在多个@Autowired构造函数之间进行选择。

1.10.6. Naming Autodetected Components 命名自动检测组件
当一个组件作为扫描过程的一部分被自动检测时,它的bean名称由扫描器所知道的BeanNameGenerator策略生成。默认情况下,任何包含名称值的Spring原型注释(@Component、@Repository、@Service和@Controller)都将该名称提供给相应的bean定义。
如果这样的注释不包含名称值或任何其他检测到的组件(例如自定义过滤器发现的组件),则默认bean名称生成器返回未大写的非限定类名。例如,如果检测到以下组件类,名称将是myMovieLister和movieFinderImpl

 @Service("myMovieLister")
public class SimpleMovieLister {
    // ...
}
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

如果不希望依赖默认的bean命名策略,可以提供自定义bean命名策略。首先,实现BeanNameGenerator接口,并确保包含一个默认的no-arg构造函数。然后,在配置扫描器时提供完全限定的类名,如下例注释和bean定义所示:

@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
    ...
}


<beans>
    <context:component-scan base-package="org.example"
        name-generator="org.example.MyNameGenerator" />
</beans>

一般来说,当其他组件可能显式引用该名称时,请考虑使用注释指定名称。另一方面,只要容器负责连接,自动生成的名称就足够了。

1.10.7. Providing a Scope for Autodetected Components
与一般的spring管理组件一样,自动检测组件的默认且最常见的作用域是单例的。但是,有时您需要一个可以由@Scope注释指定的不同范围。您可以在注释中提供作用域的名称,如下面的示例所示:

@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}
@Scope注释仅在具体bean类(用于带注释的组件)或工厂方法(用于@Bean方法)上自省。与XML bean定义相反,没有bean定义继承的概念,类级别的继承层次结构与元数据目的无关。

有关特定于web的作用域的详细信息,如Spring上下文中的“请求”或“会话”,请参见请求、会话、应用程序和WebSocket作用域(在1.5.4)。与为这些作用域预先构建的注释一样,您也可以使用Spring的元注释方法来组合自己的作用域注释:例如,使用@Scope(“prototype”)进行自定义注释元注释,还可能声明自定义作用域代理模式。

要为范围解析提供自定义策略,而不是依赖于基于注释的方法,您可以实现ScopeMetadataResolver接口。确保包含一个默认的无参数构造函数。然后,您可以在配置扫描器时提供完全限定的类名,如下面的注释和bean定义示例所示:

@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
    ...
}


<beans>
    <context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver"/>
</beans>

当使用某些非单例范围时,可能需要为范围对象生成代理。在作用域bean中,将推理描述为依赖项。为此,component-scan元素上有一个作用域代理属性。这三个可能的值是:no、interface和targetClass。例如,标准JDK动态代理的配置如下:

@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
    ...
}



<beans>
    <context:component-scan base-package="org.example" scoped-proxy="interfaces"/>
</beans>

1.10.8. Providing Qualifier Metadata with Annotations
使用注解提供限定符元数据
在使用限定符微调基于注释的自动装配中讨论了@Qualifier注释。本节中的示例演示了在解析autowire候选项时使用@Qualifier注释和自定义qualifier注释提供细粒度控制。由于这些示例基于XML bean定义,所以通过使用XML中bean元素的qualifier or meta子元素在候选bean定义上提供限定符元数据。当依赖类路径扫描来自动检测组件时,可以在候选类上提供带有类型级别注释的限定符元数据。下面三个例子演示了这种技术:

@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}

@Component
@Genre("Action")
public class ActionMovieCatalog implements MovieCatalog {
    // ...
}

@Component
@Offline
public class CachingMovieCatalog implements MovieCatalog {
    // ...
}
与大多数基于注释的替代方法一样,请记住注释元数据绑定到类定义本身,而XML的使用允许同一类型的多个bean在限定符元数据中提供变体,因为该元数据是按实例而不是按类提供的。

1.10.9. Generating an Index of Candidate Components
生成候选组件索引
虽然类路径扫描非常快,但是可以通过在编译时创建一个静态候选列表来提高大型应用程序的启动性能。在此模式下,组件扫描的所有目标模块都必须使用该机制。

您现有的@ComponentScan或<context:component-scan指令必须保持不变,以便请求上下文扫描某些包中的候选者。当ApplicationContext检测到这样一个索引时,它会自动使用它,而不是扫描类路径。
要生成索引,请向每个包含组件的模块添加附加依赖项,这些组件是组件扫描指令的目标。下面的例子展示了如何使用Maven实现这一点:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.1.8.RELEASE</version>
        <optional>true</optional>
    </dependency>
</dependencies>

With Gradle 4.6 and later, the dependency should be declared in the annotationProcessor configuration, as shown in the following example:

dependencies {
    annotationProcessor "org.springframework:spring-context-indexer:5.1.8.RELEASE"
}

该过程生成一个META-INF/spring.components文件,该文件包含在jar文件中。
在IDE中使用这种模式时,必须将spring-context-indexer注册为注释处理器,以确保在更新候选组件时索引是最新的。

当在类路径上找到META-INF/spring.components时,将自动启用该索引。如果索引对于某些库(或用例)是部分可用的,但是不能为整个应用程序构建索引,那么可以通过设置spring.index返回到常规的类路径安排(好像根本没有索引)。忽略true,无论是作为系统属性还是在spring中。类路径根目录下的属性文件。
1.11. Using JSR 330 Standard Annotations
从Spring 3.0开始,Spring提供了对JSR-330标准注释(依赖项注入)的支持。这些注释的扫描方式与Spring注释相同。要使用它们,您需要在类路径中有相关的jar。
If you use Maven, the javax.inject artifact is available in the standard Maven repository ( https://repo1.maven.org/maven2/javax/inject/javax.inject/1/). You can add the following dependency to your file pom.xml:

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

1.11.1. Dependency Injection with @Inject and @Named 使用@inject和@named依赖性注入
Instead of @Autowired, you can use @javax.inject.Inject as follows:
import javax.inject.Inject;

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    public void listMovies() {
        this.movieFinder.findMovies(...);
        ...
    }
}
与@Autowired一样,您可以在字段级、方法级和构造参数级使用@Inject。此外,您可以将注入点声明为提供者,从而允许按需访问范围较短的bean,或者通过Provider.get()调用对其他bean进行延迟访问。下面的例子提供了前一个例子的一个变体:
import javax.inject.Inject;

import javax.inject.Provider;

public class SimpleMovieLister {

    private Provider<MovieFinder> movieFinder;

    @Inject
    public void setMovieFinder(Provider<MovieFinder> movieFinder) {
        this.movieFinder = movieFinder;
    }

    public void listMovies() {
        this.movieFinder.get().findMovies(...);
        ...
    }
}

如果您想为应该注入的依赖项使用限定名,您应该使用@Named注释,如下面的示例所示:

import javax.inject.Inject;
import javax.inject.Named;

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Inject
    public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

与@Autowired一样,@Inject也可以与java.util.Optional 或@Nullable一起使用。这在这里更加适用,因为@Inject没有必需的属性。下面的两个例子展示了如何使用@Inject和@Nullable:

public class SimpleMovieLister {

    @Inject
    public void setMovieFinder(Optional<MovieFinder> movieFinder) {
        ...
    }
}

public class SimpleMovieLister {

    @Inject
    public void setMovieFinder(@Nullable MovieFinder movieFinder) {
        ...
    }
}

1.11.2. @Named and @ManagedBean: Standard Equivalents to the @Component Annotation
Instead of @Component, you can use @javax.inject.Named or javax.annotation.ManagedBean, as the following example shows:

import javax.inject.Inject;
import javax.inject.Named;

@Named("movieListener")  // @ManagedBean("movieListener") could be used as well
public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    // ...
}

It is very common to use @Component without specifying a name for the component. @Named can be used in a similar fashion, as the following example shows:

import javax.inject.Inject;
import javax.inject.Named;

@Named
public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    // ...
}

When you use @Named or @ManagedBean, you can use component scanning in the exact same way as when you use Spring annotations, as the following example shows:

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
    ...
}

1.11.3. Limitations of JSR-330 Standard Annotations
当您使用标准注释时,您应该知道一些重要的特性是不可用的,如下表所示:
在这里插入图片描述在这里插入图片描述在这里插入图片描述1.12. Java-based Container Configuration

This section covers how to use annotations in your Java code to configure the Spring container. It includes the following topics:

  • Basic Concepts: @Bean and @Configuration

-Instantiating the Spring Container by Using AnnotationConfigApplicationContext

  • Using the @Bean Annotation

  • Using the @Configuration annotation

  • Composing Java-based Configurations

  • Bean Definition Profiles

  • PropertySource Abstraction

-Using @PropertySource

-Placeholder Resolution in Statements

1.12.1. Basic Concepts: @Bean and @Configuration
Spring新的java配置支持中的核心构件是@Configuration-annotated类和@Bean-annotated方法。@Bean注释用于指示方法实例化、配置和初始化要由Spring IoC容器管理的新对象。对于那些熟悉Spring s <beans/> XML配置的人来说,@Bean注释的作用与<bean/>元素相同。您可以对任何Spring @Component使用@ bean注释的方法。但是,它们最常与@Configuration bean一起使用。用@Configuration注释类表明其主要目的是作为bean定义的源。此外,@Configuration类允许通过调用同一类中的其他@Bean方法来定义bean之间的依赖关系。最简单的@Configuration类如下所示:

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}

The preceding AppConfig class is equivalent to the following Spring <beans/> XML:

<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

完全@Configuration与lite @Bean模式?
当@Bean方法在没有使用@Configuration注释的类中声明时,它们被称为在lite模式下处理。在@Component中声明的Bean方法,甚至在普通的旧类中声明的Bean方法,都被认为是lite,包含类的主要目的不同,而@Bean方法在这里是一种附加功能。例如,服务组件可以通过每个适用组件类上的附加@Bean方法向容器公开管理视图。在这种情况下,@Bean方法是一种通用的工厂方法机制。
与完整的@Configuration不同,lite @Bean方法不能声明bean之间的依赖关系。相反,它们对包含它们的组件的内部状态进行操作,并可选地对它们可能声明的参数进行操作。因此,这样的@Bean方法不应该调用其他@Bean方法。每个这样的方法实际上只是一个特定bean引用的工厂方法,没有任何特殊的运行时语义。这里的积极副作用是在运行时不需要应用CGLIB子类,所以在类设计方面没有限制(也就是说,包含的类可能是final类,等等)。
在常见的场景中,@Bean方法将在@Configuration类中声明,确保始终使用完整的模式,从而将交叉方法引用重定向到容器的生命周期管理。这可以防止通过常规Java调用意外调用相同的@Bean方法,这有助于减少在lite模式下操作时难以跟踪的细微bug。

下面几节将深入讨论@Bean和@Configuration注释。但是,首先,我们将介绍基于java的配置创建spring容器的各种方法。

1.12.2. Instantiating the Spring Container by Using AnnotationConfigApplicationContext
下面几节将记录Spring 3.0中引入的注释AnnotationConfigApplicationContext。这个通用的ApplicationContext实现不仅可以接受@Configuration类作为输入,还可以接受普通的@Component类和用JSR-330元数据注释的类。

当@Configuration类作为输入提供时,@Configuration类本身注册为bean定义,类中所有声明的@Bean方法也注册为bean定义。

当提供@Component和JSR-330类时,它们被注册为bean定义,并且假设DI元数据(如@Autowired或@Inject)在这些类中使用。

Simple Construction
与实例化一个ClassPathXmlApplicationContext时将Spring XML文件用作输入非常相似,在实例化注释configapplicationcontext时可以使用@Configuration类作为输入。这允许完全不使用xml的Spring容器,如下面的例子所示:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

如前所述,AnnotationConfigApplicationContext不仅限于使用@Configuration类。任何@Component或JSR-330注释类都可以作为输入提供给构造函数,如下例所示:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

The preceding example assumes that MyServiceImpl, Dependency1, and Dependency2 use Spring dependency injection annotations such as @Autowired.

使用register(类<?>…)以编程方式构建容器。

您可以使用一个无arg构造函数实例化一个AnnotationConfigApplicationContext,然后使用register()方法配置它。这种方法在以编程方式构建注释configapplicationcontext时特别有用。下面的示例展示了如何做到这一点:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(AppConfig.class, OtherConfig.class);
    ctx.register(AdditionalConfig.class);
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

Enabling Component Scanning with scan(String…​)

To enable component scanning, you can annotate your @Configuration class as follows:

@Configuration
@ComponentScan(basePackages = "com.acme")  	//This annotation enables component scanning.
public class AppConfig  {
    ...
}

Experienced Spring users may be familiar with the XML declaration equivalent from Spring’s context: namespace, shown in the following example:

<beans>
    <context:component-scan base-package="com.acme"/>
</beans>

在前面的例子中,com.acme包以查找任何@Component-annotated类,这些类在容器中注册为Spring bean定义。AnnotationConfigApplicationContext公开了scan(String)方法,以支持相同的组件扫描功能,如下面的示例所示:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.scan("com.acme");
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
}

请记住@Configuration类是用@Component进行元注释的,所以它们是组件扫描的候选对象。在前面的示例中,假设AppConfig是在com中声明的com.acme包(或下面的任何包),它在调用scan()期间被获取。在refresh()之后,它的所有@Bean方法都将被处理,并在容器中注册为bean定义。

Support for Web Applications with AnnotationConfigWebApplicationContext

AnnotationConfigApplicationContext的WebApplicationContext变体可以与AnnotationConfigWebApplicationContext一起使用。您可以在配置Spring ContextLoaderListener servlet侦听器、Spring MVC DispatcherServlet等时使用此实现。下面的web.xml片段配置了一个典型的Spring MVC web应用程序(请注意使用contextClass context-param和init-param):

<web-app>
    <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
        instead of the default XmlWebApplicationContext -->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        </param-value>
    </context-param>

    <!-- Configuration locations must consist of one or more comma- or space-delimited
        fully-qualified @Configuration classes. Fully-qualified packages may also be
        specified for component-scanning -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.acme.AppConfig</param-value>
    </context-param>

    <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Declare a Spring MVC DispatcherServlet as usual -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
            instead of the default XmlWebApplicationContext -->
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            </param-value>
        </init-param>
        <!-- Again, config locations must consist of one or more comma- or space-delimited
            and fully-qualified @Configuration classes -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>com.acme.web.MvcConfig</param-value>
        </init-param>
    </servlet>

    <!-- map all requests for /app/* to the dispatcher servlet -->
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/app/*</url-pattern>
    </servlet-mapping>
</web-app>

1.12.3. Using the @Bean Annotation

@Bean是方法级别的注释,是XML 元素的直接模拟。注释支持<bean/>提供的一些属性,比如:* init-method * destroy-method * autowiring * name。
您可以在@Configuration-annotated类或@Component-annotated类中使用@Bean注释。

Declaring a Bean
要声明bean,可以用@Bean注释方法。您可以使用此方法在作为方法返回值指定的类型的ApplicationContext中注册bean定义。默认情况下,bean名称与方法名称相同。下面的例子显示了一个@Bean方法声明:

@Configuration
public class AppConfig {

    @Bean
    public TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
}

对应:

<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

Both declarations make a bean named transferService available in the ApplicationContext, bound to an object instance of type TransferServiceImpl, as the following text image shows:

transferService -> com.acme.TransferServiceImpl

You can also declare your @Bean method with an interface (or base class) return type, as the following example shows:

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
}

但是,这将提前类型预测的可见性限制为指定的接口类型(TransferService)。然后,容器只知道一次完整的类型(TransferServiceImpl),就实例化了受影响的单例bean。非惰性单例bean根据其声明顺序进行实例化,因此您可能会看到不同的类型匹配结果,这取决于另一个组件何时尝试使用非声明类型进行匹配(例如@Autowired TransferServiceImpl,它只在实例化transferService bean之后解析)。

如果始终通过声明的服务接口引用类型,则@Bean返回类型可以安全地加入该设计决策。但是,对于实现多个接口的组件或可能由其实现类型引用的组件,声明最特定的返回类型更安全(至少与引用bean的注入点所要求的一样具体)。

@ bean注释的方法可以有任意数量的参数,这些参数描述构建bean所需的依赖关系。例如,如果我们的TransferService需要一个AccountRepository,我们可以用一个方法参数来实现这个依赖关系,如下面的例子所示:

@Configuration
public class AppConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}

解析机制与基于构造函数的依赖项注入非常相似。有关详细信息,请参阅相关部分。
接收生命周期回调
使用@Bean注释定义的任何类都支持常规的生命周期回调,并且可以使用JSR-250中的@PostConstruct和@PreDestroy注释。有关详细信息,请参见JSR-250注释。
还完全支持常规的Spring生命周期回调。如果bean实现了InitializingBean、 DisposableBean, or Lifecycle,容器将调用它们各自的方法。

还完全支持标准的*Aware感知接口集(如BeanFactoryAware、BeanNameAware、MessageSourceAware、applicationcontext - ware等)。
@Bean注释支持指定任意初始化和销毁回调方法,很像Spring XML在bean元素上的init-method和destroy-method属性,如下面的示例所示:

public class BeanOne {

    public void init() {
        // initialization logic
    }
}

public class BeanTwo {

    public void cleanup() {
        // destruction logic
    }
}

@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public BeanOne beanOne() {
        return new BeanOne();
    }

    @Bean(destroyMethod = "cleanup")
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

默认情况下,使用具有公共关闭或关闭方法的Java配置定义的bean将通过销毁回调自动登记。如果您有一个公共close或shutdown方法,并且您不希望在容器关闭时调用它,那么您可以在bean定义中添加@Bean(destroyMethod="")来禁用默认(推断)模式。
缺省情况下,您可能希望对使用JNDI获得的资源这样做,因为它的生命周期是在应用程序之外管理的。特别是,请确保始终为数据源执行此操作,因为在Java EE应用服务器上这是有问题的。下面的示例显示如何防止数据源的自动销毁回调:

  @Bean(destroyMethod="")
public DataSource dataSource() throws NamingException {
    return (DataSource) jndiTemplate.lookup("MyDS");
}

另外,@ bean方法,你通常使用程序化的JNDI查找,通过使用Spring的JndiTemplate或 JndiLocatorDelegate助手或直接使用JNDI InitialContext但不是JndiObjectFactoryBean变体(这将迫使你声明返回类型作为FactoryBean类型,而不是实际的目标类型,因此很难使用交叉引用调用@ bean方法,打算在其他参考所提供的资源)。

对于上例中的BeanOne,在构造过程中直接调用init()方法同样有效,如下例所示:

@Configuration
public class AppConfig {

    @Bean
    public BeanOne beanOne() {
        BeanOne beanOne = new BeanOne();
        beanOne.init();
        return beanOne;
    }

    // ...
}

When you work directly in Java, you can do anything you like with your objects and do not always need to rely on the container lifecycle.
Specifying Bean Scope 指定bean范围
Spring包含@Scope注释,因此您可以指定bean的范围。
使用@Scope注释
您可以指定使用@Bean注释定义的bean应该具有特定的范围。您可以使用Bean作用域部分中指定的任何标准作用域。
默认的作用域是单例的,但是您可以使用@Scope注释覆盖它,如下面的例子所示:

@Configuration
public class MyConfiguration {

    @Bean
    @Scope("prototype")
    public Encryptor encryptor() {
        // ...
    }
}

@Scope和scoped-proxy 作用域内的代理
Spring提供了一种通过作用域代理处理作用域依赖项的方便方法。使用XML配置时创建这样一个代理的最简单方法是元素。使用@Scope注释在Java中配置bean可以提供与proxyMode属性相同的支持。默认值是no proxy (ScopedProxyMode. no),但是您可以指定ScopedProxyMode。TARGET_CLASS或ScopedProxyMode.INTERFACES。
如果您使用Java将作用域代理示例从XML参考文档(请参阅作用域代理)移植到我们的@Bean,它类似于:
// an HTTP Session-scoped bean exposed as a proxy

@Bean
@SessionScope
public UserPreferences userPreferences() {
    return new UserPreferences();
}

@Bean
public Service userService() {
    UserService service = new SimpleUserService();
    // a reference to the proxied userPreferences bean
    service.setUserPreferences(userPreferences());
    return service;
}

定制Bean命名
默认情况下,配置类使用@Bean方法的名称作为结果bean的名称。但是,可以使用name属性覆盖此功能,如下面的示例所示:

@Configuration
public class AppConfig {

    @Bean(name = "myThing")
    public Thing thing() {
        return new Thing();
    }
}

Bean Aliasing
正如在bean命名中所讨论的,有时需要为单个bean指定多个名称,也称为bean别名。@Bean注释的name属性接受用于此目的的字符串数组。下面的例子展示了如何为一个bean设置多个别名:

@Configuration
public class AppConfig {

    @Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
    public DataSource dataSource() {
        // instantiate, configure and return DataSource bean...
    }
}

Bean Description
有时候,提供bean更详细的文本描述是有帮助的。当出于监视目的公开bean(可能通过JMX)时,这一点特别有用。
要向@Bean添加描述,可以使用@Description注释,如下面的示例所示:

@Configuration
public class AppConfig {

    @Bean
    @Description("Provides a basic example of a bean")
    public Thing thing() {
        return new Thing();
    }
}

1.12.4. Using the @Configuration annotation

@Configuration是一个类级注释,指示对象是bean定义的源。@Configuration类通过公共的@Bean注释方法声明bean。在@Configuration类上调用@Bean方法也可以用来定义bean之间的依赖关系。有关一般介绍,请参见基本概念:@Bean和@Configuration。
注入Inter-bean依赖性
当bean之间存在依赖关系时,表示这种依赖关系就像让一个bean方法调用另一个bean方法一样简单,如下面的例子所示:

@Configuration
public class AppConfig {

    @Bean
    public BeanOne beanOne() {
        return new BeanOne(beanTwo());
    }

    @Bean
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

In the preceding example, beanOne receives a reference to beanTwo through constructor injection.
在前面的示例中,beanOne通过构造函数注入接收对beanTwo的引用。
**只有当@Bean方法在@Configuration类中声明时,这种声明bean间依赖关系的方法才有效。不能使用普通的@Component类声明bean之间的依赖关系。

Lookup Method Injection
如前所述,查找方法注入是一个高级特性,应该很少使用。当单作用域bean依赖于原型作用域bean时,它非常有用。将Java用于这种类型的配置提供了实现此模式的一种自然方法。下面的例子展示了如何使用查找方法注入:

public abstract class CommandManager {
    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        获取适当命令接口的新实例
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        在命令实例上设置状态(希望是全新的)
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}

通过使用Java配置,您可以创建CommandManager的子类,其中抽象createCommand()方法将被重写,从而查找一个新的(原型)命令对象。下面的例子说明了如何做到这一点:

@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
    AsyncCommand command = new AsyncCommand();
    // inject dependencies here as required
    return command;
}

@Bean
public CommandManager commandManager() {
    // return new anonymous implementation of CommandManager with createCommand()
    // overridden to return a new prototype Command object
    return new CommandManager() {
        protected Command createCommand() {
            return asyncCommand();
        }
    }
}

关于基于java的配置如何在内部工作的进一步信息
考虑下面的例子,它显示了一个@Bean注释的方法被调用两次:

@Configuration
public class AppConfig {

    @Bean
    public ClientService clientService1() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientService clientService2() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientDao clientDao() {
        return new ClientDaoImpl();
    }
}

在clientService1()和clientService2()中分别调用了一次clientDao()和一次clientDao()。因为这个方法创建了一个新的ClientDaoImpl实例并返回它,所以您通常希望有两个实例(每个服务一个)。这肯定会有问题:在Spring中,实例化的bean默认有一个单例范围。这就是神奇之处:所有@Configuration类都是在启动时用CGLIB子类化的。在子类中,子方法在调用父方法并创建新实例之前,首先检查容器是否有缓存的bean(作用域)。

根据bean的范围,行为可能有所不同。我们这里说的是单例。

从Spring 3.2开始,不再需要将CGLIB添加到类路径中,因为CGLIB类已经在org.springframework.cglib下重新打包,并直接包含在spring-core JAR中。

由于CGLIB在启动时动态添加特性,所以存在一些限制。特别是,配置类不能是final。但是,从4.3开始,配置类上允许使用任何构造函数,包括使用@Autowired或单个非默认构造函数声明进行默认注入。

如果希望避免cglib强加的任何限制,请考虑在非-@ configuration类上声明@Bean方法(例如,在普通的@Component类上声明)。然后不会拦截@Bean方法之间的交叉方法调用,因此必须完全依赖于构造函数或方法级别的依赖注入。

1.12.5。编写基于java的配置
Spring基于java的配置特性允许您编写注释,这可以降低配置的复杂性。
使用@Import注释
正如元素在Spring XML文件中用于帮助模块化配置一样,@Import注释允许从另一个配置类加载@Bean定义,如下面的示例所示:

@Configuration
public class ConfigA {

    @Bean
    public A a() {
        return new A();
    }
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

    @Bean
    public B b() {
        return new B();
    }
}

现在,不需要在实例化上下文时同时指定configA .class和configB.class,只需要显式地提供ConfigB,如下面的示例所示:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);

    // now both beans A and B will be available...
    A a = ctx.getBean(A.class);
    B b = ctx.getBean(B.class);
}

这种方法简化了容器实例化,因为只需要处理一个类,而不需要在构建期间记住大量的@Configuration类。

从Spring Framework 4.2开始,@Import还支持引用常规组件类,类似于AnnotationConfigApplicationContext.register方法。如果您想避免组件扫描,使用几个配置类作为入口点显式地定义所有组件,这尤其有用。

在导入的@Bean定义上注入依赖项
前面的例子有效,但过于简单。在大多数实际场景中,bean在配置类之间相互依赖。当使用XML时,这不是问题,因为不涉及编译器,您可以声明ref=“someBean”,并信任Spring在容器初始化期间解决它。当使用@Configuration类时,Java编译器会对配置模型施加约束,因为对其他bean的引用必须是有效的Java语法。

幸运的是,解决这个问题很简单。正如我们已经讨论过的,@Bean方法可以有任意数量的参数来描述bean依赖关系。考虑以下更真实的场景,其中有几个@Configuration类,每个类都依赖于其他类中声明的bean:

@Configuration
public class ServiceConfig {

    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration
public class RepositoryConfig {

    @Bean
    public AccountRepository accountRepository(DataSource dataSource) {
        return new JdbcAccountRepository(dataSource);
    }
}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

还有另一种方法可以达到同样的效果。请记住,@Configuration类最终只是容器中的另一个bean:这意味着它们可以利用@Autowired和@Value注入以及与任何其他bean相同的其他特性。

确保以这种方式注入的依赖关系只是最简单的那种。@Configuration类在上下文初始化过程中很早就被处理,强制以这种方式注入依赖项可能会导致意外的早期初始化。只要可能,就使用基于参数的注入,如前面的示例所示。

另外,要特别注意通过@Bean定义BeanPostProcessor和BeanFactoryPostProcessor。这些方法通常应该声明为静态@Bean方法,而不是触发其包含的配置类的实例化。否则,@Autowired和@Value不会处理配置类本身,因为它作为bean实例创建得太早了。

The following example shows how one bean can be autowired to another bean:

@Configuration
public class ServiceConfig {

    @Autowired
    private AccountRepository accountRepository;

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(accountRepository);
    }
}

@Configuration
public class RepositoryConfig {

    private final DataSource dataSource;

    @Autowired
    public RepositoryConfig(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }
}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }
}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

从Spring Framework 4.3开始,只支持@Configuration类中的构造函数注入。还请注意,如果目标bean只定义一个构造函数,则不需要指定@Autowired。在前面的示例中,在RepositoryConfig构造函数上不需要@Autowired。
完全符合条件的导入bean,便于导航

在前面的场景中,使用@Autowired可以很好地工作,并提供了所需的模块性,但是准确地确定在哪里声明了autowired bean定义仍然有些模糊。例如,作为一个查看ServiceConfig的开发人员,您如何确切地知道@Autowired AccountRepository bean声明在哪里?它在代码中没有显式显示,这可能很好。请记住,Spring工具套件提供了一些工具,可以呈现显示所有内容如何连接的图表,这可能正是您所需要的。而且,您的Java IDE可以轻松地找到AccountRepository类型的所有声明和使用,并快速显示返回该类型的@Bean方法的位置。如果这种模糊性是不可接受的,并且您希望在IDE中从一个@Configuration类直接导航到另一个@Configuration类,请考虑自动装配配置类本身。下面的示例展示了如何做到这一点:

@Configuration
public class ServiceConfig {

    @Autowired
    private RepositoryConfig repositoryConfig;

    @Bean
    public TransferService transferService() {
        // navigate 'through' the config class to the @Bean method!
        return new TransferServiceImpl(repositoryConfig.accountRepository());
    }
}

在前面的情况中,AccountRepository的定义是完全显式的。但是,ServiceConfig现在与RepositoryConfig紧密耦合。这就是权衡。通过使用基于接口或基于抽象类的@Configuration类,可以在一定程度上缓解这种紧密耦合。考虑下面的例子:

@Configuration
public class ServiceConfig {

    @Autowired
    private RepositoryConfig repositoryConfig;

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(repositoryConfig.accountRepository());
    }
}

@Configuration
public interface RepositoryConfig {

    @Bean
    AccountRepository accountRepository();
}

@Configuration
public class DefaultRepositoryConfig implements RepositoryConfig {

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(...);
    }
}

@Configuration
@Import({ServiceConfig.class, DefaultRepositoryConfig.class})  // import the concrete config!
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return DataSource
    }

}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

现在ServiceConfig与具体的DefaultRepositoryConfig松散耦合,并且内置IDE工具仍然很有用:您可以很容易地获得RepositoryConfig实现的类型层次结构。通过这种方式,导航@Configuration类及其依赖项与导航基于接口的代码的通常过程没有什么不同。

如果你想影响某些豆类的启动创建订单,考虑将其中一些声明为@Lazy(用于创建在第一次访问,而不是在启动时)或@DependsOn某些其他bean(确保特定的其他bean创建当前bean之前,超出后者的直接依赖关系暗示)。

有条件地包含@Configuration类或@Bean方法
根据某些任意的系统状态,有条件地启用或禁用一个完整的@Configuration类,甚至单个的@Bean方法,这通常很有用。一个常见的例子是,只有在Spring环境中启用了特定的概要文件时,才使用@Profile注释来激活Bean(有关详细信息,请参阅Bean定义概要文件)。

@Profile注释实际上是通过使用一个灵活得多的名为@Conditional的注释实现的。@ condition注释指示特定的org.springframe .context.annotation。在注册@Bean之前应该咨询的条件实现。
条件接口的实现提供一个返回true或false的matches(…)方法。例如,下面的清单显示了@Profile使用的实际条件实现:

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    if (context.getEnvironment() != null) {
        // Read the @Profile annotation attributes
        MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
        if (attrs != null) {
            for (Object value : attrs.get("value")) {
                if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
                    return true;
                }
            }
            return false;
        }
    }
    return true;
}

Combining Java and XML Configuration
Spring 的 @Configuration类支持的目标不是100%完全替代Spring XML。一些工具,如Spring XML名称空间,仍然是配置容器的理想方法。在XML方便或必要的情况下,你有一个选择:要么以XML为中心的方法通过使用容器实例化,例如,ClassPathXmlApplicationContext或实例化它以java为中心的方法通过使用所和@ImportResource注释导入XML。
以XML为中心使用@Configuration类
最好是从XML引导Spring容器,并以特别的方式包含@Configuration类。例如,在使用Spring XML的大型现有代码基中,根据需要创建@Configuration类并从现有XML文件中包含它们会更容易。在本节的后面,我们将介绍在这种以xml为中心的情况下使用@Configuration类的选项。
将@Configuration类声明为普通Spring 元素
请记住,@Configuration类最终是容器中的bean定义。在本系列示例中,我们创建了一个名为AppConfig的@Configuration类,并将其作为<bean/>定义包含在system-test-config.xml中。因为打开了context:annotation-config/,容器可以识别@Configuration注释并正确处理AppConfig中声明的@Bean方法。
The following example shows an ordinary configuration class in Java:
@Configuration
public class AppConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }

    @Bean
    public TransferService transferService() {
        return new TransferService(accountRepository());
    }
}

下面的示例显示了示例system-test-config.xml文件的一部分:

<beans>
    <!-- enable processing of annotations such as @Autowired and @Configuration -->
    <context:annotation-config/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean class="com.acme.AppConfig"/>

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

The following example shows a possible jdbc.properties file:
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=

public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}

在system-test-config.xml文件中,AppConfig <bean/>没有声明id元素。虽然这样做是可以接受的,但是没有必要这样做,因为没有其他bean引用它,而且不太可能通过名称显式地从容器中获取它。类似地,DataSource bean只根据类型自动生成,因此并不严格要求显式bean id。

Using <context:component-scan/> to pick up @Configuration classes 获取

因为@Configuration使用@Component进行元注释,所以@Configuration-annotated类自动成为组件扫描的候选类。使用与前面示例相同的场景,我们可以重新定义system-test-config.xml,以利用组件扫描。注意,在本例中,我们不需要显式地声明,因为启用了相同的功能。
下面的例子显示了修改后的system-test-config.xml文件:

<beans>
    <!-- picks up and registers AppConfig as a bean definition -->
    <context:component-scan base-package="com.acme"/>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
</beans>

以@Configuration类为中心使用XML和@ImportResource,
在以@Configuration类为配置容器的主要机制的应用程序中,仍然可能需要使用至少一些XML。在这些场景中,您可以使用@ImportResource并只定义您需要的XML。这样做实现了一种以java为中心的方法来配置容器,并将XML保持在最低限度。下面的示例(包括一个配置类、一个定义bean的XML文件、一个属性文件和一个主类)展示了如何使用@ImportResource注释来实现以java为中心的配置(根据需要使用XML)

@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }
}

   properties-config.xml
    <beans>
        <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
    </beans>

jdbc.properties
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值