一、搭建目的
在此之前,由于没有接触过SpringBoot,项目也一直用 Spring + SpringMVC 来进行开发,在上一个文章《 SpringMVC + Freemarker + JPA + MyBatis 环境搭建》 完成后,利用几天时间初步了解和学习SpringBoot框架,整体感觉非常棒,环境搭建相对常规 Spring + SpringMVC 来说简化了一些步骤,很多显示性声明SpringBoot都已经包含进去了,常用的第三方组件集成也很方便,因此手动搭建一个示例并将过程步骤记录下来,方便后期参考,本文仅供学习参考,非喜勿喷。
二、环境及版本要求
序号 | 环境名称 | 版本号 |
---|---|---|
1 | 操作系统 | deppin 社区版(20.9) |
2 | IntelliJ IDEA | 2023.2 (Ultimate Edition) |
3 | JDK | 17.0.14 |
4 | Maven | 3.9.9 |
5 | SpringBoot | 3.4.5 |
6 | MyBatis-SpringBoot | 3.0.4 |
三、搭建环境
1、创建项目
1.1、创建模块
本示例采用分层、分包、分模块进行搭建,具体如下图(图3-1)所示

1.2、模块说明
序号 | 模块 | 备注 |
---|---|---|
1 | model | 表对应实体,需显示标记@Entity |
2 | dao | 数据持久层,分jpa 和 mybatis |
3 | service | 业务层接口 |
4 | service-impl | 业务层实现 |
5 | web-admin | 控制层及应用启动入口 |
2、配置 pom
2.1、配置根 pom
根 pom 配置如下图(图3-2)所示

根 pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project-groupId>org.example</project-groupId>
<project-artifactId>spring-boot-example</project-artifactId>
<project-version>1.0-SNAPSHOT</project-version>
<mybatis-spring-boot-version>3.0.4</mybatis-spring-boot-version>
<spring-boot-version>3.4.5</spring-boot-version>
</properties>
<groupId>${project-groupId}</groupId>
<artifactId>${project-artifactId}</artifactId>
<version>${project-version}</version>
<packaging>pom</packaging>
<modules>
<module>${project-artifactId}-model</module>
<module>${project-artifactId}-dao</module>
<module>${project-artifactId}-service</module>
<module>${project-artifactId}-service-impl</module>
<module>${project-artifactId}-web-admin</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2.1、配置 model 模块 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>${project-groupId}</groupId>
<artifactId>${project-artifactId}</artifactId>
<version>${project-version}</version>
</parent>
<artifactId>${project-artifactId}-model</artifactId>
<dependencies>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
</dependency>
</dependencies>
</project>
2.1、配置 dao 模块 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>${project-groupId}</groupId>
<artifactId>${project-artifactId}</artifactId>
<version>${project-version}</version>
</parent>
<artifactId>${project-artifactId}-dao</artifactId>
<dependencies>
<dependency>
<groupId>${project-groupId}</groupId>
<artifactId>${project-artifactId}-model</artifactId>
<version>${project-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot-version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.11</version>
</dependency>
</dependencies>
</project>
2.1、配置 service 模块 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>${project-groupId}</groupId>
<artifactId>${project-artifactId}</artifactId>
<version>${project-version}</version>
</parent>
<artifactId>${project-artifactId}-service</artifactId>
<dependencies>
<dependency>
<groupId>${project-groupId}</groupId>
<artifactId>${project-artifactId}-model</artifactId>
<version>${project-version}</version>
</dependency>
</dependencies>
</project>
2.1、配置 service-impl 模块 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>${project-groupId}</groupId>
<artifactId>${project-artifactId}</artifactId>
<version>${project-version}</version>
</parent>
<artifactId>${project-artifactId}-service-impl</artifactId>
<dependencies>
<dependency>
<groupId>${project-groupId}</groupId>
<artifactId>${project-artifactId}-model</artifactId>
<version>${project-version}</version>
</dependency>
<dependency>
<groupId>${project-groupId}</groupId>
<artifactId>${project-artifactId}-dao</artifactId>
<version>${project-version}</version>
</dependency>
<dependency>
<groupId>${project-groupId}</groupId>
<artifactId>${project-artifactId}-service</artifactId>
<version>${project-version}</version>
</dependency>
</dependencies>
</project>
2.1、配置 web-admin 模块 pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>${project-groupId}</groupId>
<artifactId>${project-artifactId}</artifactId>
<version>${project-version}</version>
</parent>
<artifactId>${project-artifactId}-web-admin</artifactId>
<!-- <packaging>war/jar</packaging>-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.57</version>
</dependency>
<dependency>
<groupId>${project-groupId}</groupId>
<artifactId>${project-artifactId}-model</artifactId>
<version>${project-version}</version>
</dependency>
<dependency>
<groupId>${project-groupId}</groupId>
<artifactId>${project-artifactId}-dao</artifactId>
<version>${project-version}</version>
</dependency>
<dependency>
<groupId>${project-groupId}</groupId>
<artifactId>${project-artifactId}-service-impl</artifactId>
<version>${project-version}</version>
</dependency>
<dependency>
<groupId>${project-groupId}</groupId>
<artifactId>${project-artifactId}-service</artifactId>
<version>${project-version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 关键:仅在此模块配置Spring Boot Maven插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot-version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal> <!-- 生成可执行JAR -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
3、 启动模块配置
3.1、资源目录说明
src/main/resourses 资源目录说明如下图(图3-3)所示:

3.2、application.yml配置
server:
port: 8080
spring:
application:
name: adminApplication
profiles:
#对应参数(dev:开发环境、local:本地环境、prod:生产环境、test:测试环境)
#设置对应环境的参数时可以加载对应配置文件
active: dev
---
#静态资源映射 start
#优先级从高到底:/META-INF/resources、/resources、/static、/public 这些全部映射到 /**
spring:
web:
resources:
static-locations:
classpath:/META-INF/resources,
classpath:/resources,
classpath:/static,
classpath:/public,
classpath:/public/welcome
#静态资源映射 end
---
# freemarker start
spring:
freemarker:
# template-loader-path: classpath:/templates/
#指定Freemarker模板文件的后缀名
suffix: .ftl
settings:
# #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试
template_update_delay: 0
# #incompatible_improvements: 2.3.34
# #模板异常处理器
# template_exception_handler: rethrow
# #是否强制使用绝对路径
# classic_compatible: false
# #去掉多余的空格,非常有用(默认为true)
# whitespace_stripping: true
# #控制是否显示错误提示
# show_error_tips: true
# #中国
# locale: zh_CN
# #编码utf8
# default_encoding: UTF-8
# #输出文件编码utf8
# output_encoding: UTF-8
# #url编码utf8
# url_escaping_charset: UTF-8
# #显示日期格式
# date_format: yyyy-MM-dd
# #显示时间格式
# time_format: HH:mm:ss
# #显示日期格式
# datetime_format: yyyy-MM-dd HH:mm:ss
# #数字显示格式
# number_format: "#.####"
# freemarker end
---
# datasource start
spring:
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:54323/test
username: postgres
password: postgres
hikari:
minimum-idle: 5
maximum-pool-size: 20
auto-commit: true
# datasource end
---
# jpa start
spring:
jpa:
show-sql: true
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
use_sql_comments: true
dialect: org.hibernate.dialect.PostgreSQLDialect
# jpa end
---
# mybatis start
mybatis:
#持久层接口对应sql文件目录
mapper-locations: classpath*:mapper/*.xml
#表对应实体类包路径
type-aliases-package: org.example.model
#下划线转托峰命名
configuration:
map-underscore-to-camel-case: true
# mybatis end
---
#logging start
logging:
file:
path: /home/usr/local/temp/logs
logback:
rollingpolicy:
max-size: 1MB
max-history: 30
total-size-cap: 1GB
#logging end
3.3、日志配置
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<property name="LOG_PATTERN_CONSOLE"
value="${LOG_PATTERN_CONSOLE:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<property name="LOG_PATTERN_FILE"
value="${LOG_PATTERN_FILE:-%d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!-- 配置日志属性,值来源于application.yml/application.properties,需设置默认值, -->
<springProperty scope="context" name="APPLICATION_NAME" source="spring.application.name" defaultValue="WebApplication"/>
<springProperty scope="context" name="LOG_PATH" source="logging.file.path" defaultValue="/home/usr/local/temp/logs"/>
<springProperty scope="context" name="LOG_FILE_MAX_SIZE" source="logging.logback.rollingpolicy.max-size" defaultValue="1MB"/>
<springProperty scope="context" name="LOG_FILE_MAX_HISTORY" source="logging.logback.rollingpolicy.max-history" defaultValue="3"/>
<springProperty scope="context" name="LOG_FILE_TOTAL_SIZE" source="logging.logback.rollingpolicy.total-size-cap" defaultValue="24MB"/>
<contextName>${APPLICATION_NAME}</contextName>
<!-- 控制台输出配置 start-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender" >
<encoder>
<pattern>${LOG_PATTERN_CONSOLE}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 控制台输出配置 end-->
<!-- 文件输出配置-INFO start-->
<appender name="FILE_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${APPLICATION_NAME}-${LOG_FILE_MAX_SIZE}-${LOG_FILE_MAX_HISTORY}-${LOG_FILE_TOTAL_SIZE}-info.log</file>
<encoder>
<pattern>${LOG_PATTERN_FILE}</pattern>
<append>true</append>
</encoder>
<!-- 主日志文件(按时间+大小滚动) -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/info/${APPLICATION_NAME}-info-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>${LOG_FILE_MAX_SIZE}</maxFileSize>
<maxHistory>${LOG_FILE_MAX_HISTORY}</maxHistory>
<totalSizeCap>${LOG_FILE_TOTAL_SIZE}</totalSizeCap>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter" charset="UTF-8">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 文件输出配置-INFO end-->
<!-- 文件输出配置-DEBUG start-->
<appender name="FILE_DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${APPLICATION_NAME}-debug.log</file>
<encoder>
<pattern>${LOG_PATTERN_FILE}</pattern>
<append>true</append>
</encoder>
<!-- 主日志文件(按时间+大小滚动) -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/debug/${APPLICATION_NAME}-debug-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>${LOG_FILE_MAX_SIZE}</maxFileSize>
<maxHistory>${LOG_FILE_MAX_HISTORY}</maxHistory>
<totalSizeCap>${LOG_FILE_TOTAL_SIZE}</totalSizeCap>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter" charset="UTF-8">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 文件输出配置-DEBUG end-->
<!-- 文件输出配置-WARN start-->
<appender name="FILE_WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${APPLICATION_NAME}-warn.log</file>
<encoder>
<pattern>${LOG_PATTERN_FILE}</pattern>
<append>true</append>
</encoder>
<!-- 主日志文件(按时间+大小滚动) -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/warn/${APPLICATION_NAME}-warn-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>${LOG_FILE_MAX_SIZE}</maxFileSize>
<maxHistory>${LOG_FILE_MAX_HISTORY}</maxHistory>
<totalSizeCap>${LOG_FILE_TOTAL_SIZE}</totalSizeCap>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter" charset="UTF-8">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 文件输出配置-WARN end-->
<!-- 文件输出配置-ERROR start-->
<appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${APPLICATION_NAME}-error.log</file>
<encoder>
<pattern>${LOG_PATTERN_FILE}</pattern>
<append>true</append>
</encoder>
<!-- 主日志文件(按时间+大小滚动) -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/error/${APPLICATION_NAME}-error-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>${LOG_FILE_MAX_SIZE}</maxFileSize>
<maxHistory>${LOG_FILE_MAX_HISTORY}</maxHistory>
<totalSizeCap>${LOG_FILE_TOTAL_SIZE}</totalSizeCap>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter" charset="UTF-8">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 文件输出配置-ERROR end-->
<!-- 开发/测试/本地环境时 对应日志配置 start-->
<springProfile name="dev|test|local">
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE_INFO"/>
<appender-ref ref="FILE_DEBUG"/>
<appender-ref ref="FILE_WARN"/>
<appender-ref ref="FILE_ERROR"/>
</root>
<logger additivity="false" name="org.hibernate.SQL" level="DEBUG"><appender-ref ref="CONSOLE"/></logger>
<logger additivity="false" name="org.hibernate.orm.jdbc.bind" level="DEBUG"><appender-ref ref="CONSOLE"/></logger>
<logger additivity="false" name="org.example.dao.mapper" level="DEBUG"><appender-ref ref="CONSOLE"/></logger>
</springProfile>
<!-- 开发/测试/本地环境时 对应日志配置 end-->
<!-- 生产环境时 对应日志配置 start-->
<springProfile name="prod">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE_INFO"/>
<appender-ref ref="FILE_WARN"/>
<appender-ref ref="FILE_ERROR"/>
</root>
</springProfile>
<!-- 生产环境时 对应日志配置 end-->
</configuration>
4、构建 web-admin 模块
4.1、web模块目录
包结构说明如下图(图3-4)所示:

4.2、应用启动类
在项目根目录(本示例为org.example)下创建启动类(名字符合Java命名规范即可,本示例为AdminWebApplication),
package org.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
//@EntityScan("org.example.model") //JPA实体包目录,无需显示声明,会自动从当前目录往下寻找
//@EnableJpaRepositories("org.example.dao.repository") //JPA接口目录,无需显示声明,会自动从当前目录往下寻找
//@EnableTransactionManagement //启用事务管理,已经自动开启,无需显示声明
@SpringBootApplication
public class AdminWebApplication {
public static void main(String[] args) {
SpringApplication.run(AdminWebApplication.class, args);
}
}
4.3、过滤器
package org.example.web.filter;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
@WebFilter(urlPatterns = "/")
public class AdminWebFilter implements Filter {
private static Logger logger = LoggerFactory.getLogger(AdminWebFilter.class);
@Override
public void init(FilterConfig filterConfig) throws ServletException {
logger.info("{}->init()", this.getClass().getName());
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
logger.info("{}->doFilter()", this.getClass().getName());
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
logger.info("{}->destroy()", this.getClass().getName());
Filter.super.destroy();
}
}
4.4、拦截器
拦截器需要先定义拦截器类,然后进行注册
package org.example.web.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class AdminWebInterceptor implements HandlerInterceptor {
/**
* 在Controller处理请求前执行。
* 返回值控制请求是否继续处理(true继续,false终止)。
* 典型应用:登录状态验证、权限校验。
* */
private static Logger logger = LoggerFactory.getLogger(AdminWebInterceptor.class);
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.info("{}->preHandle()", this.getClass().getName());
return true;
}
/**
* @Des 在Controller执行完成、视图渲染前执行。
* 可修改ModelAndView对象。
* 典型应用:统一添加响应参数。
* */
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.info("{}->postHandle()", this.getClass().getName());
}
/**
* 在视图渲染完成后执行。
* 典型应用:线程本地变量清理、性能监控统计。
* */
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.info("{}->afterCompletion()", this.getClass().getName());
}
}
package org.example.web.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.example.web.utils.AdminWebUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class AdminWebLoginHandlerInterceptor implements HandlerInterceptor {
/**
* 在Controller处理请求前执行。
* 返回值控制请求是否继续处理(true继续,false终止)。
* 典型应用:登录状态验证、权限校验。
* */
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("WebLoginHandlerInterceptor->preHandle()");
if(AdminWebUtils.get_session_user_info(request) == null){
response.sendRedirect("/login/index");
return false;
}
return true;
}
/**
* @Des 在Controller执行完成、视图渲染前执行。
* 可修改ModelAndView对象。
* 典型应用:统一添加响应参数。
* */
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("WebLoginHandlerInterceptor->postHandle()");
}
/**
* 在视图渲染完成后执行。
* 典型应用:线程本地变量清理、性能监控统计。
* */
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("WebLoginHandlerInterceptor->afterCompletion()");
}
}
注册拦截器
package org.example.web.config;
import org.example.web.interceptor.AdminWebInterceptor;
import org.example.web.interceptor.AdminWebLoginHandlerInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class AdminWebConfig implements WebMvcConfigurer {
private static Logger logger = LoggerFactory.getLogger(AdminWebConfig.class);
/**
* 注册拦截器
* */
@Override
public void addInterceptors(InterceptorRegistry registry) {
logger.info("{}->addInterceptors()", this.getClass().getName());
registry.addInterceptor(new AdminWebInterceptor());
registry.addInterceptor(new AdminWebLoginHandlerInterceptor()).addPathPatterns("/user/**");
WebMvcConfigurer.super.addInterceptors(registry);
}
}
4.5、错误页面配置
package org.example.web.resolver;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import java.util.Map;
/**
* 配置错误页面
* */
@Component
public class AdminErrorViewResolver implements ErrorViewResolver {
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
if (status == HttpStatus.NOT_FOUND) {
return new ModelAndView("error/404");
} else if (status == HttpStatus.INTERNAL_SERVER_ERROR) {
return new ModelAndView("error/400");
} else if (status == HttpStatus.BAD_REQUEST) {
return new ModelAndView("error/500");
} else {
return new ModelAndView("error/error"); // 默认错误页面
}
}
}
5、构建 model 模块
5.1、创建表实体
package org.example.model;
import jakarta.persistence.*;
import java.io.Serializable;
@Entity
@Table(name="t_user")
//@NamedQuery(name = "User.selectByUserName", query = "select t from User t where t.userName = ?1")
//@NamedQuery(name = "User.selectByUserName.count", query = "select count(t) from User t where t.userName = ?1")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "nick_name")
private String nickName;
@Column(name = "user_name")
private String userName;
@Column(name = "user_pwd")
private String userPwd;
public User(){}
public User(String nickName, String userName, String userPwd) {
this.nickName = nickName;
this.userName = userName;
this.userPwd = userPwd;
}
public User(Long id, String nickName, String userName, String userPwd) {
this.id = id;
this.nickName = nickName;
this.userName = userName;
this.userPwd = userPwd;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPwd() {
return userPwd;
}
public void setUserPwd(String userPwd) {
this.userPwd = userPwd;
}
}
5.2、标记注解
在类名上方标记 @Entity 和 @Table(本示例表名有下划线),属性名上方需标记@Column(本示例列明有下划线,对应属性应转化为驼峰命名)
6、构建 dao 模块
6.1、包目录说明

6.2、Jpa
package org.example.dao.repository;
import org.example.model.Demo;
import org.springframework.data.repository.ListCrudRepository;
import java.io.Serializable;
public interface DemoRepository extends ListCrudRepository<Demo, Long>, Serializable {
}
6.3、MyBatis-接口
package org.example.dao.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.example.model.Demo;
import java.util.List;
import java.util.Map;
@Mapper
public interface DemoMapper {
public Demo queryById(Long id);
public List<Demo> queryList(Map<String, Object> map);
public int add(Demo demo);
public int remove(Long id);
}
6.4、MyBatis-Sql
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd" >
<!-- 命名空间,这里是接口的引用,方便面向接口编程,如果是第一种方法可以随意; -->
<mapper namespace="org.example.dao.mapper.DemoMapper" >
<select id="queryById" parameterType="java.lang.Long" resultType="Demo" >
select * from t_demo where id = #{id}
</select>
<select id="queryList" parameterType="java.util.Map" resultType="Demo" >
select * from t_demo
</select>
<insert id="add" parameterType="Demo">
insert into t_demo(name, age) values(#{name}, #{age})
</insert>
<delete id="remove" parameterType="java.lang.Long">
delete from t_demo where id = #{id}
</delete>
</mapper>
7、业务层
package org.example.service.impl;
import org.example.dao.mapper.DemoMapper;
import org.example.dao.repository.DemoRepository;
import org.example.model.Demo;
import org.example.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
@Service
public class DemoServiceImpl implements DemoService {
////
@Autowired
private DemoMapper demoMapper;
@Autowired
private DemoRepository demoRepository;
public Demo queryById(Long id){
System.out.println("DemoServiceImpl-> queryById();");
// return demoMapper.queryById(id);
return demoRepository.findById(id).orElse(null);
}
@Override
public List<Demo> queryList(Map<String, Object> map) {
System.out.println("DemoServiceImpl-> queryList();");
// return demoMapper.queryList(map);
return demoRepository.findAll();
}
@Transactional(rollbackFor = Exception.class)
@Override
public void add(Demo demo) {
// demoMapper.add(demo);
demo = demoRepository.save(demo);
System.out.println("DemoServiceImpl->add() " + demo.toString());
}
@Transactional(rollbackFor = Exception.class)
@Override
public void remove(Long id) {
int result = 0;
// result = demoMapper.remove(id);
demoRepository.deleteById(id);
System.out.println("DemoServiceImpl-> remove() result = " + result);
}
}
8、控制层
package org.example.web.controller.admin;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.example.model.Demo;
import org.example.service.DemoService;
import org.example.web.utils.AdminWebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Description;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/demo")
public class DemoController {
@Autowired
private DemoService demoService;
@RequestMapping("/list")
@Description("Demo->list页面!")
public ModelAndView list(HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> resultMap = new HashMap<String, Object>();
String page_title = "示例-列表页";
resultMap.put("page_title", page_title);
resultMap.put("dataList", demoService.queryList(resultMap));
System.out.println("DemoController->list()");
return AdminWebUtils.getModelAndView(request, response, resultMap, "demo/list");
}
@RequestMapping("/detail/{id}")
@Description("Demo->detail页面!")
public ModelAndView detail(HttpServletRequest request, HttpServletResponse response, @PathVariable("id") Long id) {
Map<String, Object> resultMap = new HashMap<String, Object>();
String page_title = "示例-详情页";
resultMap.put("page_title", page_title);
resultMap.put("dataObject", demoService.queryById(id));
System.out.println("DemoController->detail()");
return AdminWebUtils.getModelAndView(request, response, resultMap, "demo/detail");
}
@RequestMapping("/add")
@Description("Demo->add页面!")
public ModelAndView add_index(HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> resultMap = new HashMap<String, Object>();
String page_title = "示例-新增页面";
resultMap.put("page_title", page_title);
System.out.println("DemoController->add()");
return AdminWebUtils.getModelAndView(request, response, resultMap, "demo/add");
}
@RequestMapping("/save")
@Description("Demo->save页面!")
public ModelAndView save(HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> resultMap = new HashMap<String, Object>();
String page_title = "示例-操作结果页面";
Boolean page_result = true;
String name = request.getParameter("name");
String age_str = request.getParameter("age");
Integer age = Integer.valueOf(age_str);
Demo demo = new Demo(name, age);
try {
demoService.add(demo);
}catch (Exception e){
page_result = false;
e.printStackTrace();
}
resultMap.put("page_title", page_title);
resultMap.put("page_result", page_result);
resultMap.put("page_result_title", page_result ? "操作成功" : "操作失败");
System.out.println("DemoController->save()");
return AdminWebUtils.getModelAndView(request, response, resultMap, "demo/result");
}
@RequestMapping("/remove/{id}")
@Description("Demo->remove页面!")
public ModelAndView remove(HttpServletRequest request, HttpServletResponse response, @PathVariable("id") Long id) {
Map<String, Object> resultMap = new HashMap<String, Object>();
String page_title = "示例-操作结果页面";
Boolean page_result = true;
try {
demoService.remove(id);
}catch (Exception e){
page_result = false;
e.printStackTrace();
}
resultMap.put("page_title", page_title);
resultMap.put("page_result", page_result);
resultMap.put("page_result_title", page_result ? "操作成功" : "操作失败");
System.out.println("DemoController->remove()");
return AdminWebUtils.getModelAndView(request, response, resultMap, "demo/result");
}
}
四、总结
本文主要描述SpringBoot3.x环境搭建步骤,包含对应配置及部分示例代码,同时将示例代码分享出来(仅作学习研究参考),示例代码在本地正常运行、访问,如有错误的地方欢迎批评指正。