Spring官方文档阅读(六)之IOC容器(基于注解的容器配置)

9.基于注解的容器配置

在配置Spring时,注解比XML更好吗?

  • 基于注解的配置的引入提出了一个问题,即这种方法是否比XML“更好”。 简短的答案是“取决于情况”。 长话短说,每种方法都有其优缺点,通常,由开发人员决定哪种策略更适合他们。 由于定义方式的不同,注解在声明中提供了很多上下文,从而使配置更短,更简洁。 但是,XML擅长连接组件而不接触其源代码或重新编译它们。 一些开发人员更喜欢将布线放置在靠近源的位置,而另一些开发人员则认为带注解的类不再是POJO,而且,该配置变得分散且难以控制。
  • 无论选择如何,Spring都可以容纳两种样式,甚至可以将它们混合在一起。 值得指出的是,通过其JavaConfig选项,Spring允许以非侵入方式使用注解,而无需接触目标组件的源代码,并且就工具而言,Spring Tools for Eclipse支持所有配置样式。

基于注解的配置提供了XML设置的替代方法,该配置依赖字节码元数据来连接组件,而不是尖括号声明。 通过使用相关类,方法或字段声明上的注释,开发人员无需使用XML来描述bean的连接,而是将配置移入组件类本身。 如示例中所述:RequiredBeansProcessor,结合使用BeanPostProcessor和注解,是扩展Spring IOC容器的常用方法。 例如,Spring 2.0引入了使用@Required注释强制执行必需属性的可能性。 Spring 2.5使遵循相同的通用方法来驱动Spring的依赖注入成为可能。 本质上,@ Autowired注释提供的功能与自动装配协作器中所述的功能相同,但具有更细粒度的控制和更广泛的适用性。 Spring 2.5还添加了对JSR-250批注的支持,例如@PostConstruct@PreDestroy。 Spring 3.0增加了对javax.inject包中包含的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的注释。 这意味着,如果将<context:annotation-config />放在DispatcherServletWebApplicationContext中,它将仅检查控制器中的@Autowired bean,而不检查服务

9.1@Required

@Required注解应用于bean属性setter方法,如下面的示例所示

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属性setter方法)。

9.2使用@Autowired

在本节中的示例中,可以使用JSR 330的@Inject注解代替Spring的@Autowired注解。

您可以将@Autowired注解应用于构造函数,如以下示例所示:

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

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

    // ...
}

从Spring Framework 4.3开始,如果目标bean仅定义一个构造函数作为开始,则不再需要在此类构造函数上使用@Autowired注解。 但是,如果有多个构造函数可用,并且没有主/默认构造函数,则至少一个构造函数必须使用@Autowired注解,以指示容器使用哪个。

您还可以将@Autowired注解应用于传统的setter方法,如以下示例所示:

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;
    }

    // ...
}

您也可以将@Autowired应用于字段,甚至将其与构造函数混合使用,如以下示例所示:

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    private MovieCatalog movieCatalog;

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

    // ...
}

注意:

  • 确保您的目标组件(例如,MovieCatalog或CustomerPreferenceDao)由您为@ autowire注解的注入点使用的类型一致地声明。否则,注入可能会由于运行时出现“没有找到类型匹配”错误而失败。
  • 对于通过类路径扫描找到的XML定义的bean或组件类,容器通常预先知道具体的类型。 但是,对于@Bean工厂方法,您需要确保声明的返回类型具有足够的表现力。 对于实现多个接口的组件或可能由其实现类型引用的组件,请考虑在工厂方法中声明最具体的返回类型(至少根据引用您的bean的注入点的要求具体声明)。

您还可以通过将@Autowired注解添加到需要该类型数组的字段或方法中,指示Spring从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.Ordered接口或使用@Order或标准@Priority注解。 否则,它们的顺序将遵循容器中相应目标bean定义的注册顺序。
  • 您可以在目标类级别和@Bean方法上声明@Order注解,这可能适用于单个bean定义(如果使用同一bean类的多个定义)。 @Order值可能会影响注入点的优先级,但请注意它们不会影响单例启动顺序,这是由依赖关系和@DependsOn声明确定的正交关注点。
  • 注意,标准javax.annotation.Priority注解在@Bean级别不可用,因为无法在方法上声明它。 它的语义可以通过@Order值与@Primary结合在每种类型的单个bean上进行建模。

即使是类型化的Map实例也可以自动实现,只要期望的键类型是String。映射值包含所有期望类型的bean,键包含相应的bean名称,如下面的示例所示:

public class MovieRecommender {

    private Map<String, MovieCatalog> movieCatalogs;

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

    // ...
}

默认情况下,当给定注入点没有匹配的候选bean可用时,自动装配将失败。 对于声明的数组,集合或映射,至少应有一个匹配元素。

默认行为是将带注解的方法和字段视为指示所需的依赖项。 您可以按照以下示例中所示的方式更改此行为,从而使框架可以通过将其标记为不需要来跳过不满足要求的注入点(即,通过将@Autowired中的required属性设置为false):

public class SimpleMovieLister {

    private MovieFinder movieFinder;

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

    // ...
}

如果不需要的方法(或者在多个参数的情况下,其中一个依赖项)不可用,则根本不会调用该方法。 在这种情况下,完全不需要填充非必需字段,而保留其默认值。

注入的构造函数和工厂方法参数是一种特殊情况,因为由于Spring的构造函数解析算法可能会处理多个构造函数,因此@Autowired中的required属性的含义有所不同。 默认情况下,有效地需要构造函数和工厂方法参数,但是在单构造函数场景中有一些特殊规则,例如,如果没有可用的匹配bean,则将多元素注入点(数组,集合,映射)解析为空实例。 这允许一种通用的实现模式,其中所有依赖项都可以在唯一的多参数构造函数中声明。例如,声明为不带@Autowired注解的单个公共构造函数。

注意:

  • 任何给定bean类的构造函数都只能声明@Autowired,并将必选属性设置为true,这表示在用作Spring bean时可以自动装配的构造函数。 因此,如果必填属性保留为默认值true,则@Autowired只能注解单个构造函数。 如果多个构造函数声明了注解,则它们都必须声明required = false才能被视为自动装配的候选对象(类似于XML中的autowire = constructor)。 将选择通过匹配Spring容器中的bean可以满足的依赖项数量最多的构造函数。 如果没有一个候选者满意,则将使用主/默认构造函数(如果存在)。 同样,如果一个类声明了多个构造函数,但都没有使用@Autowired进行注解,则将使用主/默认构造函数(如果存在)。 如果一个类只声明一个单一的构造函数开始,即使没有注解,也将始终使用它。 请注意,带注解的构造函数不必是公共的。
  • 建议在设置方法上使用@Autowiredrequired属性,而不推荐使用已弃用的@Required批注。 将required属性设置为false表示该属性对于自动装配不是必需的,并且如果无法自动装配,则将忽略该属性。 另一方面,@Required更为强大,因为它可以通过容器支持的任何方式强制设置属性,并且如果未定义任何值,则会引发相应的异常。

另外,您可以通过Java 8的java.util.Optional表示特定依赖项的非必需性质,如以下示例所示:

public class SimpleMovieLister {

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

从Spring Framework 5.0开始,您还可以使用@Nullable注解(在任何包中为任何形式,例如JSR-305中的javax.annotation.Nullable)或仅利用Kotlin内置的null安全支持:

public class SimpleMovieLister {

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

您也可以将@Autowired用于众所周知的可解决依赖项的接口:BeanFactoryApplicationContextEnvironmentResourceLoaderApplicationEventPublisherMessageSource。 这些接口及其扩展接口(例如ConfigurableApplicationContextResourcePatternResolver)将自动解析,而无需进行特殊设置。 下面的示例自动装配ApplicationContext对象:

public class MovieRecommender {

    @Autowired
    private ApplicationContext context;

    public MovieRecommender() {
    }

    // ...
}

注意:

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

9.3使用@Primary微调基于注解的自动装配

由于按类型自动装配可能会导致多个候选对象,因此通常有必要对选择过程进行更多控制。 实现此目标的一种方法是使用Spring的@Primary注解。 @Primary表示当多个bean是自动装配到单值依赖项的候选对象时,应优先考虑特定的bean。 如果候选中恰好存在一个主bean,则它将成为自动装配的值。

考虑以下将firstMovieCatalog定义为主要MovieCatalog的配置:

@Configuration
public class MovieConfiguration {

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

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

    // ...
}

通过前面的配置,以下MovieRecommenderfirstMovieCatalog自动装配:

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>

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;
    }

    // ...
}

下面的示例显示了相应的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 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>

对于后备匹配,bean名称被视为默认的限定符值。 因此,可以使用idmain而不是嵌套的qualifier元素定义bean,从而得到相同的匹配结果。 但是,尽管您可以使用此约定按名称引用特定的bean,但@Autowired基本上是关于带有可选语义限定符的类型驱动的注入。 这意味着,即使带有Bean名称后备的限定符值,在类型匹配集中也始终具有狭窄的语义。 它们没有在语义上表示对唯一bean id的引用。 好的限定符值是主要的或EMEA的或持久的,表示特定组件的特性,该特性独立于bean id,在使用匿名bean定义(例如上例中的定义)的情况下,可以自动生成这些特性。

限定符还适用于类型化的集合,如前面所述(例如,应用于Set )。 在这种情况下,根据声明的限定符,将所有匹配的bean作为集合注入。 这意味着限定词不必是唯一的。 相反,它们构成了过滤标准。 例如,您可以使用相同的限定符值“ action”定义多个MovieCatalog Bean,所有这些都注入到注有@Qualifier(“ action”)的Set 中。

注意:

  • 在类型匹配的候选对象中,让限定符值根据目标Bean名称进行选择,在注入点不需要@Qualifier注解。 如果没有其他解决方案指示符(例如限定符或主标记),则对于非唯一依赖性情况,Spring将注入点名称(即字段名称或参数名称)与目标Bean名称进行匹配,然后选择 同名候选人(如果有)。

就是说,如果您打算按名称表示注解驱动的注入,则即使它能够在类型匹配的候选对象中按bean名称进行选择,也不要主要使用@Autowired。 而是使用JSR-250 @Resource批注,该注解的语义定义是通过其唯一名称标识特定目标组件,而声明的类型与匹配过程无关。 @Autowired具有不同的语义:在按类型选择候选bean之后,仅在那些类型选择的候选中考虑指定的String限定符值(例如,将帐户限定符与标记有相同限定符标签的bean进行匹配)。

从4.3开始,@Autowired还考虑了自我引用以进行注入(即,引用回当前注入的Bean)。 请注意,自我注入是一个后备。 对其他组件的常规依赖始终优先。 从这个意义上说,自我推荐不参与常规的候选人选择,因此尤其是绝不是主要的。 相反,它们总是以最低优先级结束。 实际上,您应该仅将自我引用用作最后的手段(例如,通过Bean的事务代理在同一实例上调用其他方法)。 在这种情况下,请考虑将受影响的方法分解为单独的委托bean。 或者,您可以使用@Resource,它可以通过其唯一名称获取返回到当前bean的代理。

注意:

  • 尝试将@Bean方法的结果注入相同的配置类也实际上是一种自引用方案。 要么在实际需要的方法签名中延迟解析这些引用(与配置类中的自动装配字段相对),要么将受影响的@Bean方法声明为静态,将其与包含的配置类实例及其生命周期脱钩。 否则,仅在回退阶段考虑此类Bean,而将其他配置类上的匹配Bean选作主要候选对象(如果可用)。

@Autowired适用于字段,构造函数和多参数方法,从而允许在参数级别缩小限定符注释的范围。 相反,只有具有单个参数的字段和bean属性设置器方法才支持@Resource。 因此,如果注入目标是构造函数或多参数方法,则应坚持使用限定符。

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

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

    String value();
}

然后您可以为autowired字段和参数提供自定义限定符,如下面的例子所示:

public class MovieRecommender {

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

    private MovieCatalog comedyCatalog;

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

    // ...
}

接下来,您可以提供有关候选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>

您还可以定义自定义限定符注解,该注解除了简单值属性之外或代替简单值属性,都接受命名属性。 如果随后在要自动装配的字段或参数上指定了多个属性值,则bean定义必须与所有此类属性值匹配才能被视为自动装配候选。 例如,请考虑以下注释定义:

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

    String genre();

    Format format();
}

在这种情况下,Format是一个枚举,定义如下:

public enum Format {
    VHS, DVD, BLURAY
}

要自动装配的字段将用自定义限定符进行注解,并包括两个属性的值:genreformat,如以下示例所示:

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元属性代替元素。 如果可用,元素及其属性优先,但是如果不存在这样的限定符,则自动装配机制将退回到标记内提供的值,如以下示例中的最后两个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>

9.5将泛型用作自动装配限定符

除了@Qualifier注解之外,您还可以将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;

9.6使用CustomAutowireConfigurer

CustomAutowireConfigurerBeanFactoryPostProcessor,即使您没有使用Spring的@Qualifier注解来注解自己的自定义限定符注释类型,也可以使用它来注册。 以下示例显示如何使用CustomAutowireConfigurer

<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值
  • 元素上可用的任何default-autowire-candidates模式
  • @Qualifier批注以及在CustomAutowireConfigurer中注册的所有自定义注解的存在

当多个bean符合自动装配候选条件时,“主要”的确定如下:如果候选中恰好有一个bean定义的primary属性设置为true,则将其选中。

9.7用@Resource注入

Spring还通过对字段或bean属性设置器方法使用JSR-250 @Resource注解(javax.annotation.Resource)支持注入。 这是Java EE中的常见模式:例如,在JSF管理的Bean和JAX-WS端点中。 Spring也为Spring管理的对象支持此模式。

@Resource具有名称属性。 默认情况下,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用法的特殊情况下,未指定显式名称,并且类似于@ Autowired@ Resource查找主类型匹配而不是特定的命名bean,并解析众所周知的可解决的依赖项:BeanFactoryApplicationContextResourceLoaderApplicationEventPublisherMessageSource接口。

因此,在以下示例中,customerPreferenceDao字段首先查找名为“ customerPreferenceDao”的bean,然后回退到类型为CustomerPreferenceDao的主类型匹配项:

public class MovieRecommender {

    @Resource
    private CustomerPreferenceDao customerPreferenceDao;

    @Resource
    private ApplicationContext context; 

    public MovieRecommender() {
    }

    // ...
}

9.8使用@Value

@Value通常用于注入外部属性:

@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("${catalog.name}") String catalog) {
        this.catalog = catalog;
    }
}

使用以下配置:

@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }

以及以下application.properties文件:

catalog.name=MovieCatalog

在这种情况下,目录参数和字段将等于MovieCatalog值。

Spring提供了一个默认的宽松内嵌值解析器。 它将尝试解析属性值,如果无法解析,则将属性名称(例如$ {catalog.name})作为值注入。 如果要严格控制不存在的值,则应声明一个PropertySourcesPlaceholderConfigurer bean,如以下示例所示:

@Configuration
public class AppConfig {

     @Bean
     public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
           return new PropertySourcesPlaceholderConfigurer();
     }
}

注意:

  • 当使用JavaConfig配置PropertySourcesPlaceholderConfigurer时,@Bean方法必须是静态的。

如果无法解析任何$ {}占位符,则使用上述配置可确保Spring初始化失败。 也可以使用setPlaceholderPrefixsetPlaceholderSuffixsetValueSeparator之类的方法来自定义占位符。

注意:

  • Spring Boot默认配置一个PropertySourcesPlaceholderConfigurer bean,它将从application.propertiesapplication.yml文件中获取属性。

Spring提供的内置转换器支持允许自动处理简单的类型转换(例如,转换为Integer或int)。 多个逗号分隔的值可以自动转换为String数组,而无需付出额外的努力。

可以提供如下默认值:

@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("${catalog.name:defaultCatalog}") String catalog) {
        this.catalog = catalog;
    }
}

Spring BeanPostProcessor在后台使用ConversionService处理将@Value中的String值转换为目标类型的过程。 如果要为自己的自定义类型提供转换支持,则可以提供自己的ConversionService bean实例,如以下示例所示:

@Configuration
public class AppConfig {

    @Bean
    public ConversionService conversionService() {
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
        conversionService.addConverter(new MyCustomConverter());
        return conversionService;
    }
}

@Value包含SpEL表达式时,该值将在运行时动态计算,如以下示例所示:

@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) {
        this.catalog = catalog;
    }
}

SpEL还支持使用更复杂的数据结构:

@Component
public class MovieRecommender {

    private final Map<String, Integer> countOfMoviesPerCatalog;

    public MovieRecommender(
            @Value("#{{'Thriller': 100, 'Comedy': 300}}") Map<String, Integer> countOfMoviesPerCatalog) {
        this.countOfMoviesPerCatalog = countOfMoviesPerCatalog;
    }
}

9.9使用@PostConstruct@PreDestroy

CommonAnnotationBeanPostProcessor不仅可以识别@Resource注解,还可以识别JSR-250生命周期注释:javax.annotation.PostConstructjavax.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...
    }
}

注意:

  • @Resource一样,@ PostConstruct@PreDestroy批注类型是JDK 6到8的标准Java库的一部分。但是,整个javax.annotation包与JDK 9中的核心Java模块分离,并最终在JDK 11中删除了。 如果需要,现在需要通过Maven Central获取javax.annotation-api工件,只需像其他任何库一样将其添加到应用程序的类路径中即可。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值