1、搭建项目结构
首先,利用Maven的继承和聚合特性搭建一个分模块的项目骨架,如下图所示样子,ssm-parent作为顶级父工程,打包方式为pom,主要用于集中管理项目中的依赖、插件、配置,同时它会聚合一些子模块,比如这里的ssm-common和ssm-manage两个子模块。
ssm-common作为继承自ssm-parent的子模块,打包方式为jar,主要用于编写一些通用的功能模块以供其它模块使用;ssm-manage同样作为继承自ssm-parent的子模块,但是与ssm-common不同的是,ssm-manage也是一个聚合工程,打包方式为pom,它聚合了ssm-manage-pojo、ssm-manage-mapper、ssm-manage-service、ssm-manage-web四个子模块,而这四个子模块均直接继承自ssm-manage,同时也就间接继承自ssm-parent,它们的打包方式分别是jar、jar、jar、war,同时从名字也可以看出ssm-manage-pojo用于编写一些pojo类,ssm-manage-mapper用于编写一些MyBatis的Mapper类,ssm-manage-service用于编写一些service类,最后ssm-manage-web用于编写一些controller类,同时它们之间的依赖关系如下图,这四个模块一起就组成了传统的不分模块的web项目。所以,如果还需要在ssm-parent下建立其它的一些web工程,采用ssm-manage这种聚合的思路就可以了。
接下来,在ssm-parent工程下的pom.xml文件中配置tomcat插件,因为不是所有子模块都需要用到该插件,所以将其配置在pluginManagement标签中进行管理
<properties>
<tomcat.plugin.version>2.2</tomcat.plugin.version>
</properties>
<build>
<pluginManagement>
<plugins>
<!-- 配置Tomcat7插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>${tomcat.plugin.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
就目前而言,整个项目就ssm-manage聚合工程中拥有一个ssm-manage-web的web模块,所以接下来就需要在ssm-manage这个聚合工程中配置使用这个tomcat插件,一定要注意的是聚合工程的tomcat插件要配置在聚合工程的pom.xml文件中
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<!-- http://127.0.0.1:{port}/{path} -->
<path>/</path>
<port>8080</port>
</configuration>
</plugin>
</plugins>
</build>
到这一步后就可以在ssm-manage聚合工程上执行mvn tomcat7:run命令来启动ssm-manage工程了。
2、集成Spring框架
首先在ssm-manage-web模块中的web.xml文件中添加Spring的ApplicationContext载入监听器
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
这个监听类在spring-web模块中,所以此时需要引入spring-web的依赖,首先在ssm-parent工程的pom.xml文件中配置该依赖,spring.version是配置在properties标签中的一个属性
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
接下来就需要配置使用该依赖了,但是现在的问题是包括ssm-manage这个聚合工程以及四个子模块在内一共有5个模块,到底应该是在哪个模块中配置使用该依赖呢?总的来说,聚合工程依赖的导入原则有三条:
- 所有模块都需要用到的依赖应该在聚合工程中导入,因为这样,所有子模块都会自动继承下来
- 在使用依赖的最底层导入
- 运行时需要的依赖在web模块中导入,因为这样的依赖在编码的时候不会用到,只有在运行时才会用到,所以在web模块中导入是最合适的
根据这个原则,那么这里的spring-web这个依赖就应该在ssm-manage-service模块中导入,因为在该模块中将来编写的一些Service类会用到spring-context、spring-beans等模块中的一些注解,而spring-web这个模块恰好又依赖了这些模块,所以只要引入了spring-web的依赖,根据依赖的传递性,就会引入spring-context、spring-beans等模块,所以应该在使用依赖的最底层导入,即ssm-manage-service模块
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
spring-web模块依赖了其它的一些模块,如下图所示:
接着,在Web应用中集成Spring框架时,Spring容器监听器默认加载/WEB-INF/applicationContext.xml文件,可以通过配置contextConfigLocation参数来指定监听器加载的文件路径以及要加载的文件,所以这里在ssm-manage-web的src/main/resources目录下建立一个spring目录,用于存储Spring的一些配置文件,并在其中编写一个叫做spring-context.xml的文件,然后在ssm-manage-web模块中的web.xml文件中配置Spring容器监听器加载该文件
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-context.xml</param-value>
</context-param>
接下来,配置数据源,首先在ssm-manage-web模块的src/main/resources目录中编写一个名叫jdbc.properties的文件,用于配置数据库的一些连接信息
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/ssm?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
jdbc.username=root
jdbc.password=123456
接着,在ssm-manage-web模块的src/main/resources/spring文件夹中的spring-context.xml文件中配置读取该文件,这种配置方式更底层一些
<!-- 读取资源文件,并使用Spring自带的占位符替换功能 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<!-- 允许JVM参数覆盖,就是说那些JVM -D的参数可以覆盖这些资源文件中的参数,比如java -Djdbc.username=zs -->
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<!-- 忽略没有找到的资源文件 -->
<property name="ignoreResourceNotFound" value="true" />
<!-- 配置资源文件 -->
<property name="locations">
<list>
<value>classpath:jdbc.properties</value>
</list>
</property>
</bean>
接着,在ssm-manage-web模块的src/main/resources/spring文件夹中编写一个叫做spring-datasource.xml的文件,用于配置数据源信息
<!-- 定义数据源 -->
<bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
<!-- 数据库驱动 -->
<property name="driverClass" value="${jdbc.driverClassName}" />
<!-- 相应驱动的jdbcUrl -->
<property name="jdbcUrl" value="${jdbc.url}" />
<!-- 数据库的用户名 -->
<property name="username" value="${jdbc.username}" />
<!-- 数据库的密码 -->
<property name="password" value="${jdbc.password}" />
<!-- 检查数据库连接池中空闲连接的间隔时间,单位是分,如果要取消则设置为0 -->
<property name="idleConnectionTestPeriodInMinutes" value="60" />
<!-- 连接池中未使用的链接最大存活时间,单位是分,如果要永远存活设置为0 -->
<property name="idleMaxAgeInMinutes" value="30" />
<!-- 每个分区最大的连接数,判断依据:请求并发数 -->
<property name="maxConnectionsPerPartition" value="100" />
<!-- 每个分区最小的连接数 -->
<property name="minConnectionsPerPartition" value="5" />
</bean>
这里用到的是bonecp连接池,所以还需要在ssm-parent工程的pom.xml文件中配置管理bonecp的依赖以及数据库驱动的依赖
<dependency>
<groupId>com.jolbox</groupId>
<artifactId>bonecp-spring</artifactId>
<version>${bonecp.spring.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
然后,按照依赖导入原则,此时应该在ssm-manage-web模块中配置使用这两个依赖;接着在spring-context.xml文件中导入spring-datasource.xml文件
<import resource="spring-datasource.xml" />
接着,在ssm-manage-service模块的src/main/java目录中创建一个com.ssm.manage.service包,用于编写一些Service类,并在ssm-manage-web模块的src/main/resources/spring/spring-context.xml文件中配置包扫描器
<!-- 包扫描器 -->
<context:component-scan base-package="com.ssm.manage.service"/>
接着,在ssm-manage-web模块的src/main/resources/spring文件夹中建立一个叫做spring-transaction.xml的文件,用于配置事务
<!-- 定义事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 定义事务策略 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 所有以query开头的方法都是只读的 -->
<tx:method name="query*" read-only="true" />
<!-- 其他方法使用默认事务策略 -->
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<aop:config>
<!-- pointcut元素定义一个切入点,execution中的第一个星号用于匹配方法的返回类型,这里表明匹配所有返回类型。
com.ssm.manage.service.*.*(..)表明匹配com.ssm.manage.service包下的所有类的所有方法 -->
<aop:pointcut id="txPointcut" expression="execution(* com.ssm.manage.service.*.*(..))" />
<!-- 将定义好的事务处理策略应用到上述的切入点 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
</aop:config>
Spring的事务需要用到spring-jdbc和spring-aspects模块,所以还需要在ssm-parent工程的pom.xml文件中配置管理这两个依赖,并按照依赖导入原则,此时应该在ssm-manage-web模块中配置使用这两个依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
接着在spring-context.xml文件中导入spring-transaction.xml文件
<import resource="spring-transaction.xml" />
3、Spring与MyBatis集成
首先,在ssm-parent工程的pom.xml文件中配置管理Mybatis以及与Spring集成所需依赖
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis.spring.version}</version>
</dependency>
然后,按照依赖导入原则,此时应该在ssm-manage-web模块中配置使用mybatis-spring这个依赖;而由于以后会在ssm-manage-service模块中使用MyBatis的分页插件,这个插件会用到MyBatis的一些API,所以应该在ssm-manage-service模块中配置使用mybatis这个依赖。
接着,在ssm-manage-web的src/main/resources目录下建立一个mybatis目录,并在该目录下创建一个名叫mybatis-config.xml的文件,该配置文件目前不需要配置什么内容,为以后添加一些配置做准备;并在mybatis目录下创建一个mappers目录,以后的一些mapper映射文件就放在这里面
<?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>
</configuration>
接着,在ssm-manage-pojo模块的src/main/java目录中创建一个com.ssm.manage.pojo包,用于存放一些简单pojo类,并在ssm-manage-mapper模块的src/main/java目录中创建一个com.ssm.manage.mapper包,用于存放一些mapper接口;然后在ssm-manage-web的src/main/resources/spring目录下建立一个spring-mybatis.xml文件,集成mybatis
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定数据源 -->
<property name="dataSource" ref="dataSource"/>
<!-- 指定mybatis的全局配置文件 -->
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
<!-- 指定mapper.xml文件,扫描所有的文件 -->
<property name="mapperLocations" value="classpath:mybatis/mappers/**/*.xml"/>
<!-- 指定别名包 -->
<property name="typeAliasesPackage" value="com.ssm.manage.pojo"/>
</bean>
<!-- 定义Mapper接口的扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ssm.manage.mapper"/>
</bean>
最后在spring-context.xml文件中导入spring-mybatis.xml文件
<import resource="spring-mybatis.xml" />
此时如果在ssm-manage聚合工程上执行mvn tomcat7:run命令来启动ssm-manage工程会得到“class path resource [mybatis/mappers/] cannot be resolved to URL because it does not exist”的错误,这是因为mybatis/mappers目录下还没有任何映射文件,此时可以临时将spring-mybatis.xml文件中这一行代码注释掉即可,待以后有需要加载的mappers映射文件时再来取消注释即可
<property name="mapperLocations" value="classpath:mybatis/mappers/**/*.xml"/>
在这里我们采用通用Mapper的方式来进行开发,所以还需要配置通用Mapper,首先在ssm-parent工程的pom.xml文件中配置管理通用Mapper所需依赖
<dependency>
<groupId>com.github.abel533</groupId>
<artifactId>mapper</artifactId>
<version>${mybatis.mapper.version}</version>
</dependency>
然后,按照依赖导入原则,此时应该在ssm-manage-pojo模块中配置使用这个依赖,因为使用通用Mapper时会在POJO上配置一些JPA注解,而这个依赖恰好依赖了JPA的一些API。
接着,在ssm-manage-web的src/main/resources/mybatis目录中的mybatis-config.xml文件中配置通用Mapper插件
<plugins>
<!-- 通用Mapper -->
<plugin interceptor="com.github.abel533.mapperhelper.MapperInterceptor">
<!-- UUID生成策略 -->
<!-- 配置UUID生成策略需要使用OGNL表达式 -->
<!-- 默认值32位长度:@java.util.UUID@randomUUID().toString().replace("-", "") -->
<!-- <property name="UUID" value="@java.util.UUID@randomUUID().toString()"/> -->
<!-- 主键自增回写方法,默认值MYSQL -->
<property name="IDENTITY" value="MYSQL" />
<!-- 序列的获取规则,使用{num}格式化参数,默认值为{0}.nextval,针对Oracle -->
<!-- 可选参数一共3个,对应0,1,2,分别为SequenceName,ColumnName,PropertyName -->
<!-- <property name="seqFormat" value="{0}.nextval" /> -->
<!-- 主键自增回写方法执行顺序,默认AFTER,可选值为(BEFORE|AFTER) -->
<property name="ORDER" value="AFTER" />
<!-- 通用Mapper接口,多个通用接口用逗号隔开 -->
<property name="mappers" value="com.github.abel533.mapper.Mapper"/>
</plugin>
</plugins>
接下来,一般项目中都会使用到分页查询的功能,所以这里我们用到了MyBatis的分页插件,首先,在ssm-parent工程的pom.xml文件中配置管理MyBatis分页插件所需依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>${mybatis.pagehelper.version}</version>
</dependency>
然后,按照依赖导入原则,此时应该在ssm-manage-service模块中配置使用这个依赖,因为在编写Service时会用到分页插件提供的一些API。
接着,在ssm-manage-web的src/main/resources/mybatis目录中的mybatis-config.xml文件中配置MyBatis分页插件,注意:一定要将分页插件配置在第一个,通用Mapper配置在最后一个,否则会出现这样的错误:“java.lang.NoSuchMethodException: com.github.abel533.mapper.MapperProvider.<init>()”。
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 没有默认值,必须指定该属性 -->
<property name="dialect" value="mysql"/>
<!-- 该参数默认为false -->
<!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用,可以用页码和页面大小两个参数进行分页 -->
<!-- 和startPage中的pageNum效果一样 -->
<property name="offsetAsPageNum" value="false"/>
<!-- 该参数默认为false -->
<!-- 设置为true时,使用RowBounds分页会进行count查询 -->
<property name="rowBoundsWithCount" value="true"/>
<!-- 该参数默认为false -->
<!-- 设置为true时,如果pageSize = 0或者RowBounds.limit = 0就会查询出全部的结果 -->
<!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型)-->
<property name="pageSizeZero" value="false"/>
<!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
<!-- 启用合理化时,如果pageNum < 1会查询第一页,如果pageNum > pages会查询最后一页 -->
<!-- 禁用合理化时,如果pageNum < 1或pageNum > pages会返回空数据 -->
<property name="reasonable" value="false"/>
</plugin>
4、集成Spring MVC
首先,在ssm-parent工程的pom.xml文件中配置管理Spring MVC所需依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
然后,按照依赖导入原则,此时应该在ssm-manage-web模块中配置使用这个依赖
接着,在ssm-manage-web模块的src/main/java目录下创建一个com.ssm.manage.controller包,用于编写一些Spring Controller类;并在src/main/webapp/WEB-INF目录下创建一个views目录,用于存放一些JSP文件
接着,在ssm-manage-web模块的src/main/resources/spring目录中创建一个spring-mvc.xml文件,用于配置一些关于Spring MVC的信息
<!-- 定义注解驱动 -->
<mvc:annotation-driven />
<!-- 定义Controller的扫描包 -->
<context:component-scan base-package="com.ssm.manage.controller"/>
<!-- 定义试图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
接下来,配置ssm-manage-web模块中的web.xml文件,配置Spring MVC相关信息
<!-- 配置Spring MVC框架入口 -->
<servlet>
<servlet-name>ssm-manage</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 默认加载的是/WEB-INF/${servlet-name}-servlet.xml配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-mvc.xml</param-value>
</init-param>
<!-- 取值为0或1,表示false/true,表示是否在容器启动时初始化这个servlet,否则在第一次访问的时候才去初始化 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ssm-manage</servlet-name>
<!-- 配置方式有以下三种 -->
<!-- / -->
<!-- /xxx/* -->
<!-- *.xxx,比如*.do,而在写Controller时映射路径上可以不写.xxx后缀也是可以的 -->
<!-- 特别的,不能配置为/*,/*在struts中是可以的,但是在Spring MVC中是不行的,因为入口不同 -->
<!-- struts的入口是一个过滤器,而Spring MVC是一个servlet,servlet是不能使用/*的,/*表示全部 -->
<!-- 在servlet中表示全部用/ -->
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
5、其他一些配置
首先,在使用SpringMVC框架时一般都会用到Json序列化,比如用到@ResponseBody注解,所以还需要在ssm-parent模块的pom.xml文件中配置管理Json序列化依赖,然后根据依赖导入原则,还需要在ssm-manage-web模块中配置使用该依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
接着,在ssm-manage-web模块的web.xml文件中添加CharacterEncodingFilter过滤器来解决POST请求乱码的问题,要注意的是这个过滤器不能解决GET请求乱码问题
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
接着,引入日志框架,这里采用的是slf4j作为抽象的日志框架,而采用logback作为具体的日志框架,所以首先在ssm-parent模块的pom.xml文件中配置管理这些依赖,由于logback是slf4j的原生实现,所以不需要像使用log4j作为具体的日志框架时还需要引入适配器依赖
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
根据依赖导入原则,此时应该在ssm-manage这个聚合模块中配置使用slf4j这个依赖,而logback这个依赖在ssm-manage-web模块中配置使用就好
接着,在ssm-manage-web模块的src/main/resources目录下新建一个logback.xml的日志配置文件,具体内容如下,到时候根据具体使用情况进行修改
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder charset="UTF-8">
<pattern>%d{MM-dd HH:mm:ss} [%thread] [%-5level] %msg -%logger{35}:%L%n</pattern>
</encoder>
</appender>
<appender name="DAYFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>ssm.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>ssm.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{MM-dd HH:mm:ss} [%thread] [%-5level] %msg -%logger{35}:%L%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT"/>
<appender-ref ref="DAYFILE"/>
</root>
</configuration>
接着,在ssm-parent模块的pom.xml文件中配置管理单元测试Junit的依赖,并根据依赖导入原则,在ssm-manage聚合工程中配置使用该依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
接着,配置一些通用的依赖,并根据依赖导入原则,各自在相应的模块中配置使用这些依赖
<!-- JSP相关 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>${servlet-api.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>${jsp-api.version}</version>
<scope>provided</scope>
</dependency>
<!-- Apache工具组件 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>