工程目录结构
软件环境
windows 10、jdk1.8+、maven3.4、mysql5.7+
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zeecle.web</groupId>
<artifactId>zeecle-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.10.RELEASE</spring.version>
<mybatis.version>3.2.6</mybatis.version>
<mybatis.spring.version>1.2.2</mybatis.spring.version>
<slf4j.version>1.7.21</slf4j.version>
<log4j.version>1.2.17</log4j.version>
<shiro.version>1.4.0</shiro.version>
<httpclient.version>4.5.3</httpclient.version>
<mysql.version>5.1.38</mysql.version>
<json.version>1.1.36</json.version>
<cglib.version>3.2.4</cglib.version>
<junit.version>4.8.1</junit.version>
<aspectj.version>1.8.10</aspectj.version>
<aspectjweaver.version>1.5.4</aspectjweaver.version>
<javax.version>7.0</javax.version>
</properties>
<dependencies>
<!-- spring 核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring 事务处理 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- spring 切面 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- aop注解 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectjweaver.version}</version>
</dependency>
<!-- shiro核心包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-quartz</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- c3p0 -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>mchange-commons-java</artifactId>
<version>0.2.14</version>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- mybatis/spring包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis.spring.version}</version>
</dependency>
<!-- CGLIB,mybatis的延迟加载中会用到该动态代理 -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>${cglib.version}</version>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- 日志文件管理包 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>${javax.version}</version>
</dependency>
<!-- 格式化JSON对象-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${json.version}</version>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- 配置maven编译环境为jdk1.8 -->
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<fork>true</fork>
<meminitial>128m</meminitial>
<maxmem>256m</maxmem>
</configuration>
</plugin>
</plugins>
</build>
<modules>
<module>zeecle-web</module>
</modules>
</project>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1" metadata-complete="true">
<!-- 项目系统根路径 -->
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>zeecle.web</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.WebAppRootListener</listener-class>
</listener>
<!-- log4j配置文件加载 -->
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:log4j.properties</param-value>
</context-param>
<!-- 启动一个watchdog线程每1800秒扫描一下log4j配置文件的变化 -->
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>1800000</param-value>
</context-param>
<!-- spring log4j监听器 -->
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<!-- 加载Spring MVC配置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:config/spring/spring-mybatis.xml
<!-- classpath:config/spring/spring-shiro.xml -->
</param-value>
</context-param>
<!-- 自动装配ApplicationContext的配置信息 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>zeecle-web</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/spring/spring-servlet.xml</param-value>
</init-param>
<!-- 启动优先级 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>zeecle-web</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 设置字符集 -->
<filter>
<filter-name>encodingFilter</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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 错误页面处理 -->
<error-page>
<error-code>404</error-code>
<location>/error/404</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error/500</location>
</error-page>
</web-app>
spring-mybatis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-4.0.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache-4.0.xsd
">
<!-- 扫描IOC注解bean -->
<context:component-scan base-package="com.zeecle.web">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<!-- 任务处理 -->
<task:annotation-driven />
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:props/jdbc.properties</value>
</list>
</property>
</bean>
<!-- 数据源配置 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="testConnectionOnCheckin" value="${dbcp.testConnectionOnCheckin}"/>
<property name="testConnectionOnCheckout" value="${dbcp.testConnectionOnCheckout}"/>
</bean>
<!-- 定义mybatis sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource" />
<!-- 批量注入别名 -->
<property name="typeAliasesPackage" value="com.zeecle.web.model.*.*" />
<!-- 注入mybatis配置文件 -->
<property name="configLocation" value="classpath:config/mybatis/mybatis-mapper.xml" />
<!-- 批量注入mapper配置文件 -->
<property name="mapperLocations" value="classpath:config/mybatis/mapper/*.xml" />
</bean>
<!-- mapper代理接口批量扫描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 扫描多个包value 采用逗号相隔-->
<property name="basePackage" value="com.zeecle.web.dao" />
<!-- 使用scanner时注册sqlSessionFactory时name必须sqlSessionFactoryBeanName -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!-- 事务处理 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 使用注解方式事务:@Transactional
该种方式配置在方法或类上使用注解进行事务,显得麻烦
<tx:annotation-driven transaction-manager="transactionManager" />
<aop:aspectj-autoproxy />
-->
<!-- 非注解方式 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="edit*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
<aop:pointcut expression="execution(* com.zeecle.web.impl.*.*(..))"
id="txPointcut" />
<aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice" />
</aop:config>
</beans>
spring-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
<!-- 自动扫描包:
由于在spring配置文件中已经扫描service,此处只扫描Controller即可!
use-default-filters="false":表示只扫描include指定的包,true 表示除去exclude全扫描
-->
<context:component-scan base-package="com.zeecle.web" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<!-- MVC注解驱动扫描:@RequestMapping -->
<mvc:annotation-driven />
<!-- 配置默认静态资源处理 -->
<mvc:default-servlet-handler/>
<!-- 配置静态资源解析 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 消息控制 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
<value>applicaiton/json;charset=UTF-8</value>
</list>
</property>
</bean>
</list>
</property>
</bean>
</beans>
mybatis-mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd" >
<configuration>
<settings>
<!-- 打开延迟加载的开关 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 将积极加载改为消息加载即按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
</configuration>
jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.200.75:3306/testdb
jdbc.username=root
jdbc.password=123@abcd
dbcp.initialSize=15
dbcp.maxIdle=5
dbcp.minIdle=1
dbcp.maxActive=50
dbcp.logAbandoned=true
dbcp.removeAbandoned=true
dbcp.removeAbandonedTimeout=180
dbcp.maxWait=5000
dbcp.validationQuery=select now() as nowtime
dbcp.testOnBorrow=true
dbcp.testOnReturn=true
dbcp.testWhileIdle=true
dbcp.timeBetweenEvictionRunsMillis=5000
dbcp.numTestsPerEvictionRun=3
dbcp.minEvictableIdleTimeMillis=1800000
dbcp.testConnectionOnCheckin=false
dbcp.testConnectionOnCheckout=true
log4j.properties
#日志输出级别
log4j.rootLogger=DEBUG,stdout,other
#设置stdout的日志输出控制台
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#输出日志到控制台的方式,默认为System.out
log4j.appender.stdout.Target = System.out
#设置使用灵活布局
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#灵活定义输出格式
log4j.appender.stdout.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH:mm:ss}] %l %m %n
#设置other的日志输出控制台
log4j.appender.other=org.apache.log4j.RollingFileAppender
#设置other的输出日志
log4j.appender.other.File=${zeecle.web}/logs/sys.log
#设置other的日志最大限制
log4j.appender.other.MaxFileSize=1024KB
#最多只保存20个备份文件
log4j.appender.other.MaxBackupIndex=1000
#输出INFO级别以上的日志
log4j.appender.other.Threshold=INFO
#设置使用灵活布局
log4j.appender.other.layout=org.apache.log4j.PatternLayout
#灵活定义输出格式
log4j.appender.other.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH:mm:ss}] %l %t %m %n
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
log4j.logger.org.springframework = ERROR
实例
新建一个UserInfo对象
package com.zeecle.web.model;
public class UserInfo {
private Integer id;
private String username;
private String password;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
为UserInfo对象模型添加映射DAO和Mapper,DAO为一个接口,与mybatis的mapper文件对应
UserDAO.java:
package com.zeecle.web.dao;
import com.zeecle.web.model.UserInfo;
public interface UserDAO {
boolean saveUser(UserInfo user) throws Exception;
boolean deleteUser(int id) throws Exception;
boolean updateUser(UserInfo user) throws Exception;
UserInfo findUser(String username) throws Exception;
}
UserMapper.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.zeecle.web.dao.UserDAO">
<insert id="saveUser" parameterType="com.zeecle.web.model.UserInfo">
INSERT INTO T_SYS_USER (username, password) VALUE(#{username}, #{password})
</insert>
<delete id="deleteUser" parameterType="int">
DELETE FROM T_SYS_USER WHERE id = #{id}
</delete>
<update id="updateUser" parameterType="com.zeecle.web.model.UserInfo">
UPDATE T_SYS_USER
<trim prefix="SET" suffixOverrides=",">
<if test="password!=null">password=#{password},</if>
</trim>
WHERE id = #{id}
</update>
<select id="findUser" parameterType="java.lang.String" resultType="com.zeecle.web.model.UserInfo">
SELECT * FROM T_SYS_USER WHERE username = #{username}
</select>
</mapper>
添加服务层接口实现
UserService.java
package com.zeecle.web.service;
import com.zeecle.web.model.UserInfo;
public interface UserService {
boolean saveUser(UserInfo user);
boolean deleteUser(int id);
boolean updateUser(UserInfo user);
UserInfo findUser(String username);
}
UserServiceImpl.java
package com.zeecle.web.service.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zeecle.web.dao.UserDAO;
import com.zeecle.web.model.UserInfo;
import com.zeecle.web.service.UserService;
@Service("userService")
public class UserServiceImpl implements UserService{
private static final Logger LOG = LoggerFactory.getLogger(UserServiceImpl.class);
@Autowired
UserDAO userDAO;
@Override
public boolean saveUser(UserInfo user) {
boolean result = false;
try {
result = userDAO.saveUser(user);
} catch (Exception e) {
LOG.info(e.getMessage());
result = false; // 异常可自定义捕获,这里不做深究
}
return result;
}
@Override
public boolean deleteUser(int id) {
boolean result = false;
try {
result = userDAO.deleteUser(id);
} catch (Exception e) {
LOG.info(e.getMessage());
result = false;
}
return result;
}
@Override
public boolean updateUser(UserInfo user) {
boolean result = false;
try {
result = userDAO.updateUser(user);
} catch (Exception e) {
LOG.info(e.getMessage());
result = false;
}
return result;
}
@Override
public UserInfo findUser(String username) {
UserInfo user = null;
try {
user = userDAO.findUser(username);
} catch (Exception e) {
LOG.info(e.getMessage());
}
return user;
}
}
测试类
package com.zeecle.web.test;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.zeecle.web.model.UserInfo;
import com.zeecle.web.service.UserService;
public class UserServiceTest {
ApplicationContext context = null;
UserService userService = null;
@Before
public void init() {
context = new ClassPathXmlApplicationContext("classpath:config/spring/spring-mybatis.xml");
userService = (UserService)context.getBean("userService");
}
@Test
public void saveUserTest() {
UserInfo user = new UserInfo();
user.setUsername("tanwei");
user.setPassword("tanwei");
boolean res = userService.saveUser(user);
System.out.println(res);
}
@Test
public void deleteUserTest() {
boolean res = userService.deleteUser(4);
System.out.println(res);
}
@Test
public void updateUserTest() {
UserInfo user = userService.findUser("zeecle");
user.setPassword("tanwei");
boolean res = userService.updateUser(user);
System.out.println(res);
}
@Test
public void findUserTest() {
UserInfo user = userService.findUser("tanwei");
System.out.println(user);
}
}
后记
至此,SpringMVC+Mybatis的环境搭建已经完成了,这里主要还是以配置文件的方式来配置,如果学习过SpringBoot的同学来说,使用纯注解方式来作为一个服务应用无疑是最好的选择,毕竟配置文件这么多,看着都头疼,但是我们也别嫌麻烦,理解其中的设计模式,对我们今后的项目开发还是有所帮助的。切记:多看官方文档给出的例子,Spring里还有很多值得琢磨的东西,比如后期我会写一些Spring Security、 Spring Data/ JPA等的BLOG,也算是弥补当初乱而杂的工作模式。