ztree异步加载树形菜单,demo结构如下:

图片.png
springboot打成war包后(不含jsp), 后台启动, 并不输出nohup.out日志的方法:
nohup java -jar hello.war > /dev/null &
1.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>
<groupId>com.zy</groupId>
<artifactId>spring-boot-ztree-async-ajax-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>spring-boot-ztree-async-ajax-demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.17.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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-test</artifactId>
<!--<scope>test</scope>-->
</dependency>
<!--用于解析jsp,不能与HTML的支持包同时存在-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.46</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.6.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.jsp
2.1index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<jsp:forward page="/files/getFatherFileUrls"/>
2.2fileUrl.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<HTML>
<HEAD>
<TITLE> ZTREE DEMO - Async</TITLE>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="${pageContext.request.contextPath }/css/demo.css" type="text/css">
<link rel="stylesheet" href="${pageContext.request.contextPath }/css/zTreeStyle/zTreeStyle.css" type="text/css">
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-3.3.1.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery.ztree.core.js"></script>
<SCRIPT type="text/javascript">
<!--
let setting = {
async: {
enable: true,
url:"/files/getSonFileUrls", // 这里是controller层RequestingMapping对应的值
autoParam:["id"], // 这里的id是传递到后台的参数,controller中可以接收到
//otherParam:{"otherParam":"zTreeAsyncTest"}, // 本案例中,这里可以不写
dataFilter: filter
},
data: {
simpleData: {
enable: true
}
}
};
function filter(treeId, parentNode, childNodes) {
if (!childNodes) return null;
for (var i=0, l=childNodes.length; i<l; i++) {
childNodes[i].name = childNodes[i].name.replace(/\.n/g, '.');
}
return childNodes;
}
$(document).ready(function(){
$.fn.zTree.init($("#treeDemo"), setting, ${fatherFileUrls}); // 此处的$中的值,是初始化顶级父节点
});
//-->
</SCRIPT>
</HEAD>
<BODY>
<h1>异步加载节点数据的树</h1>
<h6>[ 文件路径: core/async.html ]</h6>
<div class="content_wrap">
<div class="zTreeDemoBackground left">
<ul id="treeDemo" class="ztree"></ul>
</div>
</div>
</BODY>
</HTML>
3.css与js(均参考ztree官网)

图片.png
4.controller层
package com.zy.controller;
import com.alibaba.fastjson.JSON;
import com.zy.service.FileUrlServiceI;
import com.zy.utils.ZtreeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
import java.util.Map;
@Controller
@RequestMapping("/files/")
public class FileUrlController {
@Autowired
private FileUrlServiceI fileUrlService;
@RequestMapping("getFatherFileUrls")
public String getFatherFileUrls(Map map){
List<ZtreeUtils> list = fileUrlService.getFatherFileUrls();
Object json = JSON.toJSON(list);
map.put("fatherFileUrls", json);
return "fileUrl";
}
@RequestMapping("getSonFileUrls")
@ResponseBody
public Object getSonFileUrls(String id){
List<ZtreeUtils> list = fileUrlService.getSonFileUrls(id);
Object json = JSON.toJSON(list);
return json;
}
}
5.service层实现类
package com.zy.service.impl;
import com.zy.mapper.FileUrlMapper;
import com.zy.service.FileUrlServiceI;
import com.zy.utils.StringTools;
import com.zy.utils.ZtreeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service("fileUrlService")
public class FileUrlServiceImpl implements FileUrlServiceI {
@Autowired
private FileUrlMapper fileUrlMapper;
// 此处加载两级节点
@Override
@Cacheable(value = "fileUrls")
public List<ZtreeUtils> getFatherFileUrls() {
List<String> list = fileUrlMapper.getFatherFileUrls();
List<ZtreeUtils> ztree = new ArrayList<>();
list.forEach(fileUrl -> {
ztree.add(new ZtreeUtils(fileUrl + "/", "0", fileUrl + "/", true, true));
getSecondFileUrls(ztree, fileUrl + "/");
});
return ztree;
}
@Override
@Cacheable(value = "fileUrls")
public List<ZtreeUtils> getSonFileUrls(String filePath) {
Integer generationNum = StringTools.getCharCount(filePath, 47) + 1;
List<String> list = fileUrlMapper.getSonFileUrls(generationNum, filePath);
List<ZtreeUtils> ztree = new ArrayList<>();
// 这里并未对最后一级子节点进行处理(最后一级子节点不需要加"/"
list.forEach(fileUrl -> {
String[] split = fileUrl.split("/");
ztree.add(new ZtreeUtils(fileUrl + "/", filePath, split[split.length - 1], false, true));
});
return ztree;
}
// 加载二级子节点
private void getSecondFileUrls(List<ZtreeUtils> ztree, String filePath) {
List<String> list = fileUrlMapper.getSonFileUrls(2, filePath);
// 这里并未对最后一级子节点进行处理(最后一级子节点不需要加"/"
list.forEach(fileUrl -> {
String[] split = fileUrl.split("/");
ztree.add(new ZtreeUtils(fileUrl + "/", filePath, split[split.length - 1], false, true));
});
}
}
6.mapper层
package com.zy.mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface FileUrlMapper {
List<String> getFatherFileUrls();
List<String> getSonFileUrls(@Param("generationNum") Integer generationNum,
@Param("filePath") String filePath);
}
7.工具类
7.1ztree工具类
package com.zy.utils;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ZtreeUtils {
private String id;
private String pId;
private String name;
private Boolean open;
private Boolean isParent;
}
7.2stringtools工具类
package com.zy.utils;
import org.apache.commons.lang3.StringUtils;
public class StringTools {
public static int getCharCount(String str, int c){
if (StringUtils.isEmpty(str)){
return -1;
}
int count = 0;
for (int i = 0; i < str.length(); i ++){
if (str.charAt(i) == c){
count ++;
}
}
return count;
}
}
8.config包下配置类
8.1静态资源配置类
package com.zy.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class StaticResourceMapping extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 配置静态资源
registry.addResourceHandler("/js/**").addResourceLocations("WEB-INF/js/");
registry.addResourceHandler("/css/**").addResourceLocations("WEB-INF/css/");
super.addResourceHandlers(registry);
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 配置默认访问页:需要在浏览器中输入ip+端口号
registry.addViewController("/").setViewName("forward:/index.jsp");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
super.addViewControllers(registry);
}
}
8.2配置caffine缓存的map类
package com.zy.config;
import com.github.benmanes.caffeine.cache.CacheLoader;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CaffineCacheRefresh {
/**
* 必须要指定这个Bean,refreshAfterWrite=1800s这个配置属性才生效
*
* @return
*/
@Bean
public CacheLoader<Object, Object> cacheLoader() {
CacheLoader<Object, Object> cacheLoader = new CacheLoader<Object, Object>() {
@Override
public Object load(Object key) throws Exception {
return null;
}
// 重写这个方法将oldValue值返回回去,进而刷新缓存
@Override
public Object reload(Object key, Object oldValue) throws Exception {
return oldValue;
}
};
return cacheLoader;
}
}
8.3配置项目启动时加载数据的类
package com.zy.config;
import com.zy.mapper.FileUrlMapper;
import com.zy.service.FileUrlServiceI;
import com.zy.utils.ZtreeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.cache.annotation.CachePut;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 项目启动时加载数据的类
*/
@Component
public class InitData implements CommandLineRunner {
@Autowired
private FileUrlServiceI fileUrlService;
@Override
public void run(String... strings) throws Exception {
getFatherFileUrls();
}
@CachePut(value = "fileUrls")
public List<ZtreeUtils> getFatherFileUrls(){
return fileUrlService.getFatherFileUrls();
}
}
9.启动类
package com.zy;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@MapperScan("com.zy.mapper")
@EnableCaching
public class SpringBootZtreeAsyncAjaxDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootZtreeAsyncAjaxDemoApplication.class, args);
}
}
10.application.yml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/spring_boot_ztree
username: root
password: 123456
# 配置jsp的解析
mvc:
view:
prefix: /WEB-INF/jsp/
suffix: .jsp
cache:
cache-names: fileUrls
caffeine:
spec: initialCapacity=50,maximumSize=500,expireAfterWrite=10s,refreshAfterWrite=5s
# Caffeine配置说明:
#
#initialCapacity=[integer]: 初始的缓存空间大小
#maximumSize=[long]: 缓存的最大条数
#maximumWeight=[long]: 缓存的最大权重
#expireAfterAccess=[duration]: 最后一次写入或访问后经过固定时间过期
#expireAfterWrite=[duration]: 最后一次写入后经过固定时间过期
#refreshAfterWrite=[duration]: 创建缓存或者最近一次更新缓存后经过固定的时间间隔,刷新缓存
#weakKeys: 打开key的弱引用
# weakValues:打开value的弱引用
# softValues:打开value的软引用
# recordStats:开发统计功能
#
# 注意:
#
# expireAfterWrite和expireAfterAccess同事存在时,以expireAfterWrite为准。
# maximumSize和maximumWeight不可以同时使用
# weakValues和softValues不可以同时使用
# mybatis中,静态资源不发布:此处指的是resources文件夹下的文件
mybatis:
mapper-locations: classpath:mybatis/mapper/*.xml
config-location: classpath:mybatis/sqlMappingConfig.xml
11.mybatis配置文件
11.1全局配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--设置全局配置环境-->
<settings>
<!-- <!–开启全局缓存–>
<setting name="cacheEnabled" value="true"/>
<!–开启懒加载–>
<setting name="lazyLoadingEnabled" value="true"/>
<!–关闭延迟加载–>
<setting name="aggressiveLazyLoading" value="false"/>-->
<!-- 打印查询语句 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
<!--配置分页插件pageHelper的拦截器:5.0版本之后的配置-->
<!--<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>-->
</configuration>
11.2分配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zy.mapper.FileUrlMapper">
<!-- 查询顶级父目录 -->
<select id="getFatherFileUrls" resultType="string">
SELECT DISTINCT SUBSTRING_INDEX(fileUrl,'/',1)
FROM tb_fileurl
</select>
<!-- 查询子目录 -->
<select id="getSonFileUrls" resultType="string">
SELECT DISTINCT SUBSTRING_INDEX(fileUrl,'/',#{generationNum})
FROM tb_fileurl
where fileUrl like concat(#{filePath},'%')
</select>
</mapper>
12.表中内容

图片.png
13.结果图示

图片.png