SpringBoot 3.x + Jpa + MyBatis 环境搭建

一、搭建目的

        在此之前,由于没有接触过SpringBoot,项目也一直用 Spring + SpringMVC 来进行开发,在上一个文章《 SpringMVC + Freemarker + JPA + MyBatis 环境搭建》 完成后,利用几天时间初步了解和学习SpringBoot框架,整体感觉非常棒,环境搭建相对常规 Spring + SpringMVC 来说简化了一些步骤,很多显示性声明SpringBoot都已经包含进去了,常用的第三方组件集成也很方便,因此手动搭建一个示例并将过程步骤记录下来,方便后期参考,本文仅供学习参考,非喜勿喷。

二、环境及版本要求

序号环境名称版本号
1操作系统deppin 社区版(20.9)
2IntelliJ IDEA2023.2 (Ultimate Edition)
3JDK17.0.14
4Maven3.9.9
5SpringBoot3.4.5
6MyBatis-SpringBoot3.0.4

三、搭建环境

1、创建项目

1.1、创建模块

        本示例采用分层、分包、分模块进行搭建,具体如下图(图3-1)所示

图3-1 项目模块图

1.2、模块说明

序号模块备注
1model表对应实体,需显示标记@Entity 
2dao数据持久层,分jpa 和 mybatis
3service业务层接口
4service-impl业务层实现
5web-admin控制层及应用启动入口

2、配置 pom 

2.1、配置根 pom

        根 pom 配置如下图(图3-2)所示

图3-2 根pom配置

      根 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-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)所示:

图3-4 web模块目录说明

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、包目录说明

图3-5 包目录说明

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环境搭建步骤,包含对应配置及部分示例代码,同时将示例代码分享出来(仅作学习研究参考),示例代码在本地正常运行、访问,如有错误的地方欢迎批评指正。

五、示例代码

通过 GitHub 获取

通过 GitCode 获取

通过 Gitee 获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值