# 常规整合

# 1.项目创建

注意:如果是要求jsp+servlet,请参考:https://www.cnblogs.com/LittleHann/p/17777980.html

创建maven项目,修改pom,继承springboot依赖

<parent>
    <artifactId>spring-boot-parent</artifactId>
    <groupId>org.springframework.boot</groupId>
    <version>2.3.0.RELEASE</version>
</parent>

# 指定远程资源库:

<repositories>
    <repository>
        <id>public</id>
        <name>aliyun nexus</name>
        <url>https://maven.aliyun.com/repository/public</url>
        <releases>
            <enabled>true</enabled>
        </releases>
    </repository>
</repositories>

<pluginRepositories>
    <pluginRepository>
        <id>public</id>
        <name>aliyun nexus</name>
        <url>https://maven.aliyun.com/repository/public</url>
        <releases>
            <enabled>true</enabled>
        </releases>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </pluginRepository>
</pluginRepositories>

# 2.整合MP

依赖:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.3</version>
</dependency>

配置:

spring:
  datasource:
    username: root
    password: xxxx
    url: jdbc:mysql://127.0.0.1:3306/xxx?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
# 扫描实体类
  typeAliasesPackage: com.xxx.xxx.entity
  configuration:
    map-underscore-to-camel-case: true
    auto-mapping-behavior: FULL
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 指定mapper文件的位置
  mapper-locations:
    classpath*:mapper/*.xml

开启分页:

package com.xxx.readverse.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 向MyBatis-Plus的过滤器链中添加分页拦截器,需要设置数据库类型(主要用于分页方言)
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

案例:

@GetMapping("/page/list")
@ApiOperation("分类-分页列表")
@ResponseBody
public SaResult getNovelCategoriesByPage(@RequestParam("page") Integer page, @RequestParam("keyword") String keyword) {
    Page<Category> pages = new Page<>(page, 5); // 每页显示5条记录
    QueryWrapper<Category> queryWrapper = new QueryWrapper<>();
    // 可以根据需要添加其他查询条件,例如关键字搜索
    queryWrapper.like("category_name", keyword);
    IPage<Category> categoryPage = novelCategoryService.page(pages, queryWrapper);
    return SaResult.data(categoryPage);
}

# 3.整合测试

依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

用法:

@SpringBootTest(classes = SimsApplication.class)
@RunWith(SpringRunner.class)
public class Demo {
    
    @Test
    public void test1(){
		//test: xxxxxxxxxxxxxx
    }
}

# 4.整合web

依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

用法:

@RestController
@RequestMapping("/xxxx")
public class xxxxController {
	// xxxxx
}

# 5.代码生成

依赖:

<!--        代码生成器-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.1</version>
</dependency>

<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>

用法:

 static void generate() {
    FastAutoGenerator.create("jdbc:mysql://localhost:3306/xxx?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai", "root", "root")
            .globalConfig(builder -> {
                builder.outputDir("G:\\gitee\\xxx\\src\\main\\java"); // 指定输出目录
            })
            .packageConfig(builder -> {
                builder.parent("com.xxx.xxx") // 设置xxx父包名
                        .moduleName("xxx") // 设置父包模块名
                        .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "G:\\gitee\\xxxx\\src\\main\\resources\\mapper")); // 设置mapperXml生成路径
            })
            .strategyConfig(builder -> {
                builder.entityBuilder().enableLombok();
                builder.mapperBuilder().enableMapperAnnotation().build();
                builder.controllerBuilder().enableHyphenStyle()  // 开启驼峰转连字符
                        .enableRestStyle();  // 开启生成@RestController 控制器
                builder.addInclude("xxx") // 设置需要生成的表名
                        .addTablePrefix(""); // 设置过滤表前缀
            })
            .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
            .execute();
}

# 6.接口文档

依赖:

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.2</version> <!-- 替换为最新版本 -->
</dependency>

配置:

package com.lys.sims.config;

import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
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;

@Configuration
@EnableSwagger2
public class SwaggerConfig2 {
    // 创建Docket存入容器,Docket代表一个接口文档
    @Bean
    public Docket webApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                // 创建接口文档的具体信息
                .apiInfo(webApiInfo())
                // 创建选择器,控制哪些接口被加入文档
                .select()
                // 指定@ApiOperation标注的接口被加入文档
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                .build();
    }

    // 创建接口文档的具体信息,会显示在接口文档页面中
    private ApiInfo webApiInfo(){
        return new ApiInfoBuilder()
                // 文档标题
                .title("xxxx系统接口文档")
                // 文档描述
                .description("本文档描述了xxxx信息管理系统的接口定义")
                // 版本
                .version("1.0")
                // 联系人信息
                .contact(new Contact("程序员小李", "http://xxxxx.gitee.io", "xxxx@163.com"))
                // 版权
                .license("")
                // 版权地址
                .build();
    }

        // Admin 接口分组,例如所有 /admin/** 接口
    @Bean
    public GroupedOpenApi adminApi() {
        return GroupedOpenApi.builder()
                .group("Admin 接口")
                .pathsToMatch("/admin/**")
                .build();
    }

    // Front 接口分组,例如所有 /front/** 接口
    @Bean
    public GroupedOpenApi frontApi() {
        return GroupedOpenApi.builder()
                .group("Front 接口")
                .pathsToMatch("/front/**")
                .build();
    }
}

访问:

http://localhost:{port}/doc.html

WARNING

如果接入权限认证相关,注意放行路径,如:

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册 Sa-Token 拦截器,打开注解式鉴权功能 
        registry.addInterceptor(new SaInterceptor()).
                addPathPatterns("/**").excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v3/api-docs/**", "/swagger-ui.html/**"
                        ,"/doc.html/**","/error","/favicon.ico","doc.html", "/static/**"
                );
    }

# 7.代码简写

依赖:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

用法:

@Getter
@Setter
@TableName("score")
public class Score implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    private Integer studentId;

    private Integer courseId;

    private Integer score;


}

# 8.整合thymeleaf

依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

配置:

spring:
  thymeleaf:
    cache: false
    enabled: true
    prefix: classpath:/templates/
    suffix: .html
    encoding: UTF-8
    mode: HTML

备注:注意thymeleaf语法

# 9.优雅解决时间转换问题

增加全局配置类


import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;

@Configuration
public class DateTimeConfig {

    private static final DateTimeFormatter[] DESERIALIZE_FORMATTERS = {
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"),
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"),     // 缺秒
            DateTimeFormatter.ofPattern("yyyy-MM-dd"),           // 只有日期
            DateTimeFormatter.ofPattern("yyyy-M-d HH:mm:ss"),
            DateTimeFormatter.ofPattern("yyyy-M-d H:mm:s"),
            DateTimeFormatter.ISO_LOCAL_DATE_TIME               // 支持 2025-11-05T01:03:12
    };

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() {
        return builder -> {
            builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer() {
                @Override
                public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt)
                        throws IOException {
                    String text = p.getText().trim();
                    if (text.isEmpty()) {
                        return null;
                    }

                    // 依次尝试多种格式
                    for (DateTimeFormatter f : DESERIALIZE_FORMATTERS) {
                        try {
                            return LocalDateTime.parse(text, f);
                        } catch (DateTimeParseException ignored) {}
                    }

                    // 最后尝试 ISO 带时区的
                    try {
                        return OffsetDateTime.parse(text).toLocalDateTime();
                    } catch (Exception ignored) {}

                    throw new DateTimeParseException("无法解析时间: " + text, text, 0);
                }
            });

            // 序列化统一用这个格式返回给前端
            builder.serializerByType(LocalDateTime.class,
                    new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        };
    }
}

# 10. 简易文件上传

application.yml

spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB

配置上传及时可访问 服务类

package com.book.service;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

@Service
public class FileService {

    public String upload(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            return null;
        }

        try {
            // 获取文件名
            String originalFilename = file.getOriginalFilename();
            String fileName = "";
            if (originalFilename != null) {
                fileName = System.currentTimeMillis() + "_" + originalFilename;
            } else {
                return null;
            }

            // 保存文件到服务器(这里假设保存到项目的static/uploads目录)
            String uploadDir = "src/main/resources/static/uploads/";
            Path uploadPath = Paths.get(uploadDir);

            if (!Files.exists(uploadPath)) {
                Files.createDirectories(uploadPath);
            }

            try (InputStream inputStream = file.getInputStream()) {
                Files.copy(inputStream, uploadPath.resolve(fileName), StandardCopyOption.REPLACE_EXISTING);
                // 返回图片的访问路径
                return "/uploads/" + fileName;
            }
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}

使用示例

    @PostMapping("/books")
    public String bookAdd(@ModelAttribute Book book, @RequestParam("cover") MultipartFile coverImage) {
        String coverImagePath = fileService.upload(coverImage);
        if (coverImagePath != null) {
            book.setCoverImage(coverImagePath);
        }
        bookDAO.addBook(book);
        return "redirect:/books/list";
    }
Last Updated: 12/28/2025, 2:27:53 PM