最近处理的两个坑:Spring 启动问题与 log4j 配置

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

这个问题没空继续分析下去了,还有待弄明白的问题:

  1. 为什么一个 Bean 依赖注入失败会导致死循环?
  2. 同样是 64 位的 JDK,为什么在生产环境更容易出现,而测试环境的频率低些;开发环境是 32 位的,从引用的大小容易解释。

log4j 的日志级别问题

最近突然发现在应用的日志级别变成 debug 级别了,输出一大堆日志,根本没法看。尝试修改应用提供的 log4j 配置文件和 JBoss 自己的 log4j 配置文件都没有效果。

问下周围的人,都已经知道了,没人分析问题。有的人说把 log4j 的日志级别从 INFO 改为 ERROR,启动应用,停止,然后再改回 INFO 就可以了。好无语,这是跳大神吗??!!

看了下日志,有 Spring 输出的,从 Spring 的源码来看,它用了 commons-logging 的 API,然后自己提供一份 commongs-logging 的配置进去也没有效果。

问了旁边那个认真做事的妹子,她说是一次代码合并后才出现的,那次合并加了几个 jar 包。把那次新增的 jar 都打开检查,其中一个竟然有一份 log4j 的配置文件,把 org 开头的包的日志级别定为 debug 了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.