以编程方式解决 Bean 注入
Resolve Bean Injection Programmatically
你好,我想找到一种方法,以编程方式在 Spring 容器级别的 bean 工厂中存在的不同实现之间选择一个 Candidate。
使用配置文件我可以像这样简单地实现
我有以下配置文件 "CH" 和 "IT",我希望如果存在使用 IT 配置的 Bean,那么将回退到默认值。
鉴于那些 classes:
默认实现
@Component
public class DefaultMapper {
public void doSomething() {
System.out.println("Map Default");
}
}
瑞士实施
@Component
@Profile("CH")
public class SwissMapper extends DefaultMapper {
@Override
public void doSomething() {
System.out.println("do something swiss");
}
}
意大利语实施:
@Component
@Profile("IT")
public class ItalianMapper extends DefaultMapper {
@Override
public void doSomething() {
System.out.println("do pasta");
}
}
现在,如果我 运行 它与 @ActiveProfiles("IT")
它抛出一个 NoUniqueBeanDefinitionException,这很公平,因为默认值没有被分析,并且它将被容器注册而不需要任何进一步的关注。
一些考虑:
如果我将 @Profile("default")
添加到 DefaultMapper
这将有效,直到我有另一个默认 bean 有一个子 class 仅使用 CH 进行概要分析。
另一个 Bean 的默认实现:
@Component
@Profile("default")
public class DefaultParser {
public void doSomething() {
System.out.println("Parse Default");
}
}
然后是瑞士实现(没有可用的意大利语,或者更好地说默认适合意大利语):
@Component
@Profile("CH")
public class SwissParser extends DefaultParser {
@Override
public void doSomething() {
System.out.println("Ässe Ässe");
}
}
现在,如果我 运行 它与 @ActiveProfiles("IT")
它抛出一个 No BeanDefinitionException,这很公平,但不那么公平,因为 "default" 没有链接为 ActiveProfiles 并且对于我没有实施。此处的 CH 配置文件将起作用,因为每个默认值都有一个配置文件实现 class.
说过
我想要一种更好的方法来涵盖这种情况,而无需:
- 必须在默认 类 上定义 @Profile({"default","IT", "<all other profiles which have NOT a specialized implementation"}
或 @Profile("!CH")
,其中我排除了那些具有专业化的配置文件。
- @Primary
我认为这不是灵魂,因为在 DefaultMapper
上我必须进行专业化,这将被标记为 @Primary
而容器根本不喜欢它 ;)
然后
我想解决这个问题,我可以决定所有注入的 类,当 Beans 的分辨率不明确时,我想以编程方式决定(在容器中)我想选择哪个实现。我认为与 @Profile 类似的注释,即 @Candidate("CH") 将适合与 application.properties 中设置的值相比,即 nationality.cantidate=CH。类似于:
public class MyExtension extends UnknowSpringType {
@Override
Object resolveAmbigousDependency(final Context ctx, final Resolution resolution) {
final List<Class> clazzes = resolution.getResolvedClasses();
for (final Class clazz : clazzes) {
if (clazz.isAnnotationPresent(Candidate.class) && clazz.getAnnotation(Candidate.class).getVaue().equals(candidateApplicationPropertyValue)) {
return clazz;
}
return getDefaultImplementation(clazzes);
}
}
我过去做过一些类似于 CDI SPI 扩展的东西,它很优雅,我也可以用来自 CDI 的 @Specializes
以另一种方式覆盖它,但我找不到同样简单的方法 Spring(我没有那么多经验),BeanFactory? BeanFactoryPostProcessor ?
求助?
您可以将所有特定的解析器标记为@Primary 这将确保Spring 自动装配这个解析器而不是未标记为@Primary 的默认解析器。当所选配置文件不存在特定解析器时,它将回退到非 @Primary 默认组件。
@Component
public class DefaultParser {
public void doSomething() {
System.out.println("Parse Default");
}
}
@Component
@Primary
@Profile("CH")
public class SwissParser extends DefaultParser {
@Override
public void doSomething() {
System.out.println("Ässe Ässe");
}
您可以激活多个配置文件、CH 和 IT,您的实施将失败。
这是一个国际化问题。
你好,我想找到一种方法,以编程方式在 Spring 容器级别的 bean 工厂中存在的不同实现之间选择一个 Candidate。
使用配置文件我可以像这样简单地实现
我有以下配置文件 "CH" 和 "IT",我希望如果存在使用 IT 配置的 Bean,那么将回退到默认值。
鉴于那些 classes:
默认实现
@Component
public class DefaultMapper {
public void doSomething() {
System.out.println("Map Default");
}
}
瑞士实施
@Component
@Profile("CH")
public class SwissMapper extends DefaultMapper {
@Override
public void doSomething() {
System.out.println("do something swiss");
}
}
意大利语实施:
@Component
@Profile("IT")
public class ItalianMapper extends DefaultMapper {
@Override
public void doSomething() {
System.out.println("do pasta");
}
}
现在,如果我 运行 它与 @ActiveProfiles("IT")
它抛出一个 NoUniqueBeanDefinitionException,这很公平,因为默认值没有被分析,并且它将被容器注册而不需要任何进一步的关注。
一些考虑:
如果我将 @Profile("default")
添加到 DefaultMapper
这将有效,直到我有另一个默认 bean 有一个子 class 仅使用 CH 进行概要分析。
另一个 Bean 的默认实现:
@Component
@Profile("default")
public class DefaultParser {
public void doSomething() {
System.out.println("Parse Default");
}
}
然后是瑞士实现(没有可用的意大利语,或者更好地说默认适合意大利语):
@Component
@Profile("CH")
public class SwissParser extends DefaultParser {
@Override
public void doSomething() {
System.out.println("Ässe Ässe");
}
}
现在,如果我 运行 它与 @ActiveProfiles("IT")
它抛出一个 No BeanDefinitionException,这很公平,但不那么公平,因为 "default" 没有链接为 ActiveProfiles 并且对于我没有实施。此处的 CH 配置文件将起作用,因为每个默认值都有一个配置文件实现 class.
说过
我想要一种更好的方法来涵盖这种情况,而无需:
- 必须在默认 类 上定义 @Profile({"default","IT", "<all other profiles which have NOT a specialized implementation"}
或 @Profile("!CH")
,其中我排除了那些具有专业化的配置文件。
- @Primary
我认为这不是灵魂,因为在 DefaultMapper
上我必须进行专业化,这将被标记为 @Primary
而容器根本不喜欢它 ;)
然后 我想解决这个问题,我可以决定所有注入的 类,当 Beans 的分辨率不明确时,我想以编程方式决定(在容器中)我想选择哪个实现。我认为与 @Profile 类似的注释,即 @Candidate("CH") 将适合与 application.properties 中设置的值相比,即 nationality.cantidate=CH。类似于:
public class MyExtension extends UnknowSpringType {
@Override
Object resolveAmbigousDependency(final Context ctx, final Resolution resolution) {
final List<Class> clazzes = resolution.getResolvedClasses();
for (final Class clazz : clazzes) {
if (clazz.isAnnotationPresent(Candidate.class) && clazz.getAnnotation(Candidate.class).getVaue().equals(candidateApplicationPropertyValue)) {
return clazz;
}
return getDefaultImplementation(clazzes);
}
}
我过去做过一些类似于 CDI SPI 扩展的东西,它很优雅,我也可以用来自 CDI 的 @Specializes
以另一种方式覆盖它,但我找不到同样简单的方法 Spring(我没有那么多经验),BeanFactory? BeanFactoryPostProcessor ?
求助?
您可以将所有特定的解析器标记为@Primary 这将确保Spring 自动装配这个解析器而不是未标记为@Primary 的默认解析器。当所选配置文件不存在特定解析器时,它将回退到非 @Primary 默认组件。
@Component
public class DefaultParser {
public void doSomething() {
System.out.println("Parse Default");
}
}
@Component
@Primary
@Profile("CH")
public class SwissParser extends DefaultParser {
@Override
public void doSomething() {
System.out.println("Ässe Ässe");
}
您可以激活多个配置文件、CH 和 IT,您的实施将失败。
这是一个国际化问题。