第一次搭建ssm框架,其中遇到了些问题,想写篇博客总结下,同时也记录下这个bug,避免后人走弯路。一个很不起眼的bug,谁也想不到。话不多说,上正题
1.配置
web.xml是整个web项目的配置文件,主要配置一些过滤器、 监听器。代码如下:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>ssmTest</display-name> <!-- needed for ContextLoaderListener --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param>
<!-- Bootstraps the root web application context before servlet initialization --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- The front controller of this Spring Web application, responsible for handling all application requests --> <servlet> <servlet-name>springDispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Map all requests to the DispatcherServlet for handling --> <servlet-mapping> <servlet-name>springDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 解决post乱码问题的过滤器 --> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
主要就是配置spring和spring mvc的监听器和转发器。首先配置了spring的ContextLoaderListener,当web应用被加载时,它会创建ApplicationContext,也就是应用上下文,装配ApplicationContext.xml中配置的bean,完成依赖注入。这是spring最基础,也是最核心的功能---IOC容器和DI。<br> 接着我们配置了DispatcherServlet--->这是spring mvc的核心。首先解释下springmvc是啥。spring mvc是web框架,负责处理前台发过来的请求,并将结果返回给用户。而DispatcherServlet就是负责转发。根据URL匹配前端发来的请求,并把请求转发给控制层相对应的方法,最后把结果返回给用户,其实就是返回一个视图。这里就要说到spring mvc的配置了。代码如下:
spring mvc.xml;
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 配置自动扫描的包,只扫描控制器 --> <context:component-scan base-package="cn.ccsu"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <!-- 配置视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"></property> <property name="suffix" value=".jsp"></property> </bean> </beans>
视图解析器就是负责返回视图给用户。具体的做法是:prefix(前缀) + 目标方法返回值 +suffix(后缀)。具体到这个项目中,如果某个方法的返回值是"List"(String类型),那么映射的视图就是/WEB-INF/views/list.jsp,根据方法返回值映射到相应的视图。 另外,我在一开始配置了自动扫面,扫描项目的所有控制器,并创建。
以上就是spring mvc最主要的工作。 继续回到web.xml,最后我们配置了一个characterEncodingFilter--->字符冲编码过滤器。从名字就知道,这个过滤器就是解决字符乱码问题的。将字符的编码格式置为UTF-8,防止出现乱码。
接下来就是重头戏了--->applicationContext.xml,也就是spring的一些配置, 这是核心。
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"> <context:component-scan base-package="cn.ccsu"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <context:property-placeholder location="classpath:jdbc.properties" /> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driver}"></property> <property name="jdbcUrl" value="${jdbc.url}"></property> <property name="password" value="${jdbc.password}"></property> <property name="user" value="${jdbc.username}"></property> </bean> <!-- spring事务管理 --> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 开启基于注解的事务 --> <tx:annotation-driven transaction-manager="dataSourceTransactionManager" /> <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"></property> <property name="configLocation" value="classpath:mybatis-config.xml"></property> <property name="mapperLocations" value="classpath:cn/ccsu/mybatis/xml/*.xml"></property> </bean> <!--配置一个可以进行批量执行的sqlSession --> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg> <constructor-arg name="executorType" value="BATCH"></constructor-arg> </bean> <mybatis-spring:scan base-package="cn.ccsu.dao" /> </beans>
配置有点多,我们慢慢看。
开头和spring mvc一样,配置了一个自动扫描器,扫描所有需要自动装配的bean,除了控制器。控制器是spring mvc负责管理的。接着引入外部属性文件jdbc.properties,配置连接数据库的数据源。然后我们配置了sqlSessionFactory bean和sqlSession,这是需要注入到mapper里的,这样才可以直接调用mapper的方法和数据库进行交互。最后是<mybatis-spring:scan base-package="cn.ccsu.dao" />,他负责扫描cn.ccsu.dao包下的所有mapper,并初始化所有mapper,最后将其注入到依赖它的bean中。
2.测试
逻辑很简单:Controller层调用Service层,Service层调用dao层,和数据库交互,最后将数据传回到Controller层,渲染到视图上并返回给用户。
首先是控制层,代码如下:
package cn.ccsu.controller; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import cn.ccsu.bean.User; import cn.ccsu.service.UserService; @Controller public class UserController { @Autowired private UserService userService; public UserController() { System.out.println("UserController controller:" + this.getClass().getName()); } @RequestMapping("/Testssm") public String login(Map<String, Object> map) { User user = userService.validateUser(); map.put("user", user); return "login"; } }
Service层:
package cn.ccsu.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import cn.ccsu.bean.User; import cn.ccsu.dao.UserMapper; @Service public class UserService { @Autowired private UserMapper userMapper; public UserService() { System.out.println("UserService controller:" + this.getClass().getName()); } public User validateUser() { System.out.println("UserService validateUser"); // User getUserById(Integer id) return userMapper.getUserById(981); } }
Dao层 :
package cn.ccsu.dao; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import cn.ccsu.bean.User; @Repository public interface UserMapper { boolean updateUserById(User user); boolean addUser(User user); boolean deleteUser(User user); User selectUser(@Param("userName") String userName, @Param("mobile") String mobile); User getUserById(Integer id); }
运行结果如下:
3.遇到的bug
在配置过程中遇到了一个小bug,很有意思,问题出在jdbc.properties文件。一开始我是这么写的:
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/ibatistest username=root password=
报错是:“Access denied for user 'root'@'localhost'”
后来我改成了这样,问题解决了。
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/ibatistest jdbc.username=root jdbc.password=
具体原因我也不清楚。之前单独使用的时候没有出问题,这次搭建框架就出了问题,我也很纳闷。如果有明白的,可以解释下原因,我先在此谢过啦!!
说了这么多,最后附上demo 地址:https://github.com/YanSiJu/MyProject/tree/master/ssmTest