接上一篇博客:https://blog.csdn.net/qq_43605444/article/details/121954378?spm=1001.2014.3001.5502
在配置 Spring 方面,注解是否比 XML 更好?
- 基于注解的配置的引入提出了这种方法是否比 XML“更好”的问题。 简短的回答是“视情况而定”。 长的答案是每种方法都有其优点和缺点,通常由开发人员决定哪种策略更适合他们。 由于它们的定义方式,注解在它们的声明中提供了很多上下文,从而导致更短和更简洁的配置。 然而,XML 擅长连接组件,而无需接触它们的源代码或重新编译它们。 一些开发人员更喜欢将接线靠近源,而其他人则认为带注释的类不再是 POJO,而且,配置变得分散且更难控制。
- 无论选择哪种,Spring 都可以容纳这两种风格,甚至可以将它们混合在一起。 值得指出的是,通过其 JavaConfig 选项,Spring 允许以非侵入性方式使用注释,而无需触及目标组件源代码,并且在工具方面,Spring Tools for Eclipse 支持所有配置样式。
XML 设置的替代方案由基于注解的配置提供,它依赖于字节码元数据来连接组件而不是尖括号声明。开发人员不使用 XML 来描述 bean 连接,而是通过在相关类、方法或字段声明上使用注解将配置移动到组件类本身中。如示例:AutowiredAnnotationBeanPostProcessor 中所述,将 BeanPostProcessor 与注解结合使用是扩展 Spring IoC 容器的常用方法。例如,Spring 2.0 引入了使用 @Required 注解强制执行必需属性的可能性。 Spring 2.5 使得遵循相同的通用方法来驱动 Spring 的依赖注入成为可能。
从本质上讲,@Autowired 注解提供了与 Autowiring Collaborators 中描述的相同的功能,但具有更细粒度的控制和更广泛的适用性。 Spring 2.5 还添加了对 JSR-250 注解的支持,例如 @PostConstruct 和 @PreDestroy。 Spring 3.0 添加了对包含在 javax.inject 包中的 JSR-330(Java 依赖注入)注解的支持,例如 @Inject 和 @Named。有关这些注解的详细信息可以在相关部分中找到。
注解注入在 XML 注入之前执行。 因此,XML 配置会覆盖通过这两种方法连接的属性的注解。
与往常一样,您可以将后处理器注册为单独的 bean 定义,但也可以通过在基于 XML 的 Spring 配置中包含以下标记来隐式注册它们(注意包含上下文命名空间):
<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>
元素隐式注册以下后处理器:
ConfigurationClassPostProcessor
AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor
PersistenceAnnotationBeanPostProcessor
EventListenerMethodProcessor
仅在定义它的同一应用程序上下文中查找 bean 上的注解。 这意味着,如果您将
放在 DispatcherServlet 的 WebApplicationContext 中,它只会检查您的控制器中的 @Autowired bean,而不是您的服务。 有关更多信息,请参阅 DispatcherServlet。
@Required 注解适用于 bean 属性设置方法,如下例所示:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
该注解表明必须在配置时通过 bean 定义中的显式属性值或通过自动装配来填充受影响的 bean 属性。 如果尚未填充受影响的 bean 属性,则容器将引发异常。
这允许急切和显式失败,避免以后出现 NullPointerException 实例或类似情况。 我们仍然建议您将断言放入 bean 类本身(例如,放入 init 方法)。 即使您在容器外部使用类,这样做也会强制执行那些必需的引用和值。
RequiredAnnotationBeanPostProcessor
必须注册为 bean 才能启用对 @Required 注解的支持。
@Required 注解 和
RequiredAnnotationBeanPostProcessor
从Spring Framework 5.1
开始正式弃用,赞成使用构造函数注入进行所需设置(或InitializingBean.afterPropertiesSet()
的自定义实现或自定义 @PostConstruct 方法以及 bean 属性设置器方法)。
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;
}
// ...
}
通过上面的例子,可以看到我们没有通过 XML 方式注入属性值,而通过 @Autowired 注解实现了自动注入属性。
您还可以将注解应用于具有任意名称和多个参数的方法,如下例所示:
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)由您用于 @Autowired 注解的注入点的类型一致声明。 否则,注入可能会因运行时“未找到类型匹配”错误而失败。
对于通过类路径扫描找到的 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 实例也可以自动装配。 映射值包含预期类型的所有 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 并将 required 属性设置为 true,指示当用作 Spring bean 时自动装配的构造函数。因此,如果 required 属性保留其默认值 true,则只能使用 @Autowired 注解单个构造函数。如果多个构造函数声明了注解,它们都必须声明
required=false
才能被视为自动装配的候选对象(类似于 XML 中的autowire=constructor
)。将选择通过匹配 Spring 容器中的 bean 可以满足的依赖项数量最多的构造函数。如果没有一个候选可以满足,那么将使用主/默认构造函数(如果存在)。
类似地,如果一个类声明了多个构造函数,但没有一个用@Autowired 注解,则将使用主/默认构造函数(如果存在)。如果一个类只声明一个构造函数开始,它将始终被使用,即使没有注解。请注意,带注解的构造函数不必是公共的。
推荐使用 @Autowired 的 required 属性而不是 setter 方法上已弃用的 @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 内置的空安全支持:
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 方法显式“连接”。
文章参考:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-annotation-config