首页 > 其他分享 >Spring 框架的核心技术(二)

Spring 框架的核心技术(二)

时间:2022-11-16 14:08:15浏览次数:80  
标签:框架 示例 核心技术 Spring class 注释 Bean public

Spring 框架的核心技术(二)_限定符

1.9. 基于注释的容器配置

在配置 Spring 方面,注释是否比 XML 更好?

基于注释的配置的引入提出了一个问题,即这是否 方法比 XML “更好”。简短的回答是“视情况而定”。长答案是 每种方法都有其优点和缺点,通常,这取决于开发人员 决定哪种策略更适合他们。由于它们的定义方式,注释 在他们的声明中提供了大量的上下文,导致更短更简洁 配置。但是,XML 擅长在不接触组件源的情况下连接组件。 编码或重新编译它们。一些开发人员更喜欢将布线靠近源 而其他人则认为注释类不再是POJO,此外,注释类 配置变得分散且难以控制。

无论选择哪种选择,Spring都可以容纳两种风格,甚至可以将它们混合在一起。 值得指出的是,通过其JavaConfig选项,Spring让 注释以非侵入性方式使用,无需接触目标组件 源代码,并且在工具方面,SpringTools for Eclipse 支持所有配置样式。

基于 XML 设置的替代方法由基于注释的配置提供,它依赖于 用于连接组件的字节码元数据,而不是尖括号声明。 开发人员不使用 XML 来描述 Bean 连接,而是移动配置 通过使用相关类、方法或 字段声明。如示例中所述:自动连线注释BeanPostProcessor,使用 ain 与注释结合使用是扩展 春季 IoC 容器。例如,Spring 2.0引入了强制执行的可能性。 带有@Required批注的必需属性。春天 2.5 使得遵循相同的一般方法来驱动 Spring 的依赖成为可能 注射。从本质上讲,theannotation 提供了与 在自动布线协作者中进行了描述,但具有更细粒度的控制范围和更广泛的控制 适用性。Spring 2.5 还增加了对 JSR-250 注释的支持,例如 and。Spring 3.0增加了对JSR-330(依赖关系)的支持 Injection for Java)包含在包中的注释,例如and。有关这些注释的详细信息,请参阅相关部分。​​BeanPostProcessor​​​​@Autowired​​​​@PostConstruct​​​​@PreDestroy​​​​javax.inject​​​​@Inject​​​​@Named​

与往常一样,您可以将后处理器注册为单独的 Bean 定义,但它们 也可以通过在基于 XML 的 Spring 中包含以下标记来隐式注册 配置(注意包含命名空间):​​context​

<?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>

该元素隐式注册以下后处理器:​​<context:annotation-config/>​

  • ConfigurationClassPostProcessor
  • AutowiredAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor
  • PersistenceAnnotationBeanPostProcessor
  • EventListenerMethodProcessor

1.9.1. @Required

注释适用于 Bean 属性 setter 方法,如下所示 例:​​@Required​

public class SimpleMovieLister {

private MovieFinder movieFinder;

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

// ...
}

此注释指示受影响的 Bean 属性必须填充在 配置时间,通过 Bean 定义中的显式属性值或通过 自动接线。如果受影响的 Bean 属性尚未 填充。这允许预先和显式失败,避免以后的实例或类似情况。我们仍然建议你将断言放入 Bean 类本身(例如,放入 init 方法中)。这样做会强制执行所需的内容 引用和值,即使在容器外部使用该类也是如此。​​NullPointerException​

1.9.2. 使用​​@Autowired​

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

public class MovieRecommender {

private final CustomerPreferenceDao customerPreferenceDao;

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

// ...
}

您还可以将注释应用于传统的二传手方法, 如以下示例所示:​​@Autowired​

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

// ...
}

您还可以指示 Spring 提供特定类型的所有 bean,方法是将注释添加到字段或方法 需要该类型的数组,如以下示例所示:​​ApplicationContext​​​​@Autowired​

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,键包含 相应的 Bean 名称,如以下示例所示:​​Map​​​​String​

public class MovieRecommender {

private Map<String, MovieCatalog> movieCatalogs;

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

// ...
}

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

默认行为是将带批注的方法和字段视为指示必需 依赖。您可以更改此行为,如以下示例所示, 使框架能够通过将其标记为 非必需(即,通过将属性设置为):​​required​​​​@Autowired​​​​false​

public class SimpleMovieLister {

private MovieFinder movieFinder;

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

// ...
}

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

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

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

public class SimpleMovieLister {

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

从Spring Framework 5.0开始,您还可以使用aannotation(任何类型的注释) 在任何软件包中 — 例如,来自 JSR-305)或只是利用 Kotlin 内置的零安全支持:​​@Nullable​​​​javax.annotation.Nullable​

public class SimpleMovieLister {

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

您还可以用于已知可解析的接口 依赖关系:,,,,,和。这些接口及其扩展 接口(如 asor)是 自动解决,无需特殊设置。以下示例自动连线 对象:​​@Autowired​​​​BeanFactory​​​​ApplicationContext​​​​Environment​​​​ResourceLoader​​​​ApplicationEventPublisher​​​​MessageSource​​​​ConfigurableApplicationContext​​​​ResourcePatternResolver​​​​ApplicationContext​

public class MovieRecommender {

@Autowired
private ApplicationContext context;

public MovieRecommender() {
}

// ...
}

1.9.3. 微调基于注释的自动布线​​@Primary​

由于按类型自动布线可能会导致多个候选者,因此通常需要 更好地控制选择过程。实现此目的的一种方法是使用 Spring'sannotation.表示应该给出一个特定的 bean。 当多个 bean 是自动连接到单值的候选对象时的首选项 屬地。如果候选者中只存在一个主 Bean,则它将成为 自动连线值。​​@Primary​​​​@Primary​

请考虑以下配置,该配置定义为 主要:​​firstMovieCatalog​​​​MovieCatalog​

@Configuration
public class MovieConfiguration {

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

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

// ...
}

使用上述配置,以下内容将自动连接:​​MovieRecommender​​​​firstMovieCatalog​

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​​是按类型使用自动布线的有效方法,当一个实例有多个实例时 可以确定主要候选人。当您需要更好地控制选择过程时, 你可以使用春天的三注。可以关联限定符值 使用特定参数,缩小类型匹配集,以便特定 Bean 是 为每个参数选择。在最简单的情况下,这可以是一个普通的描述性值,如 如以下示例所示:​​@Qualifier​

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 名称被视为默认限定符值。因此,您 可以用 anof 代替嵌套限定符元素定义 bean,导致 到相同的匹配结果。但是,尽管您可以使用此约定来引用 按名称命名的特定 bean 从根本上讲是关于类型驱动的注入 可选的语义限定符。这意味着限定符值,即使带有 Bean 名称 回退,始终在类型匹配集中具有缩小语义。他们没有 语义上表示对唯一 Bean 的引用。良好的限定符值 areoror,表示特定组件的特征 独立于 Bean,在匿名 Bean 的情况下可以自动生成 定义,例如前面示例中的定义。​​id​​​​main​​​​@Autowired​​​​id​​​​main​​​​EMEA​​​​persistent​​​​id​

限定符也适用于类型化集合,如前所述,例如 to。在这种情况下,所有匹配的豆子,根据声明 限定符作为集合注入。这意味着限定符不必是 独特。相反,它们构成了过滤标准。例如,您可以定义 具有相同限定符值“action”的 multiplebeans,所有这些都是 注入到注释中。​​Set<MovieCatalog>​​​​MovieCatalog​​​​Set<MovieCatalog>​​​​@Qualifier("action")​

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

对于本身定义为集合或数组类型的 bean 来说,是一个很好的解决方案,通过唯一名称引用特定的集合或数组 bean。 也就是说,从 4.3 开始,你也可以通过 Spring 的类型匹配算法匹配集合、和数组类型,只要元素类型信息 保留返回类型签名或集合继承层次结构。 在这种情况下,可以使用限定符值在相同类型的集合中进行选择, 如上一段所述。​​Map​​​​@Resource​​​​Map​​​​@Autowired​​​​@Bean​

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

​@Autowired​​​适用于字段、构造函数和多参数方法,允许 缩小参数级别的限定符注释的范围。相比之下,仅支持具有单个参数的字段和 Bean 属性 setter 方法。 因此,如果您的注射目标是 构造函数或多参数方法。​​@Resource​

您可以创建自己的自定义限定符批注。为此,请定义注释和 在定义中提供注释,如以下示例所示:​​@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 定义的信息。您可以将标签添加为标签的子元素,然后指定 theand以匹配您的自定义限定符批注。类型与 批注的完全限定类名。或者,为了方便,如果没有风险 存在冲突的名称,您可以使用短类名。以下示例 演示了这两种方法:​​<qualifier/>​​​​<bean/>​​​​type​​​​value​

<?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 中提供限定符元数据。具体而言,请参阅提供带有批注的限定符元数据。

在某些情况下,使用没有值的批注可能就足够了。这可以是 当注释用于更通用的目的并且可以应用于 几种不同类型的依赖项。例如,您可以提供离线 在没有互联网连接可用时可以搜索的目录。首先,定义 简单批注,如以下示例所示:

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

}

然后将批注添加到要自动连线的字段或属性,如 以下示例:

public class MovieRecommender {

@Autowired
@Offline
private MovieCatalog offlineCatalog;

// ...
}

现在 Bean 定义只需要一个限定符,如以下示例所示:​​type​

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

您还可以定义接受 添加或代替简单属性。如果多个属性值为 然后在要自动连线的字段或参数上指定,Bean 定义必须匹配 所有这些属性值都将被视为自动连线候选项。举个例子, 请考虑以下注释定义:​​value​

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

String genre();

Format format();
}

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

public enum Format {
VHS, DVD, BLURAY
}

要自动连线的字段使用自定义限定符进行批注,并包含值 对于这两个属性:和,如以下示例所示:​​genre​​​​format​

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 元属性而不是元素。如果可用,元素及其属性将 优先级,但如果不存在此类限定符,则自动连线机制将回退到 thetags 中提供的值,如 以下示例:​​<qualifier/>​​​​<qualifier/>​​​​<meta/>​

<?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. 使用泛型作为自动连线限定符

除了注释之外,您还可以使用 Java 泛型类型 作为一种隐含的资格形式。例如,假设您有以下内容 配置:​​@Qualifier​

@Configuration
public class MyConfiguration {

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

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

假设前面的 bean 实现了一个泛型接口,(即 and),你可以接口和泛型是 用作限定符,如以下示例所示:​​Store<String>​​​​Store<Integer>​​​​@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​​​​List​

// 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. 使用​​CustomAutowireConfigurer​

CustomAutowireConfigurer是一个允许您注册自己的自定义限定符 注释类型,即使它们没有使用 Spring 的注释进行注释。 以下示例演示如何使用:​​BeanFactoryPostProcessor​​​​@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<beans/>
  • 注释的存在和注册的任何自定义注释 与@QualifierCustomAutowireConfigurer

当多个 bean 符合自动连线候选条件时,“主要”的确定为 如下所示:如果候选项中只有一个 Bean 定义将 aattribute 设置为,则选择该定义。​​primary​​​​true​

1.9.7. 注入​​@Resource​

Spring 还支持使用 JSR-250 注释进行注入。 () 在字段或 Bean 属性 setter 方法上。 这是Java EE中的常见模式:例如,在JSF管理的Bean和JAX-WS中。 端点。Spring 也支持 Spring 管理的对象的这种模式。​​@Resource​​​​javax.annotation.Resource​

​@Resource​​采用名称属性。默认情况下,Spring 将该值解释为 要注入的 Bean 名称。换句话说,它遵循按名称语义, 如以下示例所示:

public class SimpleMovieLister {

private MovieFinder movieFinder;

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

如果未显式指定名称,则默认名称派生自字段名称或 二传手法。如果是字段,则采用字段名称。如果是二传手方法, 它采用 Bean 属性名称。下面的示例将具有 bean 命名注入到其二传手方法中:​​movieFinder​

public class SimpleMovieLister {

private MovieFinder movieFinder;

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

在未指定显式名称的专用用法中,以及类似情况 to,查找主要类型匹配项而不是特定命名的 Bean 并解析众所周知的可解析依赖项:,,,, 和接口。​​@Resource​​​​@Autowired​​​​@Resource​​​​BeanFactory​​​​ApplicationContext​​​​ResourceLoader​​​​ApplicationEventPublisher​​​​MessageSource​

因此,在下面的示例中,该字段首先查找 bean 名为“客户偏好道”,然后回退到该类型的主要类型匹配:​​customerPreferenceDao​​​​CustomerPreferenceDao​

public class MovieRecommender {

@Resource
private CustomerPreferenceDao customerPreferenceDao;

@Resource
private ApplicationContext context;

public MovieRecommender() {
}

// ...
}

1.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

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

默认的宽松嵌入式价值解析器由 Spring 提供。它将尝试解决 属性值,如果无法解析,则为属性名称(例如) 将被注入为值。如果你想保持对不存在的严格控制 值,您应该声明 abean,如下所示 示例显示:​​${catalog.name}​​​​PropertySourcesPlaceholderConfigurer​

@Configuration
public class AppConfig {

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

使用上述配置可确保在无法解析任何占位符时 Spring 初始化失败。也可以使用诸如,,或自定义之类的方法 占位符。​${}​​​​setPlaceholderPrefix​​​​setPlaceholderSuffix​​​​setValueSeparator​

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

可以提供默认值,如下所示:

@Component
public class MovieRecommender {

private final String catalog;

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

一个春天在幕后处理 将值转换为目标类型的过程。如果你愿意 为您自己的自定义类型提供转换支持,您可以提供自己的 Bean 实例,如以下示例所示:​​BeanPostProcessor​​​​ConversionService​​​​String​​​​@Value​​​​ConversionService​

@Configuration
public class AppConfig {

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

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

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

1.9.9. 使用和​​@PostConstruct​​​​@PreDestroy​

不仅识别注释 还有JSR-250生命周期注释:和。在Spring 2.5中引入,支持这些 注解提供了初始化回调和销毁回调中描述的生命周期回调机制的替代方法。前提是在春季内注册, 携带这些批注之一的方法在生命周期的同一点调用 作为相应的 Spring 生命周期接口方法或显式声明的回调 方法。在以下示例中,缓存在初始化时预填充,并且 销毁时清除:​​CommonAnnotationBeanPostProcessor​​​​@Resource​​​​javax.annotation.PostConstruct​​​​javax.annotation.PreDestroy​​​​CommonAnnotationBeanPostProcessor​​​​ApplicationContext​

public class CachingMovieLister {

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

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

关于组合各种生命周期机制的效果,请参见组合生命周期机制。

1.10. 类路径扫描和托管组件

本章中的大多数示例都使用 XML 来指定生成 每个都在弹簧容器内。上一节 (基于注释的容器配置)演示如何提供大量配置 元数据通过源代码级注释。然而,即使在这些例子中,“基础” Bean 定义在 XML 文件中显式定义,而注释仅驱动 依赖注入。本节介绍用于隐式检测 通过扫描类路径的候选组件。候选组件是具有以下特征的类 与过滤条件匹配,并注册相应的 Bean 定义 容器。这样就无需使用 XML 来执行 Bean 注册。相反,你 可以使用注释(例如)、AspectJ 类型表达式或您自己的表达式 自定义过滤条件,用于选择注册了哪些类的 Bean 定义 容器。​​BeanDefinition​​​​@Component​

1.10.1.和进一步的构造型注释​​@Component​

注释是履行角色或 存储库的构造型(也称为数据访问对象或 DAO)。用途包括 此标记是异常的自动转换,如异常转换中所述。​​@Repository​

Spring 提供了进一步的构造型注释:,,and.is 任何 Spring 管理的组件的通用构造型。 更具体的用例(在持久性、服务和演示中 层,分别)。因此,您可以用 注释组件类,但是,通过用 注释它们,或者相反,您的类更适合通过工具进行处理或关联 与方面。例如,这些构造型批注是 切入点,,也可以 在 Spring 框架的未来版本中携带额外的语义。因此,如果你是 选择使用或为您的服务层,是 显然是更好的选择。同样,如前所述,已经 支持作为持久性层中自动异常转换的标记。​​@Component​​​​@Service​​​​@Controller​​​​@Component​​​​@Repository​​​​@Service​​​​@Controller​​​​@Component​​​​@Component​​​​@Repository​​​​@Service​​​​@Controller​​​​@Repository​​​​@Service​​​​@Controller​​​​@Component​​​​@Service​​​​@Service​​​​@Repository​

1.10.2. 使用元注释和组合注释

Spring 提供的许多注释都可以用作 自己的代码。元注释是可应用于另一个注释的注释。 例如,前面提到的注释是元注释的,如以下示例所示:​​@Service​​​​@Component​

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

// ...
}

您还可以组合元注释以创建“组合注释”。例如 来自Spring MVC的注释由and组成。​​@RestController​​​​@Controller​​​​@ResponseBody​

此外,组合批注可以选择重新声明属性 元注释以允许自定义。这在以下情况下特别有用: 希望仅公开元注释属性的子集。例如,Spring'sannotation 对范围名称进行硬编码,但仍允许 的定制。以下清单显示了注释的定义:​​@SessionScope​​​​session​​​​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 {
// ...
}

有关更多详细信息,请参阅Spring 注释编程模型wiki 页面。

1.10.3. 自动检测类并注册 Bean 定义

Spring 可以自动检测构造型类并注册相应的实例。例如,以下两个类 符合此类自动检测的条件:​​BeanDefinition​​​​ApplicationContext​

@Service
public class SimpleMovieLister {

private MovieFinder movieFinder;

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>

此外,当您使用 组件扫描元素。这意味着这两个组件是自动检测的,并且 连接在一起 — 所有这些都没有以 XML 形式提供的任何 Bean 配置元数据。​​AutowiredAnnotationBeanPostProcessor​​​​CommonAnnotationBeanPostProcessor​

1.10.4. 使用过滤器自定义扫描

默认情况下,批注的类,,,,,或本身批注的自定义批注为 唯一检测到的候选组件。但是,您可以修改和扩展此行为 通过应用自定义筛选器。添加它们的属性 theannotation(或元素的asorchild元素在 XML 配置)。每个过滤器元素都需要 and属性。 下表描述了筛选选项:​​@Component​​​​@Repository​​​​@Service​​​​@Controller​​​​@Configuration​​​​@Component​​​​includeFilters​​​​excludeFilters​​​​@ComponentScan​​​​<context:include-filter />​​​​<context:exclude-filter />​​​​<context:component-scan>​​​​type​​​​expression​

Table 5. Filter Types

过滤器类型

示例表达式

描述

批注(默认)

​org.example.SomeAnnotation​

在目标组件的类型级别存在或元存在的注释。

可分配的

​org.example.SomeClass​

目标组件可分配到(扩展或实现)的类(或接口)。

Aspectj

​org.example..*Service+​

要由目标组件匹配的 AspectJ 类型表达式。

正则表达式

​org\.example\.Default.*​

要与目标组件的类名匹配的正则表达式。

习惯

​org.example.MyTypeFilter​

接口的自定义实现。​​org.springframework.core.type.TypeFilter​

以下示例显示了忽略同义注释的配置 并改用“存根”存储库:​​@Repository​

@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
// ...
}

下面的清单显示了等效的 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>

1.10.5. 在组件中定义 Bean 元数据

Spring 组件还可以向容器贡献 Bean 定义元数据。你可以做 这与用于在带注释的类中定义 Bean 元数据的相同注解。以下示例演示如何执行此操作:​​@Bean​​​​@Configuration​

@Component
public class FactoryMethodComponent {

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

public void doWork() {
// Component method implementation omitted
}
}

前面的类是一个 Spring 组件,其方法中具有特定于应用程序的代码。但是,它也贡献了一个具有工厂的 bean 定义 方法是指方法。注释标识 工厂方法和其他 Bean 定义属性,例如限定符值 注释。可以指定的其他方法级批注包括 、 和自定义限定符批注。​​doWork()​​​​publicInstance()​​​​@Bean​​​​@Qualifier​​​​@Scope​​​​@Lazy​

如前所述,支持自动连线字段和方法,并附加 支持方法的自动连线。以下示例演示如何执行此操作:​​@Bean​

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

该示例将方法参数自动连接到另一个命名的 Bean 上的属性值。Spring 表达式语言元素 通过表示法定义属性的值。Forannotations,表达式解析器预配置为在以下情况下查找 Bean 名称 解析表达式文本。​​String​​​​country​​​​age​​​​privateInstance​​​​#{ <expression> }​​​​@Value​

从 Spring Framework 4.3 开始,您还可以声明 type(或其更具体的子类:)的工厂方法参数自 访问触发当前 Bean 创建的请求注入点。 请注意,这仅适用于 Bean 实例的实际创建,不适用于 注入现有实例。因此,此功能对于 原型范围的豆子。对于其他作用域,工厂方法只看到 触发在给定范围内创建新 Bean 实例的注入点 (例如,触发创建惰性单例 Bean 的依赖项)。 在这种情况下,可以将提供的注入点元数据与语义处理一起使用。 以下示例演示如何使用:​​InjectionPoint​​​​DependencyDescriptor​​​​InjectionPoint​

@Component
public class FactoryMethodComponent {

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

常规 Spring 组件中的方法处理方式与其不同 春班内的同行。不同之处在于,类没有使用 CGLIB 来拦截方法和字段的调用。 CGLIB 代理是调用方法或方法中的字段的方法 Inclasses 创建对协作对象的 Bean 元数据引用。 这些方法不是用普通的Java语义调用的,而是通过 容器,以提供Spring的常规生命周期管理和代理 bean,即使通过编程调用 to 方法引用其他 bean。 相比之下,在普通类中调用方法或字段中的方法或字段具有标准的Java语义,没有特殊的CGLIB处理或其他 约束适用。​​@Bean​​​​@Configuration​​​​@Component​​​​@Bean​​​​@Configuration​​​​@Bean​​​​@Bean​​​​@Component​

1.10.6. 命名自动检测的组件

当组件作为扫描过程的一部分被自动检测时,其 Bean 名称为 由该扫描仪已知的策略生成。默认情况下,任何 包含名称的 Spring 构造型注释 (,,, 和) 从而将该名称提供给 相应的 Bean 定义。​​BeanNameGenerator​​​​@Component​​​​@Repository​​​​@Service​​​​@Controller​​​​value​

如果此类注释不包含任何其他检测到的组件的名称或 (例如自定义过滤器发现的那些),默认 Bean 名称生成器返回 未大写的非限定类名。例如,如果以下组件 检测到类,名称将是:​​value​​​​myMovieLister​​​​movieFinderImpl​

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

如果不想依赖默认的 Bean 命名策略,可以提供一个自定义 Bean 命名策略。首先,实现BeanNameGenerator接口,并确保包含一个默认的 no-arg 构造函数。然后,提供完整的 配置扫描程序时的限定类名,如以下示例注释 和豆定义显示。

@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. 为自动检测的组件提供范围

与一般的 Spring 管理的组件一样,默认和最常见的范围 自动检测的组件是。但是,有时您需要不同的范围 这可以通过注释来指定。您可以提供 批注中的范围,如以下示例所示:​​singleton​​​​@Scope​

@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}

有关特定于Web的范围的详细信息,例如Spring上下文中的“请求”或“会话”, 请参阅请求、会话、应用程序和 WebSocket 作用域​。与这些范围的预构建注释一样, 您也可以使用 Spring 的元注释来编写自己的范围注释 方法:例如,一个自定义注释的元注释, 可能还会声明自定义作用域代理模式。​​@Scope("prototype")​

@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 中描述为依赖项。 为此,组件扫描上提供了作用域代理属性 元素。三个可能的值是:、和。例如 以下配置将生成标准 JDK 动态代理:​​no​​​​interfaces​​​​targetClass​

@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. 提供带有注释的限定符元数据

注释在微调基于注释的自动连线与限定符中讨论。 该部分中的示例演示了注释和 自定义限定符批注,用于在解析自动连线时提供精细控制 候选人。因为这些示例基于 XML Bean 定义,所以限定符 通过使用 XML 中元素的 theorchild 元素,在候选 Bean 定义上提供了元数据。当依赖类路径扫描 自动检测组件,可以提供类型级别的限定符元数据 候选类的注释。以下三个示例演示了这一点 技术:​​@Qualifier​​​​@Qualifier​​​​qualifier​​​​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 {
// ...
}

1.10.9. 生成候选组件的索引

虽然类路径扫描非常快,但可以提高启动性能 通过在编译时创建候选的静态列表来对大型应用程序进行备份。在此 模式下,作为组件扫描目标的所有模块都必须使用此机制。

若要生成索引,请向包含以下内容的每个模块添加额外的依赖项 作为组件扫描指令目标的组件。以下示例显示 如何使用Maven执行此操作:

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

对于 Gradle 4.5 及更早版本,应在配置中声明依赖项,如以下示例所示:​​compileOnly​

dependencies {
compileOnly "org.springframework:spring-context-indexer:5.3.23"
}

对于 Gradle 4.6 及更高版本,应在配置中声明依赖项,如以下示例所示:​​annotationProcessor​

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

Theartifact 生成一个文件 包含在 JAR 文件中。​​spring-context-indexer​​​​META-INF/spring.components​

1.11. 使用 JSR 330 标准注释

从Spring 3.0开始,Spring提供了对JSR-330标准注释的支持。 (依赖注入)。这些注释的扫描方式与 Spring 相同 附注。要使用它们,您需要在类路径中包含相关的 jar。

1.11.1. 依赖注入​​@Inject​​​​@Named​

相反,您可以使用如下:​​@Autowired​​​​@javax.inject.Inject​

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(...);
// ...
}
}

与一样,您可以在字段级别,方法级别使用 和构造函数参数级别。此外,您可以将注入点声明为 a,允许按需访问范围较短的 bean 或延迟访问 其他豆子通过 Acall。以下示例提供了 前面的示例:​​@Autowired​​​​@Inject​​​​Provider​​​​Provider.get()​

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​​​​required​​​​@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​​​​@ManagedBean​​​​@Component​

取而代之的是,您可以使用或 如以下示例所示:​​@Component​​​​@javax.inject.Named​​​​javax.annotation.ManagedBean​

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

// ...
}

使用而不指定组件的名称是很常见的。可以以类似的方式使用,如以下示例所示:​​@Component​​​​@Named​

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

@Named
public class SimpleMovieLister {

private MovieFinder movieFinder;

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

// ...
}

使用时,可以在 与使用 Spring 注释时完全相同的方式,如以下示例所示:​​@Named​​​​@ManagedBean​

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

1.11.3. JSR-330 标准注释的限制

当您使用标准注释时,您应该知道一些重要的 功能不可用,如下表所示:

Table 6. Spring component model elements versus JSR-330 variants

春天

javax.inject.*

javax.inject restrictions / comments

@Autowired

@Inject

​@Inject​​​没有“必需”属性。可以与Java 8一起使用。​​Optional​

@Component

@Named / @ManagedBean

JSR-330不提供可组合模型,仅提供一种识别命名组件的方法。

@Scope(“单例”)

@Singleton

JSR-330的默认示波器与Spring的类似。但是,为了保持它 与 Spring 的一般默认值一致,在 Spring 中声明了一个 JSR-330 bean 默认情况下,容器是 a。为了使用除以下范围以外的范围: 你应该使用 Spring'sannotation.还提供了 a​​@Scope​​​注释。 但是,此注释仅用于创建自己的注释。​​prototype​​​​singleton​​​​singleton​​​​@Scope​​​​javax.inject​

@Qualifier

@Qualifier / @Named

​javax.inject.Qualifier​​​只是用于构建自定义限定符的元注释。 具体限定符(如带有值的弹簧)可以关联 通过。​​String​​​​@Qualifier​​​​javax.inject.Named​

@Value

-

没有等效物

@Required

-

没有等效物

@Lazy

-

没有等效物

对象工厂

供应商

​javax.inject.Provider​​​是春天的直接替代品, 仅使用较短的方法名称。它也可以与 带有非注释构造函数和二传手方法的 Spring'sor。​​ObjectFactory​​​​get()​​​​@Autowired​

1.12. 基于 Java 的容器配置

本节介绍如何在 Java 代码中使用注释来配置 Spring 容器。它包括以下主题:

  • 基本概念:@Bean和@Configuration
  • 使用AnnotationConfigApplicationContext 实例化 Spring 容器
  • 使用@Bean注释
  • 使用@Configuration注释
  • 编写基于 Java 的配置
  • Bean 定义配置文件
  • 属性来源抽象化
  • 使用@PropertySource
  • 语句中的占位符解析

1.12.1. 基本概念:和​​@Bean​​​​@Configuration​

Spring 新的 Java 配置支持的核心工件是带注释的类和注释的方法。​​@Configuration​​​​@Bean​

注释用于指示方法实例化、配置和 初始化要由 Spring IoC 容器管理的新对象。对于那些熟悉的人 使用Spring的XML配置,theannotation扮演着与 元素。您可以在任何 Spring 中使用带注释的方法。然而,它们最常与豆类一起使用。​​@Bean​​​​<beans/>​​​​@Bean​​​​<bean/>​​​​@Bean​​​​@Component​​​​@Configuration​

对类进行注释表明其主要用途是作为 豆子定义的来源。此外,类让豆间 通过在同一类中调用其他方法来定义依赖项。 最简单的类如下所示:​​@Configuration​​​​@Configuration​​​​@Bean​​​​@Configuration​

@Configuration
public class AppConfig {

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

前面的类等效于下面的 SpringXML:​​AppConfig​​​​<beans/>​

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

全@Configuration与“精简”@Bean模式?

当方法在没有注释的类中声明时,它们被称为以“精简”模式处理。豆子方法 即使在普通的旧类中,在AOR中声明也被认为是“轻量级”, 具有包含类和方法的不同主要目的 是一种奖励。例如,服务组件可能会公开管理视图 通过每个适用的组件类上的附加方法到容器。 在这种情况下,方法是通用的工厂方法机制。​​@Bean​​​​@Configuration​​​​@Component​​​​@Bean​​​​@Bean​​​​@Bean​

与 full 不同,litemethods 不能声明 Bean 间的依赖关系。 相反,它们对其包含组件的内部状态进行操作,并且可以选择在 他们可能宣布的论点。因此,这样的方法不应该调用其他方法。每个这样的方法实际上只是特定方法的工厂方法 Bean 引用,没有任何特殊的运行时语义。这里的积极副作用是 在运行时不必应用 CGLIB 子类化,因此在 类设计术语(即,包含类可以是等等)。​​@Configuration​​​​@Bean​​​​@Bean​​​​@Bean​​​​final​

在常见场景中,方法将在类内声明, 确保始终使用“完整”模式,并因此引用跨方法 重定向到容器的生命周期管理。这可以防止通过常规 Java 调用意外调用相同的方法,这有助于 减少在“精简”模式下操作时难以追踪的细微错误。​​@Bean​​​​@Configuration​​​​@Bean​

以下各节将深入讨论注释。 但是,首先,我们将介绍使用 基于 Java 的配置。​​@Bean​​​​@Configuration​

1.12.2. 使用​​AnnotationConfigApplicationContext​

以下各节记录了春季的,在春季介绍 3.0. 这种多功能实现不仅能够接受类作为输入,还能够接受普通类和类 使用 JSR-330 元数据进行注释。​​AnnotationConfigApplicationContext​​​​ApplicationContext​​​​@Configuration​​​​@Component​

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

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

结构简单

就像在实例化 a 时将 Spring XML 文件用作输入一样,您可以在以下情况下使用类作为输入。 实例化一个。这允许完全 Spring 容器的无 XML 使用,如以下示例所示:​​ClassPathXmlApplicationContext​​​​@Configuration​​​​AnnotationConfigApplicationContext​

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

如前所述,不仅限于工作 与类。可以提供任意或JSR-330注释类 作为构造函数的输入,如以下示例所示:​​AnnotationConfigApplicationContext​​​​@Configuration​​​​@Component​

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

前面的示例假定,,并使用 Spring 依赖注入批注,例如。​​MyServiceImpl​​​​Dependency1​​​​Dependency2​​​​@Autowired​

使用 以编程方式生成容器​​register(Class<?>…)​

您可以使用 no-arg 构造函数实例化 anby 然后使用该方法对其进行配置。这种方法特别有用 以编程方式构建时。以下 示例显示了如何执行此操作:​​AnnotationConfigApplicationContext​​​​register()​​​​AnnotationConfigApplicationContext​

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();
}
启用组件扫描​​scan(String…)​

要启用组件扫描,您可以按如下方式注释您的类:​​@Configuration​

@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig {
// ...
}

在前面的示例中,扫描包以查找任何带注释的类,这些类被注册为 Spring bean 容器中的定义。公开方法以允许与 以下示例显示:​​com.acme​​​​@Component​​​​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);
}
支持 Web 应用程序​​AnnotationConfigWebApplicationContext​

提供变种 跟。您可以在以下情况下使用此实现 配置 Springservlet 侦听器、Spring MVC 等。以下代码片段配置了一个典型的 Spring MVC Web 应用程序(注意上下文参数和 init-param):​​WebApplicationContext​​​​AnnotationConfigApplicationContext​​​​AnnotationConfigWebApplicationContext​​​​ContextLoaderListener​​​​DispatcherServlet​​​​web.xml​​​​contextClass​

<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. 使用注解​​@Bean​

​@Bean​​是方法级批注和 XML元素的直接模拟。 注释支持 提供的一些属性,例如:​​<bean/>​​​​<bean/>​

  • 初始化方法
  • 销毁方法
  • 自动接线
  • ​name​​.

您可以在 a 注释或 a 注释类中使用注释。​​@Bean​​​​@Configuration​​​​@Component​

声明豆

要声明 bean,您可以使用注释来注释方法。你用这个 在类型中注册 Bean 定义的方法 指定为方法的返回值。默认情况下,Bean 名称与 方法名称。下面的示例演示方法声明:​​@Bean​​​​ApplicationContext​​​​@Bean​

@Configuration
public class AppConfig {

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

前面的配置完全等同于下面的Spring XML:

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

这两个声明都使 Bean 命名为 可用 在 中,绑定到类型的对象实例,作为 以下文本图像显示:​​transferService​​​​ApplicationContext​​​​TransferServiceImpl​

transferService -> com.acme.TransferServiceImpl

您还可以使用默认方法来定义 Bean。这允许豆子的组成 通过在缺省方法上使用 Bean 定义实现接口进行配置。

public interface BaseConfig {

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

@Configuration
public class AppConfig implements BaseConfig {

}

您还可以使用接口(或基类)声明您的方法 返回类型,如以下示例所示:​​@Bean​

@Configuration
public class AppConfig {

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

但是,这会将高级类型预测的可见性限制为指定的 接口类型 ()。然后,使用完整类型 () 只有在实例化受影响的单例 Bean 后,容器才知道。 非懒惰的单例 bean 根据它们的声明顺序进行实例化, 因此,您可能会看到不同的类型匹配结果,具体取决于另一个组件的时间 尝试按未声明的类型(例如, 仅在 thebean 实例化后解析)。​​TransferService​​​​TransferServiceImpl​​​​@Autowired TransferServiceImpl​​​​transferService​

Bean 依赖项

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

@Configuration
public class AppConfig {

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

解析机制与基于构造函数的依赖项几乎相同 注射。有关更多详细信息,请参阅相关部分。

接收生命周期回调

使用注释定义的任何类都支持常规生命周期回调 并且可以使用 JSR-250 中的注释。有关进一步内容,请参阅JSR-250 注释 详。​​@Bean​​​​@PostConstruct​​​​@PreDestroy​

完全支持常规的 Spring生命周期回调作为 井。如果一个豆子实现了,或者,他们的 容器调用相应的方法。​​InitializingBean​​​​DisposableBean​​​​Lifecycle​

还完全支持标准接口集(如BeanFactoryAware、BeanNameAware、MessageSourceAware、ApplicationContextAaware 等)。​​*Aware​

注解支持指定任意初始化和销毁 回调方法,很像Spring XML的sand属性 在元素上,如以下示例所示:​​@Bean​​​​init-method​​​​destroy-method​​​​bean​

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

在上述示例中,在构造期间直接调用该方法同样有效,如以下示例所示:​​BeanOne​​​​init()​

@Configuration
public class AppConfig {

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

// ...
}
指定 Bean 作用域

Spring 包含注释,以便您可以指定 bean 的范围。​​@Scope​

使用注释​​@Scope​

您可以指定使用注释定义的 bean 应该具有 具体范围。您可以使用在 Bean 作用域部分中指定的任何标准​​作用域​​。​​@Bean​

默认范围是,但您可以使用注释覆盖它, 如以下示例所示:​​singleton​​​​@Scope​

爪哇岛

科特林

@Configuration
public class MyConfiguration {

@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
​@Scope​​和​​scoped-proxy​

Spring 提供了一种通过作用域代理处理作用域依赖关系的便捷方法。最简单的创建方式 使用XML配置时的此类代理是元素。 使用 aannotation 在 Java 中配置 bean 可提供等效的支持 与属性。默认值为 通常指示除非使用不同的默认值,否则不应创建任何作用域代理 已在组件扫描指令级别配置。您可以指定,或。​​<aop:scoped-proxy/>​​​​@Scope​​​​proxyMode​​​​ScopedProxyMode.DEFAULT​​​​ScopedProxyMode.TARGET_CLASS​​​​ScopedProxyMode.INTERFACES​​​​ScopedProxyMode.NO​

如果将作用域代理示例从 XML 参考文档(请参阅作用域代理)移植到我们的 Java 中, 它类似于以下内容:​​@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 命名

缺省情况下,配置类使用 amethod's 的名称作为 产生的豆子。但是,可以使用属性覆盖此功能, 如以下示例所示:​​@Bean​​​​name​

@Configuration
public class AppConfig {

@Bean("myThing")
public Thing thing() {
return new Thing();
}
}
Bean 别名

正如命名 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 的更详细的文本描述会很有帮助。这可以 当 bean 公开(可能通过 JMX)用于监视目的时特别有用。

若要向 添加说明,可以使用 @Description注释,如以下示例所示:​​@Bean​

@Configuration
public class AppConfig {

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

1.12.4. 使用注释​​@Configuration​

​@Configuration​​是一个类级注释,指示对象是 Bean definitions.classes 声明 bean through annotated 方法。对类方法的调用也可用于定义 Bean 间依赖关系。有关一般介绍,请参阅基本概念:@Bean和@Configuration。​​@Configuration​​​​@Bean​​​​@Bean​​​​@Configuration​

注入 Bean 间依赖关系

当 bean 相互依赖时,表达这种依赖性就很简单了 因为有一个 Bean 方法调用另一个 Bean 方法,如以下示例所示:

@Configuration
public class AppConfig {

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

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

在前面的示例中,接收对通过构造函数的引用 注射。​​beanOne​​​​beanTwo​

查找方法注入

如前所述,查找方法注入是一个 您应该很少使用的高级功能。在以下情况下很有用 单例范围的 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 配置,您可以创建 where 的子类 抽象方法被覆盖的方式是,它查找一个新的 (原型)命令对象。以下示例演示如何执行此操作:​​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();
}
}

​clientDao()​​被叫了一次进去,一次进去。 由于此方法创建一个新的实例并返回它,因此您将 通常期望有两个实例(每个服务一个)。那肯定是 问题:在 Spring 中,实例化的 bean 默认具有 ascope。这是 神奇之处:所有类在启动时被子类化 跟。在子类中,子方法首先检查容器中是否有任何 在调用父方法并创建新实例之前缓存(作用域)bean。​​clientService1()​​​​clientService2()​​​​ClientDaoImpl​​​​singleton​​​​@Configuration​​​​CGLIB​

1.12.5. 编写基于 Java 的配置

Spring 基于 Java 的配置功能允许您撰写注释,这可以减少 配置的复杂性。

使用注释​​@Import​

就像在Spring XML文件中使用元素来帮助模块化一样 配置,注释允许从 另一个配置类,如以下示例所示:​​<import/>​​​​@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​

注入对导入定义的依赖关系​​@Bean​

前面的示例有效,但过于简单。在大多数实际场景中,豆子有 跨配置类相互依赖。使用 XML 时,这不是 问题,因为不涉及编译器,您可以声明并信任 Spring 在容器初始化期间解决它。 使用类时,Java 编译器将约束放在 配置模型,其中对其他 Bean 的引用必须是有效的 Java 语法。​​ref="someBean"​​​​@Configuration​

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

@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");
}

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

以下示例显示了如何将一个 Bean 自动连接到另一个 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;

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

完全合格的进口豆,便于导航

在前面的场景中,use效果很好,并提供所需的 模块化,但确定自动连线 Bean 定义的确切位置是 还是有些模棱两可。例如,作为开发人员,如何 你知道底豆是在哪里宣布的吗?它不是 在代码中显式,这可能很好。请记住,​​Spring Tools for Eclipse​​提供了以下工具: 可以渲染显示所有内容如何连接的图形,这可能是您所需要的。也 您的Java IDE可以轻松找到该类型的所有声明和用法 并快速显示返回该类型的方法的位置。​​@Autowired​​​​ServiceConfig​​​​@Autowired AccountRepository​​​​AccountRepository​​​​@Bean​

如果这种歧义是不可接受的,并且您希望直接导航 从 IDE 内部从一个类到另一个类,考虑自动连接 配置类本身。以下示例演示如何执行此操作:​​@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());
}
}

在上述情况下,定义 where 是完全显式的。 但是,现在紧密耦合到。那就是 权衡。通过使用基于接口或 抽象的基于类的类。请考虑以下示例:​​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");
}

现在相对于混凝土松散耦合,内置的 IDE 工具仍然很有用:您可以轻松地 获取实现的类型层次结构。在此 方式,导航类及其依赖项变得没有什么不同 比导航基于接口的代码的通常过程。​​ServiceConfig​​​​DefaultRepositoryConfig​​​​RepositoryConfig​​​​@Configuration​

有条件地包含类或方法​​@Configuration​​​​@Bean​

有条件地启用或禁用完整类通常很有用 甚至是基于某种任意系统状态的单个方法。一个常见的 这方面的例子是,仅当特定 Profile 已在 Spring 中启用(有关详细信息,请参阅Bean 定义配置文件)。​​@Configuration​​​​@Bean​​​​@Profile​​​​Environment​

注释实际上是通过使用更灵活的注释来实现的 叫@Conditional。 注释指示应 在 AIS 注册之前进行了咨询。​​@Profile​​​​@Conditional​​​​org.springframework.context.annotation.Condition​​​​@Bean​

接口的实现提供了一个返回或的方法。例如,下面的清单显示了用于以下目的的实际实现:​​Condition​​​​matches(…)​​​​true​​​​false​​​​Condition​​​​@Profile​

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 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;
}

有关更多详细信息,请参阅@Conditionaljavadoc。

结合 Java 和 XML 配置

Spring的类支持并非旨在成为100%完全的替代品 对于 Spring XML。一些工具,如Spring XML命名空间,仍然是理想的方式 配置容器。在 XML 方便或必要的情况下,您有一个 选择:例如,通过使用 XML 以“以 XML 为中心”的方式实例化容器,或者通过使用 and 注释导入 XML 以“以 Java 为中心”的方式实例化容器 根据需要。​​@Configuration​​​​ClassPathXmlApplicationContext​​​​AnnotationConfigApplicationContext​​​​@ImportResource​

以 XML 为中心的类使用​​@Configuration​

最好从XML引导Spring容器,并以临时方式包含类。例如,在大型现有代码库中 使用Spring XML,在 根据需要,并从现有 XML 文件中包括它们。在本节的后面,我们将介绍 在这种“以 XML 为中心”的情况下使用类的选项。​​@Configuration​​​​@Configuration​​​​@Configuration​

将类声明为普通的 Spring元素​​@Configuration​​​​<bean/>​

请记住,类最终是 容器。在本系列示例中,我们创建了 namedand 的类 包括它作为定义。因为打开,容器识别注释并处理不正确声明的方法。​​@Configuration​​​​@Configuration​​​​AppConfig​​​​system-test-config.xml​​​​<bean/>​​​​<context:annotation-config/>​​​​@Configuration​​​​@Bean​​​​AppConfig​

以下示例显示了 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>

以下示例显示了一个可能的文件:​​jdbc.properties​

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);
// ...
}

使用 <context:component-scan/> 选取类​​@Configuration​

因为是元注释,注释 类自动成为组件扫描的候选项。使用与 在前面的示例中描述,我们可以重新定义以利用组件扫描。 请注意,在这种情况下,我们不需要显式声明,因为启用相同的 功能性。​​@Configuration​​​​@Component​​​​@Configuration​​​​system-test-config.xml​​​​<context:annotation-config/>​​​​<context:component-scan/>​

以下示例显示了修改后的文件:​​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​

在类是配置主要机制的应用程序中 容器,仍然可能需要至少使用一些 XML。在这些 方案中,您可以根据需要仅使用和定义尽可能多的 XML。行为 因此,实现了“以 Java 为中心”的方法来配置容器,并将 XML 保留为 最低 限度。以下示例(包括一个配置类、一个 XML 文件 定义 Bean、属性文件和类)演示如何使用 使用XML实现“以Java为中心”的配置的注释 根据需要:​​@Configuration​​​​@ImportResource​​​​main​​​​@ImportResource​

@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);
// ...
}

版本 5.3.23

标签:框架,示例,核心技术,Spring,class,注释,Bean,public
From: https://blog.51cto.com/u_15326439/5856071

相关文章

  • springboot openfeign服务端与客户端调用演示demo
    文章目录​​serverdemo演示​​​​创建server项目​​​​application.properties配置​​​​importjar[pom.xml]​​​​创建服务端的restfulcontroller​​​​验......
  • 极速交易场景下的三大核心技术点,超低时延网卡如何拆解?
    量化投资是通过数量化方式及计算机程序化发出买卖指令,以获取稳定收益为目的的交易方式。量化投资在国内落地发展已有10余年,始终保持的迅猛的发展态势,管理规模突破100亿的量......
  • SpringBoot 2学习笔记(二)
    45、web实验-抽取公共页面官方文档-TemplateLayout公共页面/templates/common.html <!DOCTYPEhtml> <htmllang="en"xmlns:th="http://www.thymeleaf.org">......
  • SpringBoot 2学习笔记(一)
    01、基础入门-SpringBoot2课程介绍SpringBoot2核心技术SpringBoot2响应式编程学习要求-熟悉Spring基础-熟悉Maven使用环境要求Java8及以上Maven......
  • SpringMVC
    README一、目录0、简介1、@RequestMapping注解2、获取请求参数3、域对象共享数据4、视图5、RESTful6、HttpMessageConverter7、拦截器和异常处理8、完......
  • Spring 中定时任务cron表达式问题
    1.问题:Cronexpressionmustconsistof6fields(found7in“0/5****?*“)@Scheduled(cron="0/5****?*")2.原因:年的项1099~2099年,为默认。因此只需要......
  • springMVC-解读<url-pattern/>- 用 / 替换掉 *.xxx的报错解决办法
    2.4解读1配置详解(1)*.do在没有特殊要求的情况下,SpringMVC的中央调度器DispatcherServlet的常使用后辍匹配方式,如写为*.do或者*.action,*.mvc等。(2)/......
  • Java SpringBoot FTP 上传下载文件
    POM添加依赖<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.7</version></dependency><!--......
  • Springboot整合Jwt实现用户认证
    前言相信大家在进行用户认证中或多或少都要对用户进行认证,当前进行认证的方式有基于session、token等主流方式,但是目前使用最广泛的还是基于JWT的用户认证,特别适用于......
  • RuoYi 若依框架 使用总结
    环境JDK>=1.8(推荐1.8版本)Mysql>=5.7.0(推荐5.7版本)Redis>=3.0Maven>=3.0Node>=12下载完成记得配置环境变量。导入项目下载项目直接在:https://git......