这是我的第一篇博客,其实很早就打算写了,一直没能动手,可能是自己太懒了。空话不说了,这篇博客的目的主要是记录下这两天搭建过程中遇到的问题
一年多来一直在做游戏服务器,web的东西很久没搞了,最近公司手上的事情不多,所以抽空准备弄一个springmvc的web项目。以前在上一家公司用过一段时间,不过那都是大神搭建的,自己也就是复制粘贴试了下水,没多大的感觉。公司网络限制,没法使用maven,也没建内部仓库(这是我想吐槽的地方),所以只有一个jar包一个jar包的下载。第一次找下来大概需要30多个jar包。开始搭建web环境,现在使用的eclipse不支持web开发,只有自己手动搭建。开始出现问题:
一、开始的时候未使用任何框架,就是一个简单的servlet,想看看项目的流程走动
1.jar包的放置位置。
开始把ja程引用的时候毫无问题,但是通过命令行打了war包以后,项目无法正常运行,没有办法只有把jar包放到WEB-INF/lib下面。ok,可以正常启动了。tomcat的启动默认是寻找WEB-INF下面的web.xml和lib下面的jar包。
2.项目输入路径和打war的路径
我的项目名叫JavaWeb,classpath的输出路径<classpathentry kind="output" path="webapp/WEB-INF/classes"/>。开始的时候我直接/JavaWeb目录下,通过dos运用命令进行的打包(jar -cvf JavaWeb.war*)大家发现问题了吗?接的看后面,将打好的jar包直接拷贝到tomcat的webapps下面,启动tomcat,项目启动失败。再仔细看
看classpath的输出路径根目录是webapp。将当前路径指向webapp再重新打包。ok,正常运行。
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/applicationContent.xml
</param-value>
</context-param>
<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:/applicationContent_web.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>
</web-app>
将servlet交给spring进行管理。引入spring的配置文件,默认是servlet-name + [-servlet].xml 也就是这里的SpringMVC-servlet.xml,如果不使用默认的,则需要加入这段代码
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/applicationContent_web.xml
</param-value>
</init-param>
加载其他配置文件
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/applicationContent.xml
</param-value>
</context-param>
此文件主要配置的数据库相关的
2.applicationContent_web.xml.xml文件中的主要内容
<!-- 注解驱动 -->
<mvc:annotation-driven />
<context:annotation-config />
<!-- 扫描包 -->
<context:component-scan base-package="com.ktios" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<!-- ViewResolver -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
3.applicationContent.xml中的主要内容
<context:property-placeholder location="classpath:/resource/database.properties"
ignore-unresolvable="true" />
<aop:aspectj-autoproxy />
<context:annotation-config />
<!-- 扫描包 -->
<context:component-scan base-package="com.ktios" />
<context:component-scan base-package="resource.mybatis" />
<!-- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"> -->
<bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource"
destroy-method="close">
<!-- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"> -->
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<!-- <property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" /> -->
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- MyBatis -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="com.ktios.enity" />
<property name="mapperLocations" value="classpath:/resource/mybatis/*Mapper.xml" />
</bean>
<!-- 扫描mapper接口 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ktios.dao" />
<property name="annotationClass" value="com.ktios.dao.annotation.MyBatisRepository" />
</bean>
<!-- Transaction -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 采用注解方式的事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />
4.以上基本配置已经完成,打war包,运行。报错了
org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.ktios.dao.UserInfoMapper] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
看意思好像是mapper没有完成注入。辗转反侧,最终发现是web.xml中忘记加入
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
其作用如下:
如果您想要在自己所定义的Servlet类别中使用Spring的容器功能
ContextLoaderListener的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。
5.重新打包,执行,依旧报错
这次感觉奇怪了,tomcat启动后未发现任何错误,项目首页也正常访问,但是涉及数据库操作的访问浏览器一直转圈,拿不到任何返回,坑爹啊!!!!!!好歹给个错吧!!
初步估计是数据库配置的问题,自己写链接,设置配置,数据库可以正常链接,乖乖检查了很久,依旧不行,怀疑是使用 com.jolbox.bonecp.BoneCPDataSource连接池的问题。
换成org.apache.commons.dbcp.BasicDataSource试一下,问题出现了,如下:
java.lang.ClassNotFoundException: ${jdbc.driverClassName}
org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class '${jdbc.driverClassName}'
坑爹,将jdbc.driverClassName直接当成字符串加载了,是什么原因导致这样的事情发生的呢?答案如下:
在spring里使用org.mybatis.spring.mapper.MapperScannerConfigurer 进行自动扫描的时候,设置了sqlSessionFactory 的话,可能会导致PropertyPlaceholderConfigurer失效,也就是用${jdbc.driverClassName}这样之类的表达式,将无法获取到properties文件里的内容。 导致这一原因是因为,MapperScannerConigurer实际是在解析加载bean定义阶段的,这个时候要是设置sqlSessionFactory的话,会导致提前初始化一些类,这个时候,PropertyPlaceholderConfigurer还没来得及替换定义中的变量,导致把表达式当作字符串复制了。
解决方案:
改用sqlSessionFactoryBeanName注入就没有问题(不要使用sqlSessionFactory属性注入,使用sqlSessionFactoryBeanName注入),因为这时不会立即初始化sqlSessionFactory,传入的只是名字,非bean,所以不会引发提前初始化问题。。
<!-- MyBatis -->
<bean id="mSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="com.ktios.enity" />
<property name="mapperLocations" value="classpath:/resource/mybatis/*Mapper.xml" />
</bean>
<!-- 扫描mapper接口 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ktios.dao" />
<property name="annotationClass" value="com.ktios.dao.annotation.MyBatisRepository" />
<property name="sqlSessionFactoryBeanName" value="mSqlSessionFactory" />
</bean>
6.好了重启启动项目,ok成功运行。再次将数据库连接池换成com.jolbox.bonecp.BoneCPDataSource,也可以正常使用了,坑爹的!!!!!!!!此处需要主要两个链接池的属性有所差异
使用org.apache.commons.dbcp.BasicDataSource时需要如下配置:
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
7.最后再简单说说classpath和classpath*的问题
classpath就是代表项目的根目录(不知道对不对,但是感觉我的这个项目就是这样的)
比如说此项目对database.properties的引用。 直接使用classpath:/database.properties 不能找到文件,classpath*:/database.properties可以找到
好了,本文结束了,感想大家的拜读!!!其实整个过程中还有许多小插曲,比如说jar包的缺少。。。。。。
一年多来一直在做游戏服务器,web的东西很久没搞了,最近公司手上的事情不多,所以抽空准备弄一个springmvc的web项目。以前在上一家公司用过一段时间,不过那都是大神搭建的,自己也就是复制粘贴试了下水,没多大的感觉。公司网络限制,没法使用maven,也没建内部仓库(这是我想吐槽的地方),所以只有一个jar包一个jar包的下载。第一次找下来大概需要30多个jar包。开始搭建web环境,现在使用的eclipse不支持web开发,只有自己手动搭建。开始出现问题:
一、开始的时候未使用任何框架,就是一个简单的servlet,想看看项目的流程走动
1.jar包的放置位置。
开始把ja程引用的时候毫无问题,但是通过命令行打了war包以后,项目无法正常运行,没有办法只有把jar包放到WEB-INF/lib下面。ok,可以正常启动了。tomcat的启动默认是寻找WEB-INF下面的web.xml和lib下面的jar包。
2.项目输入路径和打war的路径
我的项目名叫JavaWeb,classpath的输出路径<classpathentry kind="output" path="webapp/WEB-INF/classes"/>。开始的时候我直接/JavaWeb目录下,通过dos运用命令进行的打包(jar -cvf JavaWeb.war*)大家发现问题了吗?接的看后面,将打好的jar包直接拷贝到tomcat的webapps下面,启动tomcat,项目启动失败。再仔细看
看classpath的输出路径根目录是webapp。将当前路径指向webapp再重新打包。ok,正常运行。
二、开始我们的springmvc搭建吧,项目继续延用上面的结构,引用一系列的jar包,在这里简单描述下我遇见的问题,其他的不便一一描述
项目结构基本如下
需要的jar包如下,有些jar包是此次不需要的,不影响运行
1.web.xml的配置
<context-param><param-name>contextConfigLocation</param-name>
<param-value>
classpath:/applicationContent.xml
</param-value>
</context-param>
<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:/applicationContent_web.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>
</web-app>
将servlet交给spring进行管理。引入spring的配置文件,默认是servlet-name + [-servlet].xml 也就是这里的SpringMVC-servlet.xml,如果不使用默认的,则需要加入这段代码
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/applicationContent_web.xml
</param-value>
</init-param>
加载其他配置文件
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/applicationContent.xml
</param-value>
</context-param>
此文件主要配置的数据库相关的
2.applicationContent_web.xml.xml文件中的主要内容
<!-- 注解驱动 -->
<mvc:annotation-driven />
<context:annotation-config />
<!-- 扫描包 -->
<context:component-scan base-package="com.ktios" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<!-- ViewResolver -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
3.applicationContent.xml中的主要内容
<context:property-placeholder location="classpath:/resource/database.properties"
ignore-unresolvable="true" />
<aop:aspectj-autoproxy />
<context:annotation-config />
<!-- 扫描包 -->
<context:component-scan base-package="com.ktios" />
<context:component-scan base-package="resource.mybatis" />
<!-- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"> -->
<bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource"
destroy-method="close">
<!-- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"> -->
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<!-- <property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" /> -->
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- MyBatis -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="com.ktios.enity" />
<property name="mapperLocations" value="classpath:/resource/mybatis/*Mapper.xml" />
</bean>
<!-- 扫描mapper接口 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ktios.dao" />
<property name="annotationClass" value="com.ktios.dao.annotation.MyBatisRepository" />
</bean>
<!-- Transaction -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 采用注解方式的事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />
4.以上基本配置已经完成,打war包,运行。报错了
org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.ktios.dao.UserInfoMapper] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
看意思好像是mapper没有完成注入。辗转反侧,最终发现是web.xml中忘记加入
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
其作用如下:
如果您想要在自己所定义的Servlet类别中使用Spring的容器功能
ContextLoaderListener的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。
5.重新打包,执行,依旧报错
这次感觉奇怪了,tomcat启动后未发现任何错误,项目首页也正常访问,但是涉及数据库操作的访问浏览器一直转圈,拿不到任何返回,坑爹啊!!!!!!好歹给个错吧!!
初步估计是数据库配置的问题,自己写链接,设置配置,数据库可以正常链接,乖乖检查了很久,依旧不行,怀疑是使用 com.jolbox.bonecp.BoneCPDataSource连接池的问题。
换成org.apache.commons.dbcp.BasicDataSource试一下,问题出现了,如下:
java.lang.ClassNotFoundException: ${jdbc.driverClassName}
org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class '${jdbc.driverClassName}'
坑爹,将jdbc.driverClassName直接当成字符串加载了,是什么原因导致这样的事情发生的呢?答案如下:
在spring里使用org.mybatis.spring.mapper.MapperScannerConfigurer 进行自动扫描的时候,设置了sqlSessionFactory 的话,可能会导致PropertyPlaceholderConfigurer失效,也就是用${jdbc.driverClassName}这样之类的表达式,将无法获取到properties文件里的内容。 导致这一原因是因为,MapperScannerConigurer实际是在解析加载bean定义阶段的,这个时候要是设置sqlSessionFactory的话,会导致提前初始化一些类,这个时候,PropertyPlaceholderConfigurer还没来得及替换定义中的变量,导致把表达式当作字符串复制了。
解决方案:
改用sqlSessionFactoryBeanName注入就没有问题(不要使用sqlSessionFactory属性注入,使用sqlSessionFactoryBeanName注入),因为这时不会立即初始化sqlSessionFactory,传入的只是名字,非bean,所以不会引发提前初始化问题。。
<!-- MyBatis -->
<bean id="mSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeAliasesPackage" value="com.ktios.enity" />
<property name="mapperLocations" value="classpath:/resource/mybatis/*Mapper.xml" />
</bean>
<!-- 扫描mapper接口 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ktios.dao" />
<property name="annotationClass" value="com.ktios.dao.annotation.MyBatisRepository" />
<property name="sqlSessionFactoryBeanName" value="mSqlSessionFactory" />
</bean>
6.好了重启启动项目,ok成功运行。再次将数据库连接池换成com.jolbox.bonecp.BoneCPDataSource,也可以正常使用了,坑爹的!!!!!!!!此处需要主要两个链接池的属性有所差异
使用org.apache.commons.dbcp.BasicDataSource时需要如下配置:
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
7.最后再简单说说classpath和classpath*的问题
classpath就是代表项目的根目录(不知道对不对,但是感觉我的这个项目就是这样的)
比如说此项目对database.properties的引用。 直接使用classpath:/database.properties 不能找到文件,classpath*:/database.properties可以找到
好了,本文结束了,感想大家的拜读!!!其实整个过程中还有许多小插曲,比如说jar包的缺少。。。。。。