Spring-boot-1.3.1
Resin-4.0.47
部署Spring Boot到Resin后,出现异常:
2016-01-23 23:41:02,334 [WARN ] o.s.b.f.s.DefaultListableBeanFactory - Bean creation exception on FactoryBean type check: org.springframework.beans.factory.U
nsatisfiedDependencyException: Error creating bean with name 'userWoundedArmyMapper' defined in URL [jar:file:/opt/resin-test/webapps/rabbit-webapp/WEB-INF/l
ib/rabbit-dao-0.0.1-SNAPSHOT.jar!/com/qiyun/persistence/UserWoundedArmyMapper.class]: Unsatisfied dependency expressed through bean property 'sqlSessionFacto
ry': : Error creating bean with name 'org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration': Injection of autowired dependencies failed; nested exc
eption is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.mybatis.spring.boot.autoconfigure.MybatisProperties
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.properties; nested exception is org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'mybatis.CONFIGURATION_PROPERTIES': Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: Could not ini
tialize class org.hibernate.validator.internal.engine.ConfigurationImpl; nested exception is org.springframework.beans.factory.BeanCreationException: Error c
reating bean with name 'org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration': Injection of autowired dependencies failed; nested exception is org.
springframework.beans.factory.BeanCreationException: Could not autowire field: private org.mybatis.spring.boot.autoconfigure.MybatisProperties org.mybatis.sp
ring.boot.autoconfigure.MybatisAutoConfiguration.properties; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean
with name 'mybatis.CONFIGURATION_PROPERTIES': Initialization of bean failed; nested exception is java.lang.NoClassDefFoundError: Could not initialize class
org.hibernate.validator.internal.engine.ConfigurationImpl
- -
2016-01-23 23:41:02,894 [WARN ] o.s.b.f.s.DefaultListableBeanFactory - Bean creation exception on FactoryBean type check: org.springframework.beans.factory.U
nsatisfiedDependencyException: Error creating bean with name 'userWoundedArmyMapper' defined in URL [jar:file:/opt/resin-test/webapps/rabbit-webapp/WEB-INF/l
ib/rabbit-dao-0.0.1-SNAPSHOT.jar!/com/qiyun/persistence/UserWoundedArmyMapper.class]: Unsatisfied dependency expressed through bean property 'sqlSessionFacto
ry': : Error creating bean with name 'sqlSessionFactory': Requested bean is currently in creation: Is there an unresolvable circular reference?; nested excep
tion is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'sqlSessionFactory': Requested bean is currently in
creation: Is there an unresolvable circular reference?
异常的大概问题就是,创建'userWoundedArmyMapper'时失败,因为无法注入sqlSessionFactory失败。
而sqlSessionFactory在MybatisAutoConfiguration中创建的,故需要先创建MybatisAutoConfiguration,在创MybatisAutoConfiguration时失败,因为无法注入MybatisProperties。
创建MybatisProperties后,初始化MybatisProperties失败,因为它的初始化是由ConfigurationPropertiesBindingPostProcessor进行初始化的,在ConfigurationPropertiesBindingPostProcessor中,使用了Javax Validation,在应用中使用了Hibernate的Validation实现,找不到ConfigurationImpl的定义。
进行远程Debug,并结合jvminspect.jar,发现Resin/lib目录下存在validation-api和hibernate-validator,且版本与项目引用的版本不一致。
ConfigurationImpl类,发现静态代码块
static{
Version.touch();
}
private static final Log log = LoggerFactory.make();
LoggerFactory类的make方法:
public static Log make() {
Throwable t =newThrowable();
StackTraceElement directCaller = t.getStackTrace()[1];
returnLogger.getMessageLogger( Log.class, directCaller.getClassName() );
}
其中使用了jboss的logging,很有可能是jboss logging存在多个版本导致ConfigurationImpl静态代码块执行失败,发现Resin/lib下存在jboss-logging,于是问题明朗了。
由于重复的validation-api、hibernate-validator、jboss-logging,导致加载ConfigurationImpl后进行静态代码块执行时,引用jboss logger错误,导致ConfigurationImpl出现NoClassDefFoundError。
解决方法:删除Resin/lib下的validation-api、hibernate-validator、jboss-loggingjar包。
之后,又出现了另外的问题:
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:635) ~[na:1.7.0_67]
at java.util.ArrayList.get(ArrayList.java:411) ~[na:1.7.0_67]
at com.caucho.server.webapp.WebApp.hasListener(WebApp.java:2197) ~[resin.jar:4.0.47]
at com.caucho.server.webapp.WebApp.addListenerObject(WebApp.java:2148) ~[resin.jar:4.0.47]
at com.caucho.server.webapp.WebApp.addListener(WebApp.java:2108) ~[resin.jar:4.0.47]
经过查看Resin源码,发现这是resin-4.0.45版本中引入的bug:
/**
* Returns true if a listener with the given type exists.
*/
public boolean hasListener(ArrayList listeners, Class listenerClass)
{
for (int i = 0; i < listeners.size(); i++) {
Object listener = _listeners.get(i);
if (listener.getClass().equals(listenerClass)) {
return true;
}
}
return false;
}
其中Object listener = _listeners.get(i);的_listeners使用了类属性,而不是参数listeners,导致异常,这个BUG只能等后续版本修复。
于是,我下载了未引入这个BUG的Resin-4.0.44版本,运行之后,又发现了新的问题:
Custom bean class '{0}' is not public. Bean classes must be public, concrete, and have a zero-argument constructor.
经过查看Resin源码,发现com.caucho.server.dispatch.FilterConfigImpl的setFilterClass方法:
@DisableConfig
public void setFilterClass(Class filterClass)
{
this._filterClass = filterClass;
Config.validate(this._filterClass, Filter.class);
}
这又是Resin的一个BUG,当直接添加Filter对象到ServletContext时,Resin还会去验证Filter的CLass是不是public的类。这个Bug在Resin-4.0.46中修复了。
servlet: drop instantiation check for instances of servlets and filters (#5934)
@DisableConfig
public void setFilterClass(Class filterClass)
{
_filterClass = filterClass;
if (_filter == null)
Config.validate(_filterClass, Filter.class);
}
为了解决这个问题,必须使用Resin-4.0.46以后的版本。
综合Resin上诉2个BUG,导致Resin目前没有一个版本可以完美支持Spring Boot,只能换Tomcat和Jetty了。