最近在回顾和总结一些技术,想到了把之前比较火的 SSM 框架重新搭建出来,作为一个小结,同时也希望本文章写出来能对大家有一些帮助和启发,因本人水平有限,难免可能会有一些不对之处,欢迎各位大神拍砖指教,共同进步。

本文章示例使用 IntelliJ IDEA 来开发,JDK 使用 11 版本,其余各框架和技术基本上使用了文章撰写当时的最新版本。

好的,下面直接进入正题。

打开 IntelliJ IDEA,File > New > Project > Maven,选中“Create from archetype”,然后再选中“org.apache.maven.archetypes:maven-archetype-webapp”:

Next,输入项目的“GroupId”、“ArtifactId”和Version:

Next,指定“Maven home directory”等配置:

Next,修改Project Name:

Finish,打开项目,添加一些必要的目录,最终项目框架目录图如下:

修改pom.xml文件,指定各依赖和插件的版本等信息:

<properties>    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>    <java.version>11</java.version>    <maven.compiler.source>11</maven.compiler.source>    <maven.compiler.target>11</maven.compiler.target>    <spring.version>5.1.6.RELEASE</spring.version>    <junit.version>4.12</junit.version>    <lombok.version>1.18.6</lombok.version>    <mybatis-plus.version>3.1.1</mybatis-plus.version>    <freemarker.version>2.3.28</freemarker.version>    <druid.version>1.1.16</druid.version>    <jsqlparser.version>2.0</jsqlparser.version>    <mysql-connector.version>8.0.16</mysql-connector.version>    <jstl-api.version>1.2</jstl-api.version>    <servlet-api.version>4.0.1</servlet-api.version>    <jsp-api.version>2.3.3</jsp-api.version>    <springfox-swagger.version>2.9.2</springfox-swagger.version>    <commons-lang3.version>3.9</commons-lang3.version>    <jackson.version>2.9.8</jackson.version>    <mapstruct.version>1.3.0.Final</mapstruct.version>    <log4j.version>2.11.2</log4j.version>    <slf4j.version>1.7.26</slf4j.version>    <clean.plugin.version>3.1.0</clean.plugin.version>    <resources.plugin.version>3.1.0</resources.plugin.version>    <compiler.plugin.version>3.8.0</compiler.plugin.version>    <surefire.plugin.version>3.0.0-M3</surefire.plugin.version>    <war.plugin.version>3.2.2</war.plugin.version>    <install.plugin.version>3.0.0-M1</install.plugin.version>    <deploy.plugin.version>3.0.0-M1</deploy.plugin.version></properties>

在<dependencyManagement>标签里面管理各依赖的版本号:

<dependencyManagement>    <dependencies>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-context</artifactId>            <version>${spring.version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-context-support</artifactId>            <version>${spring.version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-beans</artifactId>            <version>${spring.version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-jdbc</artifactId>            <version>${spring.version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-aop</artifactId>            <version>${spring.version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-aspects</artifactId>            <version>${spring.version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-webmvc</artifactId>            <version>${spring.version}</version>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-test</artifactId>            <version>${spring.version}</version>            <scope>test</scope>        </dependency>        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>            <version>${junit.version}</version>            <scope>test</scope>        </dependency>        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>            <version>${lombok.version}</version>            <scope>provided</scope>        </dependency>        <dependency>            <groupId>com.baomidou</groupId>            <artifactId>mybatis-plus</artifactId>            <version>${mybatis-plus.version}</version>        </dependency>        <dependency>            <groupId>com.baomidou</groupId>            <artifactId>mybatis-plus-generator</artifactId>            <version>${mybatis-plus.version}</version>            <scope>test</scope>            <optional>true</optional>        </dependency>        <dependency>            <groupId>org.freemarker</groupId>            <artifactId>freemarker</artifactId>            <version>${freemarker.version}</version>            <scope>test</scope>            <optional>true</optional>        </dependency>        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>druid</artifactId>            <version>${druid.version}</version>        </dependency>        <dependency>            <groupId>com.github.jsqlparser</groupId>            <artifactId>jsqlparser</artifactId>            <version>${jsqlparser.version}</version>        </dependency>        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <version>${mysql-connector.version}</version>        </dependency>        <dependency>            <groupId>javax.servlet.jsp.jstl</groupId>            <artifactId>jstl-api</artifactId>            <version>${jstl-api.version}</version>        </dependency>        <dependency>            <groupId>javax.servlet</groupId>            <artifactId>javax.servlet-api</artifactId>            <version>${servlet-api.version}</version>            <scope>provided</scope>        </dependency>        <dependency>            <groupId>javax.servlet.jsp</groupId>            <artifactId>javax.servlet.jsp-api</artifactId>            <version>${jsp-api.version}</version>            <scope>provided</scope>        </dependency>        <dependency>            <groupId>io.springfox</groupId>            <artifactId>springfox-swagger2</artifactId>            <version>${springfox-swagger.version}</version>        </dependency>        <dependency>            <groupId>io.springfox</groupId>            <artifactId>springfox-swagger-ui</artifactId>            <version>${springfox-swagger.version}</version>        </dependency>        <dependency>            <groupId>org.apache.commons</groupId>            <artifactId>commons-lang3</artifactId>            <version>${commons-lang3.version}</version>        </dependency>        <dependency>            <groupId>com.fasterxml.jackson.core</groupId>            <artifactId>jackson-databind</artifactId>            <version>${jackson.version}</version>        </dependency>        <dependency>            <groupId>com.fasterxml.jackson.core</groupId>            <artifactId>jackson-annotations</artifactId>            <version>${jackson.version}</version>            <scope>compile</scope>        </dependency>        <dependency>            <groupId>org.mapstruct</groupId>            <artifactId>mapstruct</artifactId>            <version>${mapstruct.version}</version>        </dependency>        <dependency>            <groupId>org.slf4j</groupId>            <artifactId>slf4j-api</artifactId>            <version>${slf4j.version}</version>        </dependency>        <dependency>            <groupId>org.apache.logging.log4j</groupId>            <artifactId>log4j-slf4j-impl</artifactId>            <version>${log4j.version}</version>        </dependency>    </dependencies></dependencyManagement>

添加项目依赖:

<dependencies>    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-context-support</artifactId>    </dependency>    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-jdbc</artifactId>    </dependency>    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-aspects</artifactId>    </dependency>    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-webmvc</artifactId>    </dependency>    <dependency>        <groupId>org.springframework</groupId>        <artifactId>spring-test</artifactId>    </dependency>    <dependency>        <groupId>junit</groupId>        <artifactId>junit</artifactId>    </dependency>    <dependency>        <groupId>org.projectlombok</groupId>        <artifactId>lombok</artifactId>    </dependency>    <dependency>        <groupId>com.baomidou</groupId>        <artifactId>mybatis-plus</artifactId>    </dependency>    <dependency>        <groupId>com.baomidou</groupId>        <artifactId>mybatis-plus-generator</artifactId>    </dependency>    <dependency>        <groupId>org.freemarker</groupId>        <artifactId>freemarker</artifactId>    </dependency>    <dependency>        <groupId>com.alibaba</groupId>        <artifactId>druid</artifactId>    </dependency>    <dependency>        <groupId>mysql</groupId>        <artifactId>mysql-connector-java</artifactId>    </dependency>    <dependency>        <groupId>javax.servlet.jsp.jstl</groupId>        <artifactId>jstl-api</artifactId>    </dependency>    <dependency>        <groupId>javax.servlet</groupId>        <artifactId>javax.servlet-api</artifactId>    </dependency>    <dependency>        <groupId>javax.servlet.jsp</groupId>        <artifactId>javax.servlet.jsp-api</artifactId>    </dependency>    <dependency>        <groupId>io.springfox</groupId>        <artifactId>springfox-swagger2</artifactId>    </dependency>    <dependency>        <groupId>io.springfox</groupId>        <artifactId>springfox-swagger-ui</artifactId>    </dependency>    <dependency>        <groupId>org.apache.commons</groupId>        <artifactId>commons-lang3</artifactId>    </dependency>    <dependency>        <groupId>com.fasterxml.jackson.core</groupId>        <artifactId>jackson-databind</artifactId>    </dependency>    <dependency>        <groupId>org.mapstruct</groupId>        <artifactId>mapstruct</artifactId>    </dependency>    <dependency>        <groupId>org.apache.logging.log4j</groupId>        <artifactId>log4j-slf4j-impl</artifactId>    </dependency></dependencies>

管理<build>:

<build>    <finalName>ssm</finalName>    <pluginManagement>        <plugins>            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-clean-plugin</artifactId>                <version>${clean.plugin.version}</version>            </plugin>            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-resources-plugin</artifactId>                <version>${resources.plugin.version}</version>                <configuration>                    <encoding>${project.build.sourceEncoding}</encoding>                </configuration>            </plugin>            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-compiler-plugin</artifactId>                <version>${compiler.plugin.version}</version>                <configuration>                    <source>${maven.compiler.source}</source>                    <target>${maven.compiler.target}</target>                    <encoding>${project.build.sourceEncoding}</encoding>                    <annotationProcessorPaths>                        <path>                            <groupId>org.projectlombok</groupId>                            <artifactId>lombok</artifactId>                            <version>${lombok.version}</version>                        </path>                        <path>                            <groupId>org.mapstruct</groupId>                            <artifactId>mapstruct-processor</artifactId>                            <version>${mapstruct.version}</version>                        </path>                    </annotationProcessorPaths>                    <compilerArgs>                        <compilerArg>-Amapstruct.defaultComponentModel=spring</compilerArg>                        <compilerArg>-Amapstruct.unmappedTargetPolicy=IGNORE</compilerArg>                    </compilerArgs>                </configuration>            </plugin>            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-surefire-plugin</artifactId>                <version>${surefire.plugin.version}</version>            </plugin>            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-war-plugin</artifactId>                <version>${war.plugin.version}</version>            </plugin>            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-install-plugin</artifactId>                <version>${install.plugin.version}</version>            </plugin>            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-deploy-plugin</artifactId>                <version>${deploy.plugin.version}</version>            </plugin>        </plugins>    </pluginManagement></build>

依赖配置好之后,开始整合。

先整合Log4j2日志,在项目classpath目录(src/main/resources)下创建log4j2.xml文件,添加Log4j2日志配置:

<?xml version="1.0" encoding="UTF-8"?><configuration status="WARN" monitorInterval="30">    <appenders>        <console name="STDOUT" target="SYSTEM_OUT">            <PatternLayout disableAnsi="false" pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight{%5level} %magenta{%pid{-1}} --- [%-15.15t] %cyan{%c{1.}.%M(%F:%L)} : %m%n"/>        </console>    </appenders>    <loggers>        <root level="DEBUG">            <appenderRef ref="STDOUT"/>        </root>    </loggers></configuration>

再整合 Spring 和 Mybatis,本次还整合了 Mybatis Plus 开源框架,新建 src/main/resources/mybatis/mybatis-config.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"/>        <!--哪些方法触发关系查询-->        <setting name="lazyLoadTriggerMethods" value="clone"/>    </settings>    <!--配置别名-->    <typeAliases>        <!--<package name=""/>-->    </typeAliases></configuration>

新建 src/main/resources/properties/jdbc.properties 文件,配置数据源连接信息:

jdbc.driverClassName=com.mysql.cj.jdbc.Driverjdbc.url=jdbc:mysql://127.0.0.1:3306/ssm?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8jdbc.username=rootjdbc.password=root

新建 src/main/resources/spring/applicationContext-dao.xml 文件,配置 Druid 数据源,SqlSessionFactory 会话工厂,Mybatis 的 Mapper 接口扫描等信息:

<?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:aop="http://www.springframework.org/schema/aop"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">    <context:property-placeholder location="classpath:properties/jdbc.properties"/>    <!-- Druid和Spring关联监控配置 -->    <bean id="druidStatInterceptor" class="com.alibaba.druid.support.spring.stat.DruidStatInterceptor"/>    <bean id="druidStatPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut" scope="prototype">        <property name="patterns">            <list>                <value>com.example.ssm.service.*</value>                <value>com.example.ssm.mapper.*</value>            </list>        </property>    </bean>    <aop:config expose-proxy="true" proxy-target-class="true">        <aop:advisor advice-ref="druidStatInterceptor" pointcut-ref="druidStatPointcut"/>    </aop:config>    <!-- Druid 配置 StatFilter:用于统计监控信息 -->    <bean id="statFilter" class="com.alibaba.druid.filter.stat.StatFilter">        <property name="mergeSql" value="true"/>        <property name="slowSqlMillis" value="10000"/>        <property name="logSlowSql" value="true"/>    </bean>    <!-- Druid 配置 WallFilter:防御SQL注入攻击 -->    <bean id="wallFilter" class="com.alibaba.druid.wall.WallFilter">        <property name="dbType" value="mysql"/>    </bean>    <!-- Druid 配置 Slf4jLogFilter:用于SQL日志打印 -->    <bean id="slf4jLogFilter" class="com.alibaba.druid.filter.logging.Slf4jLogFilter">        <property name="dataSourceLogEnabled" value="true"/>        <property name="statementExecutableSqlLogEnable" value="true"/>        <property name="resultSetLogEnabled" value="false"/>    </bean>    <!-- 配置数据源 -->    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">        <property name="name" value="dataSource"/>        <property name="driverClassName" value="${jdbc.driverClassName}"/>        <property name="url" value="${jdbc.url}"/>        <property name="username" value="${jdbc.username}"/>        <property name="password" value="${jdbc.password}"/>        <property name="initialSize" value="5"/>        <property name="minIdle" value="5"/>        <property name="maxActive" value="20"/>        <property name="maxWait" value="60000"/>        <property name="timeBetweenEvictionRunsMillis" value="60000"/>        <property name="minEvictableIdleTimeMillis" value="300000"/>        <property name="testOnBorrow" value="false"/>        <property name="testOnReturn" value="false"/>        <property name="testWhileIdle" value="true"/>        <property name="removeAbandoned" value="false"/>        <property name="removeAbandonedTimeout" value="120"/>        <property name="logAbandoned" value="true"/>        <property name="validationQuery" value="SELECT 1"/>        <property name="poolPreparedStatements" value="true"/>        <property name="maxOpenPreparedStatements" value="20"/>        <property name="asyncInit" value="true"/>        <property name="proxyFilters">            <list>                <ref bean="statFilter"/>                <ref bean="wallFilter"/>                <ref bean="slf4jLogFilter"/>            </list>        </property>        <property name="useGlobalDataSourceStat" value="true"/>    </bean>    <!-- 配置会话工厂 -->    <bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">        <property name="dataSource" ref="dataSource"/>        <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>        <property name="typeAliasesPackage" value="com.example.ssm.entity"/>        <property name="mapperLocations" value="classpath:mapper/*Mapper.xml"/>        <property name="plugins">            <array>                <!-- 分页插件配置 -->                <bean id="paginationInterceptor"                      class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"/>                <!-- 乐观锁插件 -->                <bean id="optimisticLockerInterceptor"                      class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>            </array>        </property>    </bean>    <!-- 配置扫描 Mapper 接口 -->    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">        <property name="basePackage" value="com.example.ssm.mapper"/>        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>    </bean></beans>

新建 src/main/resources/spring/applicationContext-tx.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: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.xsd        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">    <context:component-scan base-package="com.example.ssm.service"/>    <!-- 事务管理器 -->    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="dataSource"/>    </bean>    <tx:annotation-driven/></beans>

配置 Mybatis Plus 自动生成 Controller、Service 和 Mapper 文件:

// 自动代码生成器AutoGenerator autoGenerator = new AutoGenerator();// 全局配置GlobalConfig globalConfig = new GlobalConfig();String projectPath = System.getProperty("user.dir");globalConfig.setOutputDir(projectPath + "/src/main/java");globalConfig.setAuthor("calvinit");globalConfig.setOpen(false);globalConfig.setFileOverride(true);// Druid 1.1.16 版本貌似不支持 java8 新的时间类型,如 java.time.LocalDateTimeglobalConfig.setDateType(DateType.ONLY_DATE);autoGenerator.setGlobalConfig(globalConfig);// 数据源配置DataSourceConfig dataSourceConfig = new DataSourceConfig();dataSourceConfig.setDbType(DbType.MYSQL);dataSourceConfig.setUrl("jdbc:mysql://172.0.0.1:3306/ssm?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8");// dataSourceConfig.setSchemaName("public");dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");dataSourceConfig.setUsername("root");dataSourceConfig.setPassword("root");autoGenerator.setDataSource(dataSourceConfig);// 包配置PackageConfig packageConfig = new PackageConfig();packageConfig.setParent("com.example.ssm");autoGenerator.setPackageInfo(packageConfig);// 自定义配置InjectionConfig injectionConfig = new InjectionConfig() {    @Override    public void initMap() {        // to do nothing    }};// 如果模板引擎是 freemarkerString templatePath = "/templates/mapper.xml.ftl";// 自定义输出配置List<FileOutConfig> focList = new ArrayList<>();// 自定义配置会被优先输出focList.add(new FileOutConfig(templatePath) {    @Override    public String outputFile(TableInfo tableInfo) {        tableInfo.setImportPackages("com.baomidou.mybatisplus.annotation.TableName");        tableInfo.setConvert(true);        // 自定义输出文件名        return projectPath + "/src/main/resources/mapper/"                + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;    }});injectionConfig.setFileOutConfigList(focList);autoGenerator.setCfg(injectionConfig);// 配置模板TemplateConfig templateConfig = new TemplateConfig();// 配置自定义输出模板// templateConfig.setEntity(ConstVal.TEMPLATE_ENTITY_JAVA);// templateConfig.setService(ConstVal.TEMPLATE_SERVICE);// templateConfig.setController(ConstVal.TEMPLATE_CONTROLLER);templateConfig.setXml(null);autoGenerator.setTemplate(templateConfig);// 策略配置StrategyConfig strategy = new StrategyConfig();strategy.setNaming(NamingStrategy.underline_to_camel);strategy.setColumnNaming(NamingStrategy.underline_to_camel);// strategy.setSuperEntityClass("com.example.ssm.entity.BaseEntity");strategy.setEntityLombokModel(true);strategy.setEntityColumnConstant(true);strategy.setRestControllerStyle(true);// strategy.setSuperControllerClass("com.example.ssm.controller.BaseController");strategy.setInclude("t_user", "t_group", "t_user_group_relation");// strategy.setSuperEntityColumns("id");strategy.setControllerMappingHyphenStyle(true);strategy.setTablePrefix(packageConfig.getModuleName() + "_");strategy.setEntityTableFieldAnnotationEnable(true);autoGenerator.setStrategy(strategy);autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine());autoGenerator.execute();

然后整合 Spring 和 Spring MVC,其中对日期类型返回json作了自定义格式化处理:

import com.fasterxml.jackson.core.JsonGenerator;import com.fasterxml.jackson.databind.JsonSerializer;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.SerializerProvider;import com.fasterxml.jackson.databind.module.SimpleModule;import java.io.IOException;import java.text.SimpleDateFormat;import java.time.LocalDate;import java.time.LocalDateTime;import java.time.LocalTime;import java.time.format.DateTimeFormatter;/** * Jackson ObjectMapper 工厂类 */public class ObjectMapperFactory {    public static ObjectMapper getMapper() {        ObjectMapper objectMapper = new ObjectMapper();        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));        SimpleModule module = new SimpleModule();        module.addSerializer(LocalDate.class, new LocalDateSerializer());        module.addSerializer(LocalTime.class, new LocalTimeSerializer());        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());        objectMapper.registerModule(module);        return objectMapper;    }    static class LocalDateSerializer extends JsonSerializer<LocalDate> {        private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");        /**         * {@inheritDoc}         */        @Override        public void serialize(LocalDate value, JsonGenerator jgen, SerializerProvider provider) throws IOException {            jgen.writeString(dateFormatter.format(value));        }    }    static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {        private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");        /**         * {@inheritDoc}         */        @Override        public void serialize(LocalDateTime value, JsonGenerator jgen, SerializerProvider provider) throws IOException {            jgen.writeString(dateTimeFormatter.format(value));        }    }    static class LocalTimeSerializer extends JsonSerializer<LocalTime> {        private static final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");        /**         * {@inheritDoc}         */        @Override        public void serialize(LocalTime value, JsonGenerator jgen, SerializerProvider provider) throws IOException {            jgen.writeString(timeFormatter.format(value));        }    }}

还集成了 Swgger UI ,方便接口调试:

import org.springframework.context.annotation.Bean;import springfox.documentation.builders.ApiInfoBuilder;import springfox.documentation.builders.PathSelectors;import springfox.documentation.builders.RequestHandlerSelectors;import springfox.documentation.service.ApiInfo;import springfox.documentation.service.Contact;import springfox.documentation.spi.DocumentationType;import springfox.documentation.spring.web.plugins.Docket;import springfox.documentation.swagger2.annotations.EnableSwagger2;@EnableSwagger2public class Swagger2Configuration {    @Bean("docket")    public Docket createDocket() {        return new Docket(DocumentationType.SWAGGER_2)                .select()                .apis(RequestHandlerSelectors.basePackage("com.example.ssm.controller"))                .paths(PathSelectors.any())                .build()                .apiInfo(apiInfo());    }    private ApiInfo apiInfo() {        Contact contact = new Contact("calvinit", "https://gitee.com/calvinit/ssm-demo", "");        return new ApiInfoBuilder()                .title("SSM框架搭建Demo")                .description("SSM框架搭建Demo,仅供猿友们学习交流之用,请勿用于商业用途")                .contact(contact)                .version("1.0-SNAPSHOT")                .build();    }}

新建 src/main/resources/spring/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:mvc="http://www.springframework.org/schema/mvc"       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.xsd        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">    <context:component-scan base-package="com.example.ssm.controller"/>    <mvc:default-servlet-handler/>    <!-- Jackson ObjectMapper -->    <bean id="objectMapper" class="com.example.ssm.json.ObjectMapperFactory" factory-method="getMapper"/>    <mvc:annotation-driven>        <mvc:message-converters>            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">                <property name="objectMapper" ref="objectMapper"/>                <property name="supportedMediaTypes">                    <list>                        <value>application/json;charset=UTF-8</value>                    </list>                </property>            </bean>            <bean class="org.springframework.http.converter.StringHttpMessageConverter">                <constructor-arg value="UTF-8"/>            </bean>        </mvc:message-converters>    </mvc:annotation-driven>    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">        <property name="prefix" value="/WEB-INF/view/"/>        <property name="suffix" value=".jsp"/>    </bean>    <!-- Swagger2 配置 -->    <bean class="com.example.ssm.configure.Swagger2Configuration"/>    <mvc:resources mapping="swagger-ui.html" location="classpath:/META-INF/resources/"/>    <mvc:resources mapping="/webjars/**" location="classpath:/META-INF/resources/webjars/"/></beans>

同时整合了MapStruct,方便 Entity、DTO 和 VO 等之间的转换,使用示例:

import com.example.ssm.entity.User;import com.example.ssm.vo.UserVo;import org.mapstruct.InheritInverseConfiguration;import org.mapstruct.Mapper;import org.mapstruct.Mapping;import org.mapstruct.Mappings;import java.util.List;@Mapperpublic interface UserVoConverter {    @Mappings({            @Mapping(source = "id", target = "userId"),            @Mapping(source = "name", target = "userName"),            @Mapping(target = "createDt", dateFormat = "yyyy-MM-dd HH:mm:ss"),            @Mapping(target = "lastUpdateDt", dateFormat = "yyyy-MM-dd HH:mm:ss")    })    UserVo entityToVo(User user);    List<UserVo> batchEntityToVo(List<User> userList);    @InheritInverseConfiguration    User voToEntity(UserVo userVo);    List<User> batchVoToEntity(List<UserVo> userVoList);}

新建 src/main/resources/spring/applicationContext-common.xml 文件,配置 MapStruct 的 bean 被 Spring 管理:

<?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"       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.xsd">    <context:component-scan base-package="com.example.ssm.converter"/></beans>

修改 web.xml 文件:

<?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_4_0.xsd"         id="WebApp_ID" version="4.0">    <display-name>ssm</display-name>    <!-- 初始化spring容器 -->    <context-param>        <param-name>contextConfigLocation</param-name>        <param-value>classpath:spring/applicationContext-*.xml</param-value>    </context-param>    <!-- 配置log4j2 -->    <context-param>        <param-name>log4jConfigLocation</param-name>        <param-value>classpath:log4j2.xml</param-value>    </context-param>    <!-- 解决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>    <!-- Druid 配置 WebStatFilter:用于采集web-jdbc关联监控的数据 -->    <filter>        <filter-name>druidWebStatFilter</filter-name>        <filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>        <init-param>            <param-name>exclusions</param-name>            <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>        </init-param>        <init-param>            <param-name>profileEnable</param-name>            <param-value>true</param-value>        </init-param>    </filter>    <filter-mapping>        <filter-name>druidWebStatFilter</filter-name>        <url-pattern>/*</url-pattern>    </filter-mapping>    <listener>        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>    </listener>    <!-- springMVC 前端控制器 -->    <servlet>        <servlet-name>dispatcherServlet</servlet-name>        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>        <init-param>            <param-name>contextConfigLocation</param-name>            <param-value>classpath:spring/spring-mvc.xml</param-value>        </init-param>        <load-on-startup>1</load-on-startup>    </servlet>    <servlet-mapping>        <servlet-name>dispatcherServlet</servlet-name>        <url-pattern>/</url-pattern>    </servlet-mapping>    <!-- Druid 配置 StatViewServlet:用于展示Druid的统计信息 -->    <servlet>        <servlet-name>druidStatView</servlet-name>        <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>        <init-param>            <!-- 访问白名单 -->            <param-name>allow</param-name>            <param-value>127.0.0.1</param-value>        </init-param>        <init-param>            <!-- 访问用户名 -->            <param-name>loginUsername</param-name>            <param-value>user</param-value>        </init-param>        <init-param>            <!-- 访问密码 -->            <param-name>loginPassword</param-name>            <param-value>password</param-value>        </init-param>        <init-param>            <!-- 是否允许清空统计数据 -->            <param-name>resetEnable</param-name>            <param-value>true</param-value>        </init-param>    </servlet>    <servlet-mapping>        <servlet-name>druidStatView</servlet-name>        <url-pattern>/druid/*</url-pattern>    </servlet-mapping>    <welcome-file-list>        <welcome-file>index.jsp</welcome-file>    </welcome-file-list></web-app>

修改 index.jsp 文件:

<%@ page contentType="text/html;charset=UTF-8" %><html><head>    <title>ssm</title></head><body><h2>Hello World!</h2><a href="${pageContext.request.contextPath}/swagger-ui.html">打开 Swagger UI</a></body></html>

至此,框架基本整合完毕,下面写一个测试 Contoller 测试 Spring 和 Spring MVC 整合结果:

package com.example.ssm.controller;import com.google.common.collect.Maps;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import springfox.documentation.annotations.ApiIgnore;import java.util.Map;@Api(tags = "首页控制器")@Controller@RequestMappingpublic class IndexController {    @ApiIgnore    @ApiOperation(value = "首页")    @GetMapping    public String index() {        return "index";    }    @ApiOperation(value = "返回纯字符串")    @GetMapping("/hello")    @ResponseBody    public String hello() {        return "Hello World!";    }    @ApiOperation(value = "测试日期 json 返回")    @GetMapping("/date/test")    @ResponseBody    public Map<String, Object> testDate() {        Map<String, Object> map = Maps.newHashMap();        map.put("java.util.Date", new java.util.Date());        map.put("java.sql.Date", new java.sql.Date(System.currentTimeMillis()));        map.put("java.time.LocalDate", java.time.LocalDate.now());        map.put("java.time.LocalTime", java.time.LocalTime.now());        map.put("java.time.LocalDateTime", java.time.LocalDateTime.now());        return map;    }}

右上角“Add Configuration”添加 Tomcat 配置,将项目部署信息配置好:

然后我们启动 Tomcat,打开 http://localhost:8080/ssm,如果正常,应该显示如下界面:

点击界面上的超链接,进入 Swagger UI 的界面。

再测试一下 Spring 和 Mybatis 整合是否完成:

package com.example.ssm.controller;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.baomidou.mybatisplus.core.metadata.IPage;import com.baomidou.mybatisplus.core.toolkit.Wrappers;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;import com.example.ssm.converter.UserVoConverter;import com.example.ssm.entity.User;import com.example.ssm.service.IUserService;import com.example.ssm.vo.PageVo;import com.example.ssm.vo.UserVo;import io.swagger.annotations.Api;import io.swagger.annotations.ApiOperation;import io.swagger.annotations.ApiParam;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.lang.NonNull;import org.springframework.web.bind.annotation.*;import java.util.List;@Api@RestController@RequestMapping("/users")public class UserController {    private IUserService userService;    private UserVoConverter userVoConverter;    @Autowired    public UserController(@NonNull IUserService userService, @NonNull UserVoConverter userVoConverter) {        this.userService = userService;        this.userVoConverter = userVoConverter;    }    @ApiOperation(value = "分页查询用户列表")    @GetMapping("/page")    @ResponseBody    public PageVo<UserVo> page(            @ApiParam(value = "当前页", required = true, defaultValue = "1", example = "1")            @RequestParam("pageNum") int pageNum,            @ApiParam(value = "页面大小", required = true, defaultValue = "10", example = "10")            @RequestParam(value = "pageSize", defaultValue = "10") int pageSize) {        Page<User> page = new Page<>();        page.setSize(pageSize);        page.setCurrent(pageNum);        page.setAsc(User.CREATE_DT, User.LAST_UPDATE_DT);        IPage<User> userPage = userService.selectPage(page);        List<User> userList = userPage.getRecords();        List<UserVo> userVoList = userVoConverter.batchEntityToVo(userList);        return new PageVo<UserVo>()                .setPageNum(userPage.getCurrent())                .setPageSize(userPage.getSize())                .setPageTotal(userPage.getPages())                .setRowTotal(userPage.getTotal())                .setRowList(userVoList)                .get();    }    @ApiOperation(value = "查询某个用户")    @GetMapping("/{id}")    @ResponseBody    public User page(            @ApiParam(value = "用户Id", required = true, example = "1")            @PathVariable(value = "id") int id) {        return userService.selectByPrimaryKey(id);    }    @ApiOperation(value = "获取所有00后用户列表")    @GetMapping("/00/list")    @ResponseBody    public List<User> list00() {        LambdaQueryWrapper<User> userLambdaQueryWrapper = Wrappers.lambdaQuery();        userLambdaQueryWrapper.ge(User::getBirthday, "2000-01-01");        return userService.list(userLambdaQueryWrapper);    }}

在 Swagger UI 的界面里面测试接口,返回正确:

另外,Druid 的监控信息页面链接为:http://localhost:8080/ssm/druid/index.html,访问白名单、账号和密码在 web.xml 文件中配置。

至此,SSM 框架集成完毕,测试通过!

因篇幅关系,一些代码并没有在此文章中展现,可到我的Gitee上看完整框架代码。