Support catch NoClassDefFoundError exception When use ClassLoaderWrapper load Class
See original GitHub issueMyBatis version
3.5.3
Database vendor and version
use MySQL 5.6.16 SpringBoot 2.3.2.RELEASE
Test case or example project
When MyBatis integrates Spring Boot, the Mapper XML uses OGNL expressions to refer to nested enumeration constant classes, causing a NoClassDefFoundError exception
The below constains OrgEnum, TestController class, User entity class, UserMapper interface and user.xml mapper
@Getter
@RequiredArgsConstructor
public enum OrgEnum {
//org info
AB("000001", "ABorg"),
CD("000002", "CDorg");
private final String code;
private final String name;
//constants
public static final String AB_ORG_CODE = AB.getCode();
public static final String CD_ORG_CODE = CD.getCode();
public static final String D1_DEPARTMENT_CODE = OrgEnum.DepartmentEnum.D1_DEPARTMENT.getCode();
/**
* 保司信息
*/
@Getter
@RequiredArgsConstructor
public enum DepartmentEnum {
// department info
D1_DEPARTMENT(OrgEnum.AB, "D001", "D001Info..."),
D2_DEPARTMENT(OrgEnum.AB, "D002", "D002Info..."),
D3_DEPARTMENT(OrgEnum.CD, "D003", "D003Info..."),
;
private final OrgEnum org;
private final String code;
private final String name;
}
}
@RestController
@RequestMapping("test")
@RequiredArgsConstructor
public class TestController {
private final UserMapper userMapper;
@GetMapping
public User get(Long id) {
Objects.requireNonNull(id);
return userMapper.getOne(id);
}
}
@Data
public class User {
private String orgCode;
private String name;
}
public interface UserMapper {
User getOne(Long id);
}
The below is sql use ognl expression in user.xml
<select id="getOne" resultMap="com.example.demo.t.User">
SELECT
'${@com.example.demo.t.OrgEnum@AB_ORG_CODE}' AS orgCode,
name
FROM
user
WHERE
id = #{id}
</select>
When client request TestController#get method,The Mybatis throw java.lang.NoClassDefFoundError exception ,the below is detial info
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: Could not initialize class com.example.demo.t.OrgEnum
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1055)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:652)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1589)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NoClassDefFoundError: Could not initialize class com.example.demo.t.OrgEnum
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at org.apache.ibatis.io.ClassLoaderWrapper.classForName(ClassLoaderWrapper.java:186)
at org.apache.ibatis.io.ClassLoaderWrapper.classForName(ClassLoaderWrapper.java:89)
at org.apache.ibatis.io.Resources.classForName(Resources.java:261)
at org.apache.ibatis.scripting.xmltags.OgnlClassResolver.toClassForName(OgnlClassResolver.java:34)
at org.apache.ibatis.ognl.DefaultClassResolver.classForName(DefaultClassResolver.java:58)
at org.apache.ibatis.ognl.OgnlRuntime.classForName(OgnlRuntime.java:1167)
at org.apache.ibatis.ognl.OgnlRuntime.getStaticField(OgnlRuntime.java:2059)
at org.apache.ibatis.ognl.ASTStaticField.getValueBody(ASTStaticField.java:68)
at org.apache.ibatis.ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212)
at org.apache.ibatis.ognl.SimpleNode.getValue(SimpleNode.java:258)
at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:493)
at org.apache.ibatis.ognl.Ognl.getValue(Ognl.java:457)
at org.apache.ibatis.scripting.xmltags.OgnlCache.getValue(OgnlCache.java:46)
at org.apache.ibatis.scripting.xmltags.TextSqlNode$BindingTokenParser.handleToken(TextSqlNode.java:77)
at org.apache.ibatis.parsing.GenericTokenParser.parse(GenericTokenParser.java:77)
at org.apache.ibatis.scripting.xmltags.TextSqlNode.apply(TextSqlNode.java:51)
at org.apache.ibatis.scripting.xmltags.MixedSqlNode.lambda$apply$0(MixedSqlNode.java:32)
at java.util.ArrayList.forEach(ArrayList.java:1259)
at org.apache.ibatis.scripting.xmltags.MixedSqlNode.apply(MixedSqlNode.java:32)
at org.apache.ibatis.scripting.xmltags.DynamicSqlSource.getBoundSql(DynamicSqlSource.java:39)
at org.apache.ibatis.mapping.MappedStatement.getBoundSql(MappedStatement.java:297)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:81)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:147)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:140)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:426)
at com.sun.proxy.$Proxy123.selectList(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:223)
at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:147)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:80)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:93)
at com.sun.proxy.$Proxy130.selectGift2Short(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
when I debug it ,I find the method org.apache.ibatis.io.ClassLoaderWrapper#classForName(java.lang.String, java.lang.ClassLoader[]) second args contains three ClassLoaders,and first ClassLoader is org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader,anoter ClassLoader is AppClassLoader, while the second args ClassLoader[] is from the method org.apache.ibatis.io.ClassLoaderWrapper#getClassLoaders, and have a Thread.currentThread().getContextClassLoader(),that is TomcatEmbeddedWebappClassLoader,of this time, when custom ClassLoader not load Class and throw NoClassDefFoundError, the Mybatis framework should catch it, rather that just catch ClassNotFoundException The method ClassLoaderWrapper#classForName have below code
for (ClassLoader cl : classLoader) {
if (null != cl) {
try {
Class<?> c = Class.forName(name, true, cl);
if (null != c) {
return c;
}
} catch (ClassNotFoundException e) {
// we'll ignore this until all classloaders fail to locate the class
}
}
}
The method org.apache.ibatis.io.ClassLoaderWrapper#getClassLoaders code is
return new ClassLoader[]{
classLoader,
defaultClassLoader,
Thread.currentThread().getContextClassLoader(),
getClass().getClassLoader(),
systemClassLoader};
}
Steps to reproduce
use http client request TestController#get method
Expected result
select orgCode and set User#orgCode
Actual result
throw java.lang.NoClassDefFoundError
Issue Analytics
- State:
- Created 3 years ago
- Reactions:1
- Comments:6 (3 by maintainers)
It’s OK,Thanks for your answers
There is a circular dependency,When I try to remove this line , it’s ok.