共计 31909 个字符,预计需要花费 80 分钟才能阅读完成。
最近在回顾和总结一些技术,想到了把之前比较火的 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.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://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}
};
// 如果模板引擎是 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 配置,将项目部署信息配置好:
然后我们启动 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 上看完整框架代码。