SpringBoot整合Thymeleaf+EasyExcel实现excel文件的读取并展示,附加swagger2配置(超详细示范!)

目录

1.Springboot整合Thyemleaf+EasyExcel 步骤

1.1 pom文件引入依赖

1.2 yml文件配置 

1.3 config配置类

1.3.1 Swagger2配置类

1.3.2 前端跨域问题解决

2. 后端代码编写

2.1 Controller层

2.1.1 补充:@RestController页面跳转方法

2.2 service层 

实现AnalysisEventListener方法

2.3 Entity层 

2.4 excel表  

3. 前端页面设计

3.1 Thyemleaf模板

index.html

 实际页面展示

id查询 

查询全部 

Springboot启动,方法自动执行


        最近由于工作需要,实现从excel文件读取数据并用静态页面展示。(前端的html页面迟迟不发过来,只能自己动手写页面展示了,虽然现在都是前后端分离展示,但是对于好多后端开发刚入门的是一个难点,所以就考虑使用Thymeleaf模板引擎实现页面数据展示。

Thymeleaf是一款现代的服务器端 Java 模板引擎,可以轻松与SpringMvc、springBoot等web框架进行集成,适用于 Web 和独立环境开发。与jsp、Velocity、FreeMaker功能类似,thymeleaf可以通过thymeleaf标签渲染处理数据用以展示给用户。

1.Springboot整合Thyemleaf+EasyExcel 步骤

1.1 pom文件引入依赖

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.0</version>
  </parent>

  <dependencies>

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

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

    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.2.2</version>
    </dependency>

 <!--fastjson-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.76</version>
    </dependency>

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

    <!-- lombok依赖包 -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.16.10</version>
      <scope>provided</scope>
    </dependency>

    <!--swagger依赖-->
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger2</artifactId>
      <version>2.9.2</version>
    </dependency>

    <!--swagger-ui-->
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger-ui</artifactId>
      <version>2.9.2</version>
    </dependency>

    <!-- easyexcel 依赖 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>easyexcel</artifactId>
      <version>3.1.1</version>
    </dependency>

        这里有一个很大的坑,自己在这里花了很长时间来解决jar包冲突问题。

  • spring-boot-starter-parent在导入这个依赖后,必须要导入spring-boot-starter-web这个依赖。不然会导致springboot项目启动后立即停止的问题。
  • spring-boot-starter-thymeleaf的这个依赖版本建议与spring-boot-starter-parent的版本一致,不然可能会导致thymeleaf加载不上。

1.2 yml文件配置 

server:
  port: 8080
//  解决springboot2.7与swagger2冲突问题
spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    encoding: UTF-8
    mode: HTML5

doctor:
  fileName: "C:\\\\Users\\\\admin\\\\Desktop\\\\Api\\\\医师节数据样例.xlsx"

1.3 config配置类

1.3.1 Swagger2配置类

@Configuration
@EnableSwagger2
public class Swagger2ApiConfig {
    /**
     * 创建API应用
     * apiInfo() 增加API相关信息
     * 通过select()函数返回一个ApiSelectorBuilder实例,用来控制哪些接口暴露给Swagger来展现,
     * 指定扫描的包路径来定义指定要建立API的目录。
     * @return
     */
    @Bean
    public Docket coreApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(adminApiInfo())
                .groupName("Api-wx")
                .select()
                // 用于指定扫描哪个包下的接口
                .apis(RequestHandlerSelectors.basePackage("com.yif.controller"))
                // 选择所有的API,如果你想只为部分API生成文档,可以配置这里
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo adminApiInfo(){
        return new ApiInfoBuilder()
                .title("wx--api文档")
                .description("wx-api接口描述")
                .version("1.0")
                .contact(new Contact("凡仔","https://github.com/hszyf2051/","151900983@qq.com"))
                .build();
    }
}

1.3.2 前端跨域问题解决

@Component
public class CrossFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse res = (HttpServletResponse) servletResponse;
        res.addHeader("Access-Control-Allow-Credentials", "true");
        res.addHeader("Access-Control-Allow-Origin", "*");
        //*代表所有网站均无拦截,也可设置自己相应的域名
        res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
        res.addHeader("Access-Control-Allow-Headers", "Content-Type,X-CAF-Authorization-Token,sessionToken,X-TOKEN");
        if (((HttpServletRequest) servletRequest).getMethod().equals("OPTIONS")) {
            servletResponse.getWriter().println("ok");
            return;
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

}

2. 后端代码编写

2.1 Controller层

@Controller
public class DoctorController {

    @Autowired
    private IDoctorService doctorService;

    /**
     * 查找所有医生
     * @param model
     * @return
     */
    @RequestMapping("/getDoctors")
    public String getDoctors(Model model) {
        List<Doctor> doctors = doctorService.readDoctors();
        model.addAttribute("doctors",doctors);
        return "index";
    }

    /**
     * 根据id查找对应的医生
     * @param model
     * @param id
     * @return
     */
    @GetMapping("/findDoctorById")
    public String findDoctorById(Model model,@RequestParam String id) {
        List<Doctor> doctors = doctorService.findDoctorById(id);
        model.addAttribute("doctors",doctors);
        return "index";
    }
}

注意这里的@Controller注解不能写成@RestController,不然会导致页面直接返回index。

return "index" 填写的是resources/templates/下的html页面。必须与页面名字相一致。


2.1.1 补充:@RestController页面跳转方法

若只用@RestController注解,则需要将方法的类型改为ModelAndView。页面的返回路径前不可以加/,不然会无法解析路径,返回字符串!(本人在这里绕了很久)例如"/User",切记不能这么写

    @GetMapping("/findUserById")
    public ModelAndView findDoctorById(Model model,@RequestParam String id) {
        List<User> userList = userService.findUserById(id);
        model.addAttribute("users",userList);
        // 返回值类型也必须是ModelAndView!!! 
        return new ModelAndView("User");
    }

2.2 service层 

@Slf4j
@Service
public class DoctorServiceImpl implements IDoctorService {

    /**
     * 从yml中读取文件路径
     */
    @Value("${doctor.fileName}")
    private String fileName;

    @Override
    public List<Doctor> readDoctors() {
        // 创建一个数据格式来承装读取到数据
        Class<Doctor> head = Doctor.class;
        // 创建ExcelReader对象
        List<Doctor> doctorList = new ArrayList<>();
        ExcelReader excelReader = EasyExcel.read(fileName, head, new AnalysisEventListener<Doctor>() {
            @Override
            public void invoke(Doctor doctor, AnalysisContext analysisContext) {
                if (doctor.getHealing() == null) {
                    // 住院患者治疗量
                    doctor.setHealing(0);
                }
                if (doctor.getBirthOperations() == null) {
                    // 生日当天手术量
                    doctor.setBirthOperations(0);
                }
                if (doctor.getBirthVisits() == null) {
                    // 生日当天看诊量
                    doctor.setBirthVisits(0);
                }
                doctorList.add(doctor);
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            }
        }).build();
        // 创建sheet对象,并读取Excel的第1个sheet(下标从0开始)
        ReadSheet readSheet = EasyExcel.readSheet(0).build();
        excelReader.read(readSheet);
        // 关闭流操作,在读取文件时会创建临时文件,如果不关闭,磁盘爆掉
        excelReader.finish();
        log.info(String.valueOf(doctorList));
        return doctorList;

    }

    @Override
    public List<Doctor> findDoctorById(String id) {
        // 创建一个数据格式来承装读取到数据
        Class<Doctor> head = Doctor.class;
        // 创建ExcelReader对象
        List<Doctor> doctorList = new ArrayList<>();
        ExcelReader excelReader = EasyExcel.read(fileName, head, new AnalysisEventListener<Doctor>() {
            @Override
            public void invoke(Doctor doctor, AnalysisContext analysisContext) {
                if (doctor.getId().equals(id)) {
                    if (doctor.getHealing() == null) {
                        // 住院患者治疗量
                        doctor.setHealing(0);
                    }
                    if (doctor.getBirthOperations() == null) {
                        // 生日当天手术量
                        doctor.setBirthOperations(0);
                    }
                    if (doctor.getBirthVisits() == null) {
                        // 生日当天看诊量
                        doctor.setBirthVisits(0);
                    }
                    doctorList.add(doctor);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                if (doctorList.size() == 0) {
                    log.info("该企业账号:"+ id +",查无此人");
                }
            }
        }).build();
        // 创建sheet对象,并读取Excel的第1个sheet(下标从0开始)
        ReadSheet readSheet = EasyExcel.readSheet(0).build();
        excelReader.read(readSheet);
        // 关闭流操作,在读取文件时会创建临时文件,如果不关闭,磁盘爆掉
        excelReader.finish();
        log.info(String.valueOf(doctorList));
        return doctorList;
    }
}

实现AnalysisEventListener方法

        invoke方法是遍历每一条excel中数据,可以实现空数据,重复数据的检查。可以在外层定义一个List,实现数据的临时缓存。

        doAfterAllAnalysed方法是在整个遍历完成后执行的方法,我在这里增加逻辑判断,如果list的大小为0,则显示该id查无此人。

 @Value("${doctor.fileName}")
    private String fileName;

这一块引用的是yml里的属性,可以做到一定数据的解耦

2.3 Entity层 

@Data
public class Doctor {
    /**
     * 姓名
     */
    @ExcelProperty(value = "姓名")
    private String name;

    /**
     * 企业账号
     */
    @ExcelProperty(value = "企业账号")
    private String id;

    /**
     * 身份
     */
    @ExcelProperty(value = "身份")
    private String identity;

    /**
     * 就诊量
     */
    @ExcelProperty(value = "门诊就诊量")
    private Integer visits;

    /**
     * 看诊最多日期
     */
    @DateTimeFormat("yyyy年MM月dd日")
    @ExcelProperty(value = "看诊最多日期")
    private String maxVisitDate;

    /**
     * 看诊最多日看诊量
     */
    @ExcelProperty(value = "看诊最多日看诊量")
    private Integer maxVisit;

    /**
     * 住院患者治疗量
     */
    @ExcelProperty(value = "住院患者治疗量")
    private Integer healing;

    /**
     * 手术量
     */
    @ExcelProperty(value = "手术量")
    private Integer operation;

    /**
     * 手术总时长
     */
    @ExcelProperty(value = "手术总时长")
    private String timeOperations;

    /**
     * 手术最多搭档
     */
    @ExcelProperty(value = "手术最多搭档")
    private String partner;

    /**
     * 手术时长最长时间
     */
    @ExcelProperty(value = "手术时长最长时间")
    private String maxTimeOperations;

    /**
     * 手术最长结束时间
     */
    @DateTimeFormat("yyyy年MM月dd日HHmm")
    @ExcelProperty(value = "手术最长结束时间")
    private String latestTimeOperations;

    /**
     * 医生生日
     */
    @DateTimeFormat("mm月dd日")
    @ExcelProperty(value = "医生生日")
    private String birthDate;

    /**
     * 生日当天看诊量
     */
    @ExcelProperty(value = "生日当天看诊量")
    private Integer birthVisits;

    /**
     * 生日当天手术量
     */
    @ExcelProperty(value = "生日当天手术量")
    private Integer birthOperations;

    /**
     * 线上诊疗人次(互联网门诊)
     */
    @ExcelProperty(value = "线上诊疗人次(互联网门诊)")
    private Integer onlineVisits;
}

 @ExcelProperty(value = " ") 注解是EasyExcel提供的,value中填写excel中对应字段名字,一定要完全对应上,不然会出现映射错误,导致读取的数据为空值。

 @DateTimeFormat("yyyy年MM月dd日") 实现对日期类型数据的转换,如果只需要部分时间字段,填写对应的日期格式就好了。

完整格式: yyyy-MM-dd HH:mm:ss

2.4 excel表  

 

3. 前端页面设计

3.1 Thyemleaf模板

        Thyemleaf入门教程:http://t.csdn.cn/RDU5r

index.html

<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>循环遍历获取医生信息</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>

<body>
    <h2 class="index-f-head" style="text-align: center"> 医师节活动 <span>查询医生信息</span> </h2>

    <form action="http://localhost:8080/findDoctorById" method="get">
        企业账号:<input type="text" name="id" placeholder="请输入你的企业账号">
        <input type="submit" value="查询">
        <input type="reset" value="重置">
    </form>
    <br>
    <input type="reset" value="清空数据" onclick="resetAll()">
    <input type="button" value="查询全部" onclick="queryAll()"> <br><br><br>
    <div class="index-f-body" style="text-align: center">
        <table style="border-width: 1px;border-style: solid;width: 100% ">
            <tr>
                <td>姓名</td>
                <td>企业账号</td>
                <td>身份</td>
                <td>门诊就诊量</td>
                <td>看诊最多日期</td>
                <td>看诊最多日看诊量</td>
                <td>住院患者治疗量</td>
                <td>手术量</td>
                <td>手术总时长</td>
                <td>手术最多搭档</td>
                <td>手术时长最长时间</td>
                <td>手术最长结束时间</td>
                <td>医生生日</td>
                <td>生日当天看诊量</td>
                <td>生日当天手术量</td>
                <td>线上诊疗人次(互联网门诊)</td>
            </tr>

            <div th:each="doctor : ${doctors}" style="align-items: center">
                <tr>
                    <td th:text="${doctor.name}"></td>
                    <td th:text="${doctor.id}"></td>
                    <td th:text="${doctor.identity}"></td>
                    <td th:text="${doctor.visits}"></td>
                    <td th:text="${doctor.maxVisitDate}"></td>
                    <td th:text="${doctor.maxVisit}"></td>
                    <td th:text="${doctor.healing}"></td>
                    <td th:text="${doctor.operation}"></td>
                    <td th:text="${doctor.timeOperations}"></td>
                    <td th:text="${doctor.partner}"></td>
                    <td th:text="${doctor.maxTimeOperations}"></td>
                    <td th:text="${doctor.latestTimeOperations}"></td>
                    <td th:text="${doctor.birthDate}"></td>
                    <td th:text="${doctor.birthVisits}"></td>
                    <td th:text="${doctor.birthOperations}"></td>
                    <td th:text="${doctor.onlineVisits}"></td>
                </tr>
            </div>
        </table>
    </div>

</body>
<script>
    function resetAll() {
        window.location.href = 'http://localhost:8080/findDoctorById?id'
    }
    function queryAll(){
        window.location.href = 'http://localhost:8080/getDoctors'
    }
</script>
</html>

         注意这里的html页面路径一定要放在templates文件夹下,不然会映射不到。

 实际页面展示

id查询 

 

查询全部 

 

        在按钮中添加了window.location.href的方法,实现当前页面url的跳转。这里要注意的是配置跨域,前面已经在config中配置。


Springboot启动,方法自动执行

如果想实现Springboot启动后自动实现方法读取数据,可以实现ApplicationRunner接口。

@Slf4j
@Component
@Order(value = 1)
public class MyApplicationRunner implements ApplicationRunner {
    @Autowired
    private IDoctorService doctorService;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("测试MyApplicationRunner");
        doctorService.readDoctors();
    }
}

以上,喜欢请点个赞吧~

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
书城管理系统主要分为前台和后台两部分,前台主要是面向读者的,包括图书展示、购买、借阅等功能;后台主要是面向管理员的,包括图书管理、用户管理、订单管理等功能。 下面是一个基于SpringBoot + Thymeleaf + Mybatis的书城管理系统的简单实现: 1. 数据库设计 - 用户表:id, username, password, email, phone - 图书表:id, book_name, author, price, stock, sales, image - 订单表:id, user_id, book_id, status, create_time, update_time 2. 实体类设计 - 用户类:包括id、用户名、密码、邮箱、电话等属性 - 图书类:包括id、书名、作者、价格、库存、销量、图片等属性 - 订单类:包括id、用户id、图书id、状态、创建时间、更新时间等属性 3. DAO层设计 - 用户DAO:包括增删改查等操作,使用Mybatis注解或XML方式实现 - 图书DAO:包括增删改查等操作,使用Mybatis注解或XML方式实现 - 订单DAO:包括增删改查等操作,使用Mybatis注解或XML方式实现 4. 服务层设计 - 用户服务:包括用户注册、登录、修改密码、查询用户信息等方法 - 图书服务:包括图书查询、购买、借阅、归还等方法 - 订单服务:包括订单生成、查询、修改状态等方法 5. 控制层设计 - 前台控制器:包括图书展示、购买、借阅等操作的处理 - 后台控制器:包括图书管理、用户管理、订单管理等操作的处理 6. 视图层设计 - 前台视图:包括图书展示、购买、借阅等页面的设计 - 后台视图:包括图书管理、用户管理、订单管理等页面的设计 以上是一个简单的基于SpringBoot + Thymeleaf + Mybatis的书城管理系统的设计实现,可以根据实际需求进行调整和扩展。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值