Spring 依赖注入问题
近半年,在生产环境老是出现应用在 JBoss 里启动不来,在下面的方法输出日志、进入循环后就走不出那个循环了:
Spring 3.0.5: org.springframework.beans.factory.support.DefaultListableBeanFactory
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isInfoEnabled()) {
this.logger.info("Pre-instantiating singletons in " + this);
}
synchronized (this.beanDefinitionMap) {
for (String beanName : this.beanDefinitionNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
final FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
return ((SmartFactoryBean) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean) factory).isEagerInit();
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}
}
}
因为是生产环境,没法直接远程 debug,只能 dump 出栈和堆来分析,从栈来看,Spring 一直在做 bean 实例化;从堆来看,是这样的:
Top elements include:
•13,701 × Error creating bean with name ‘sqlSessionFactory’ … > (496 bytes)
•13,732 × Could not autowire method: public final void org.m… (336 bytes)
…….//还有很多其他的类创建失败
这个问题有一定的随机性,因为不是总是起不来,重启多次之后就可能顺利启动了,搞得每次发版本都胆战心惊。
最近项目改版,新增了很多类,然后测试环境也出现这样的问题了。通过 debug,最终发现是 org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement
类的方法:
protected void inject(Object target, String requestingBeanName, PropertyValues pvs) throws Throwable {
if (this.isField) {
Field field = (Field) this.member;
ReflectionUtils.makeAccessible(field);
field.set(target, getResourceToInject(target, requestingBeanName));
}
else {
if (checkPropertySkipping(pvs)) {
return;
}
try {
Method method = (Method) this.member;
ReflectionUtils.makeAccessible(method);
method.invoke(target, getResourceToInject(target, requestingBeanName)); // here
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
这个方法在注入 DataSource
时抛出了栈溢出异常 StackOverflowError
,这个异常被 Spring 捕获并转换为 BeanCreationException
,认为是正常的情况,捕获的地方是类 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
的 protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
方法:
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
这个问题没空继续分析下去了,还有待弄明白的问题:
- 为什么一个 Bean 依赖注入失败会导致死循环?
- 同样是 64 位的 JDK,为什么在生产环境更容易出现,而测试环境的频率低些;开发环境是 32 位的,从引用的大小容易解释。
log4j 的日志级别问题
最近突然发现在应用的日志级别变成 debug 级别了,输出一大堆日志,根本没法看。尝试修改应用提供的 log4j 配置文件和 JBoss 自己的 log4j 配置文件都没有效果。
问下周围的人,都已经知道了,没人分析问题。有的人说把 log4j 的日志级别从 INFO 改为 ERROR,启动应用,停止,然后再改回 INFO 就可以了。好无语,这是跳大神吗??!!
看了下日志,有 Spring 输出的,从 Spring 的源码来看,它用了 commons-logging 的 API,然后自己提供一份 commongs-logging 的配置进去也没有效果。
问了旁边那个认真做事的妹子,她说是一次代码合并后才出现的,那次合并加了几个 jar 包。把那次新增的 jar 都打开检查,其中一个竟然有一份 log4j 的配置文件,把 org 开头的包的日志级别定为 debug 了。
欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。