spring+mybatis启动NoClassDefFoundError异常分析三部曲之一:稳定重现问题

上一周,web项目在发布的时候应用启动失败了,错误信息如下:

org.springframework.web.context.ContextLoader] - Context initialization failed
java.lang.NoClassDefFoundError: Could not initialize class org.springframework.beans.factory.BeanCreationException
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:547)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:700)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5118)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5634)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:899)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:875)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:652)
    at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1260)
    at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:2002)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

组里的同学们对比了本次发布的改动,再结合异常去网上搜索,很快就定位到了问题所在,在mybatis的配置文件中,我们经常做如下配置:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.ssm.dao" />
    </bean>

正常情况下,这样配置后mybatis就会扫码com.ssm.dao目录下的接口,我们在业务代码中autowire的这些接口,就会被注入实例化好的动态代理对象,这是正常的用法。

但是在com.ssm.dao目录下的接口达到某个数量范围后,应用启动时就会失败,而错误信息就是上面的那个。

解决的方法很简单,把配置改成下面这样就行了:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.ssm.dao" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>

增加了一个sqlSessionFactoryBeanName属性的节点,注意sqlSessionFactory是我们早就配置好的:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- 自动扫描mapping.xml文件 -->
        <property name="mapperLocations" value="classpath:com/ssm/mapping/*.xml"></property>
    </bean>

这样配置后再次启动就成功了,业务验证也正常,不过虽然问题解决了,但是对问题的来龙去脉并不清楚,所以很有必要做深入的了解和分析;

周末经过一番折腾对问题有了更深的了解,由于涉及东西不少,因此打算分三篇文章由浅入深的分析,本人水平有限,实践和认识上有不对之处欢迎随时交流,谢谢啦;

三篇文章的重点依次是: 1. 构造环境,使问题可以稳定重现,这样我们断点也好,改代码也好,可以用更多的手段去定位和分析问题; 2. 追踪,定位问题; 3. 分析问题,为何会导致此问题,寻找解决之道;

本章作为三部曲之一,目标是让问题稳定重现,下面开始实践吧:

准备tomcat

本次实战,我用tomcat是7.0.75版本,为了让应用在启动时更容易报错,需要对tomcat的启动参数做一下设置,把栈的大小调得更小一些,打开tomcat安装目录下的bin目录,打开catalina.bat文件,找到这一行:

set "JAVA_OPTS=%JAVA_OPTS% %JSSE_OPTS%"

改成

set "JAVA_OPTS=%JAVA_OPTS% %JSSE_OPTS% -Xss160K"

低于160k的话,启动tomcat会失败,提示不能低于160k;

至于为什么调小了这个参数会使应用启动更容易报错,其实启动报错的更本原因就是栈溢出StackoverFlowError(后面的章节会详细讨论),这里栈越小就越容易报错了;

准备数据库

mybatis相关的应用是和mysql数据库有关的,要建数据库和对应的表,这里我建了一个库和一个表,导入了一条记录,如果应用启动成功运行正常,是可以访问到这条记录的:

数据库名称:mybatistest 建表和增加数据的sql如下:

CREATE TABLE `user_t` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(255) NOT NULL DEFAULT '',
  `password` varchar(255) NOT NULL DEFAULT '',
  `age` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

insert into user_t (user_name, password, age) values ('Tom', '123456', 11);

对于电脑上没有装mysql的读者,如果已经装了docker,这里推荐用docker直接run一个mysql容器:

docker run --name mysql001 -idt -e MYSQL_ROOT_PASSWORD=888888 -p 3306:3306 daocloud.io/library/mysql:8

一行命令把mysql装好,再执行docker exec -it mysql001 /bin/bash 进入容器把数据库和表建好;

准备代码

公司的代码拿来做实验和写博客自然是不太合适的,所以这里我准备了一套简单的demo,一样可以重现问题,demo的源码在git上,地址:git@github.com:zq2599/blog_demos.git

下载后可以发现里面有很多工程,本次实战用的工程是springmybatisexceptiondemo,如下图红框所示:

这是个普通的spring+mybatis的web应用,数据库的配置在jdbc.properties文件中,请读者结合自己的mysql配置进行修改;

这个应用可以直接在tomcat上运行起来,数据库准备好,该应用部署到tomcat后,在浏览器输入http://localhost:8080/springmybatisexceptiondemo/user/showUser?id=1,可以看到如下页面:

如图所示,数据库的数据被读取并展示到页面上;

重现问题

打开工程中的spring-mybatis.xml文件,修改的地方如下图红框所示,把红框中的内容注释掉,使其不生效:

再次部署在tomcat上,这时候就可以在tomcat控制台上看到应用启动失败的日志,如下图:

至此,问题已经可以稳定复现,就等我们通过各种手段去定位和分析了,下一章,我们会深入到spring内部,去探究问题来龙去脉;

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏CSDN技术头条

大数据技术Hadoop面试题,看看你能答对多少?答案在后面

单项选择题 1. 下面哪个程序负责 HDFS 数据存储。 a)NameNode b)Jobtracker c)Datanode d)secondaryNameN...

24410
来自专栏Albert陈凯

hbase的二级索引解决方案

HBase的一级索引就是rowkey,我们只能通过rowkey进行检索。如果我们相对hbase里面列族的列列进行一些组合查询,就需要采用HBase的二级索引方案...

2825
来自专栏Jed的技术阶梯

HBase和Hive整合

1-3步骤不是必须的,如果没有进行1-3步骤的设置,那么想要让hive和hbase整合,每次进入hive命令行后,需要进行如下配置:

793
来自专栏蓝天

Hadoop-2.8.0分布式安装手册

10.12.154.79: Error: JAVA_HOME is not set and could not be found.

1422
来自专栏公有云大数据平台弹性MapReduce

EMR之HBASE集群参数调优与压测

HBase 是Hadoop生态里重要一员。对HBase的调优,对节约成本,提升用户体验有重要意义。

1934
来自专栏大数据和云计算技术

HBase框架基础(一)

HBase的基础框架,将分成几个章节对HBase进行描述,不当之处还望大家批评指正。下面是了解HBase基础架构的第一部分。

1423
来自专栏Hadoop实操

如何使用Sentry管理Hive外部表(补充)

/extwarehouse/student_hive数据目录不存,在创建外部表时自动生成,且数据目录属主为hive。

3724
来自专栏美图数据技术团队

Spark Streaming VS Flink

本文从编程模型、任务调度、时间机制、Kafka 动态分区的感知、容错及处理语义、背压等几个方面对比 Spark Stream 与 Flink,希望对有实时处理...

791
来自专栏PPV课数据科学社区

干货 | 98道常见Hadoop面试题及答案解析(一)

这是一篇hadoop的测试题及答案解析,题目种类挺多,一共有98道题,题目难度不大,对于高手来说,90分以上才是你的追求。 1 单选题 1.1 下面哪个程序负责...

3064
来自专栏恰同学骚年

Hadoop学习笔记—17.Hive框架学习

  Hive 是建立在 Hadoop 基础上的数据仓库基础构架。它提供了一系列的工具,可以用来进行数据提取转化加载(ETL),这是一种可以存储、查询和分析存储在...

732

扫码关注云+社区