最近因为要测试一个功能,需要用最短的时间来启动服务,开启测试程序,但平常所用的框架中已经集成了各种三方的东西,想着那就再重新搭建一个最简单的ssm框架吧。
搭建可参考:简单ssm最新搭建
搭建过程并不麻烦,整合springmvc测试成功,接口正常调用,最后整合mybatis后,在service中注入调用时出现了问题,启动服务时报错如下:
No qualifying bean of type 'com.test.mapper.TpmTestLogMapper' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
一、bug解决
首先先说一下我的错误原因:web.xml中没有配置监听器listener,也就是下边这些代码:
(其实配置了,但是不知道为什么最后发现是注释掉的,真的 头大!!!)
<!-- Spring监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
(文末附有完整的web.xml配置,直接到文末)
那么为什么没有配置监听,就导致mybatis注入失败了呢,这就不得不提到监听配置其中的一个作用了,监听加载容器配置文件,也就是Spring的配置文件applicationContext.xml。
加载流程: tomcat服务启动时,会首先加载web.xml,然后监听器会默认去加载Spring的相关配置文件,从而创建spring容器中的各个bean组件。
默认加载的配置文件路径是:WEB-INF/applicationContext.xml,当然很多时候我们为了项目结构的清晰,会将此配置文件放在resource下,这时也需要在web.xml中额外配置
<!--spring的其他配置文件(包括mybatis配置文件) -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:spring-applicationContext.xml
</param-value>
</context-param>
错误原因总结:启动文件web.xml中没有配置监听器,导致我的Spring相关配置文件没有加载,所以其中引入的spring-mybatis配置文件更不会加载,bean没有创建成功,最终导致了在service中调用时注入失败。
二、排查思路
排查过程中的思路:
-
spring-mybatis整合配置文件路径是否正确,能否被扫描到
-
mapper.java和mapper.xml之间有没有联系起来,数据库配置是否正确,mapper接口是否可以扫描到(查看配置文件)
- 有没有扫描到mapper.xml文件(查看编译文件,及对应路径是否正确)
1、检查spring-mybatis配置文件,在web.xml中是否可以扫描到:在serviceImpl中手动获取,看能否获取到
@Override
public void ceshi(TpmTestLog tpmTestLog) {
//手动获取创建
ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring-mybatis.xml");
TpmTestLogMapper bean = ac.getBean(TpmTestLogMapper.class);
bean.insert(tpmTestLog);
System.out.println(bean.toString());
}
测试是否可以成功获取到,可以,则表示mybatis路径正确,可以在web.xml中被正常扫描。
2、检查mybatis配置文件是否正确:主要是下方标红的两项配置会造成此错误
<!-- 读取配置文件信息 --> <context:property-placeholder ignore-unresolvable="true" location="classpath:jdbc.properties"/> <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 数据库基本配置 --> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <!-- 初始化连接数量 --> <property name="initialSize" value="${jdbc.initialSize}"/> <!-- 最大并发连接数量 --> <property name="maxActive" value="${jdbc.maxActive}"/> <!-- 最小空闲连接数 --> <property name="minIdle" value="${jdbc.minIdle}"/> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="${jdbc.maxWait}" /> <!-- 超过时间限制是否回收 --> <property name="removeAbandoned" value="${jdbc.removeAbandoned}" /> <!-- 超过时间限制多长 --> <property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}" /> <!-- 用来检测连接是否有效的sql,要求是一个查询语句--> <property name="validationQuery" value="${jdbc.validationQuery}" /> <!-- 申请连接的时候检测 --> <property name="testWhileIdle" value="${jdbc.testWhileIdle}" /> <!-- 申请连接时执行validationQuery检测连接是否有效,配置为true会降低性能 --> <property name="testOnBorrow" value="${jdbc.testOnBorrow}" /> <!-- 归还连接时执行validationQuery检测连接是否有效,配置为true会降低性能 --> <property name="testOnReturn" value="${jdbc.testOnReturn}" /> <property name="logAbandoned" value="true" /> <!-- 配置监控统计拦截的filters,wall用于防止sql注入,stat用于统计分析 --> <property name="filters" value="stat" /> </bean> <!-- MyBatis SqlSessionFactoryBean 配置 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!-- 自动扫描Mapping.xml文件 --> <property name="mapperLocations" value="classpath:com/test/mapper/xml/*.xml"/> <!-- 配置MyBaties全局配置文件:mybatis-config.xml --> <property name="configLocation" value="classpath:mybatis-config.xml"/> <!-- 扫描model包 xml中parameterType就可以使用类名,不用全路径 --> <property name="typeAliasesPackage" value="com.test.model"/> </bean> <!-- 加载 mapper.xml对应的接口 配置文件 --> <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 给出需要扫描mapper接口包 --> <property name="basePackage" value="com.test.mapper"/> <!-- 注入sqlSessionFactory --> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
3、mapper.xml文件是否可以被正确扫描到,编译后的文件中是否存在以及路径是否正确
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <!--spring的其他配置文件(包括mybatis配置文件) --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:spring-applicationContext.xml </param-value> </context-param> <!-- 编码过滤器 --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <async-supported>true</async-supported> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- Spring监听器 监听加载相关配置文件--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--防止Spring内存溢出监听器--> <listener> <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class> </listener> <!--Spring MVC servlet--> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>/index.jsp</welcome-file> <welcome-file>/index.html</welcome-file> <welcome-file>/reg.html</welcome-file> </welcome-file-list> </web-app>