從零開始搭建SSM框架(Spring + Spring MVC + Mybatis)

最近在回顧和總結一些技術,想到了把以前比較火的 SSM 框架從新搭建出來,做爲一個小結,同時也但願本文章寫出來能對你們有一些幫助和啓發,因本人水平有限,不免可能會有一些不對之處,歡迎各位大神拍磚指教,共同進步。css

本文章示例使用 IntelliJ IDEA 來開發,JDK 使用 11 版本,其他各框架和技術基本上使用了文章撰寫當時的最新版本。html

好的,下面直接進入正題。前端

打開 IntelliJ IDEA,File > New > Project > Maven,選中「Create from archetype」,而後再選中「org.apache.maven.archetypes:maven-archetype-webapp」:java

clipboard.png

Next,輸入項目的「GroupId」、「ArtifactId」和Version:mysql

clipboard.png

Next,指定「Maven home directory」等配置:git

clipboard.png

Next,修改Project Name:github

clipboard.png

Finish,打開項目,添加一些必要的目錄,最終項目框架目錄圖以下:web

clipboard.png

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

<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>標籤裏面管理各依賴的版本號:sql

<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.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/ssm?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=GMT%2B8
jdbc.username=root
jdbc.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.LocalDateTime
globalConfig.setDateType(DateType.ONLY_DATE);

autoGenerator.setGlobalConfig(globalConfig);

// 數據源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.MYSQL);
dataSourceConfig.setUrl("jdbc:mysql://127.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
    }
};

// 若是模板引擎是 freemarker
String 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;

@EnableSwagger2
public 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;

@Mapper
public 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
@RequestMapping
public 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 配置,將項目部署信息配置好:

clipboard.png

clipboard.png

而後咱們啓動 Tomcat,打開 http://localhost:8080/ssm,若是正常,應該顯示以下界面:

clipboard.png

點擊界面上的超連接,進入 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 的界面裏面測試接口,返回正確:

clipboard.png

clipboard.png

另外,Druid 的監控信息頁面連接爲:http://localhost:8080/ssm/druid/index.html,訪問白名單、帳號和密碼在 web.xml 文件中配置。

至此,SSM 框架集成完畢,測試經過!

因篇幅關係,一些代碼並無在此文章中展示,可到個人Gitee上看完整框架代碼。

相關文章
相關標籤/搜索