【发布时间】:2019-02-21 06:12:48
【问题描述】:
我们有一个中等复杂的 spring boot 1.5.14 应用程序,后端使用 rest api + mybatis,前端使用 angular 4 和 material/prime-ng。从开发人员的盒子到 UAT 环境,它都可以正常工作,但在生产中,前几天它工作正常,然后抛出 NoSuchBeanDefinition。生产环境为openshift + openjdk版本“1.8.0_171”。
要精简应用并留下相关信息,这里是代码sn-ps:
public interface ITaxCalculator {
BigDecimal calc(BigDecimal amount);
}
public class FedProvTaxCalculator implements ITaxCalculator {
... ...
}
@Configuration
public class TaxCalculatorConfiguration {
...
@Bean("onTaxCalculator")
public ITaxCalculator ontairioTaxCalculator() {
FedProvTaxCalculator ret = ..
...
return ret;
}
@Bean("bcTaxCalculator")
public ITaxCalculator britishColumbiaTaxCalculator() {
FedProvTaxCalculator ret = ..
...
return ret;
}
}
public class CAOrderProcessor implements IOrderProcessor {
@Autowire @Qualifier("onTaxCalculator")
private FedProvTaxCalculator onTaxCalculator;
@Autowire @Qualifier("bcTaxCalculator")
private FedProvTaxCalculator bcTaxCalculator;
....
}
// --------------- below code are at framework level -----
public interface IOrderProcessor {
void process(Order order);
}
public interface IOrderProcessorFactory {
IOrderProcessor createOrderProcessor(String countryCode, MembershipType membership);
}
@Service
public class OrderProcessorFactoryPropImpl implements IOrderProcessorFactory {
@Autowired
private AutowireCapableBeanFactory beanFactory;
@Override
@Cacheable("orderProcessor")
public IOrderProcessor createOrderProcessor(String countryCode, MembershipType membership) {
String clzName = resolveOrderProcessClzName(countryCode, membership); // resolve to CAOrderProcess clz-name
try {
Object ret = Class.forName(clzName).newInstance();
beanFactory.autowireBean(ret);
// the above line throws error after a while
return (IOrderProcessor)ret;
} catch (Exception ex) {
...
throw new RuntimeException(...);
}
}
private String resolveOrderProcessClzName(String countryCode, MembershipType membership) {
String clzName = lookupFromPropFile(countryCode + "." + membership.name());
if (StringUtils.isBlank( clzName )) {
clzName = lookupFromPropFile(countryCode);
}
return clzName;
}
}
重启 Spring Boot 应用后,前几天它运行良好,即使使用 CA=CAOrderProcessor。但是有一天,在 countryCode=CA 的情况下,它会抛出 NoSuchBeanDefinitionException:没有可用的“FedProvTaxCalculator”类型的合格 bean:预计至少有 1 个有资格作为自动装配候选者的 bean。重新启动 Java 应用后,它再次为 CA=CAOrderProcessor 工作。
为什么spring框架会这样?提前致谢!
这个问题可以解决
@Configuration public class TaxCalculatorConfiguration {
@Bean("onTaxCalculator")
public ITaxCalculator ontairioTaxCalculator() { ... }
}
public class CAOrderProcessor implements IOrderProcessor {
@Autowire @Qualifier("onTaxCalculator")
private ITaxCalculator onTaxCalculator;
}
使用 AutowireCapableBeanFactory 很好。为什么它最初可以工作,然后失败,并且只在一个 ENV 上失败 - openshift 至少有 2 个 pod?其他 ENV 始终可以正常工作。看起来spring最初放松了autowire bean-type检查,后来在某些条件下,它检查了bean-type。逻辑猜测是bean-definition返回接口类型,可能被代理,bean-wiring指的是具体类型,代理接口不等于具体类型,引发此错误。但在这种情况下,它应该总是给出错误。如果没有,如果我不使用缓存或驱逐缓存,我应该能够在任何 ENV 中轻松地重新生成它,但它在我的本地 macos + oracle jdk 1.8 上运行良好。我什至创建了一个基于生产 openshift docker 镜像的 docker 容器来运行没有缓存、驱逐缓存、强制 YGC 和 FGC 的应用程序,它也可以正常工作。
【问题讨论】:
标签: java spring-boot