SpringBoot2.X之旅,FreeMarker模板及静态化(Web Project)

一、使用idea新建web工程

1、引入freemarker依赖:

SpringBoot2.X之旅,FreeMarker模板及静态化(Web Project)

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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.cobra.freemarker</groupId>
    <artifactId>freemarkerdemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>freemarkerdemo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <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>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3、整个工程目录结构:

SpringBoot2.X之旅,FreeMarker模板及静态化(Web Project)

二、后端coding:

1、定义domain,User.java:

package com.cobra.freemarker.domain;

/**
 * @Author: Baron
 * @Description:
 * @Date: Created in 2019/3/23 22:34
 */
public class User {

    private Integer userId;

    private String username;

    private String address;

    private String email;

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

2、service层

定义接口UserService和方法:

package com.cobra.freemarker.service;

import com.cobra.freemarker.domain.User;

import java.util.Collection;

/**
 * @Author: Baron
 * @Description: 用户服务接口
 * @Date: Created in 2019/3/23 23:26
 */
public interface UserService {

    /**
     * 根据userId查找User
     * @param userId
     * @return
     */
    User findById(Integer userId);

    /**
     * 添加新用户
     * @param user
     */
    void add(User user);

    /**
     * 查询所有用户
     * @return
     */
    Collection<User> findAllUsers();

}

实现接口方法,使用ConcurrentHashMap存储用户信息,AtomicInteger生成用户id:

package com.cobra.freemarker.service.impl;

import com.cobra.freemarker.service.UserService;
import com.cobra.freemarker.domain.User;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author: Baron
 * @Description: 用户服务实现
 * @Date: Created in 2019/3/23 23:42
 */
@Service
public class UserServiceImpl implements UserService {

    private ConcurrentMap<Integer,User> userMap = new ConcurrentHashMap<>();

    private final static AtomicInteger idGenerator = new AtomicInteger();

    /**
     * 根据userId查找User
     *
     * @param userId
     * @return
     */
    @Override
    public User findById(Integer userId) {
        return userMap.get(userId);
    }

    /**
     * 添加新用户
     *
     * @param user
     */
    @Override
    public void add(User user) {
        user.setUserId(idGenerator.incrementAndGet());
        userMap.put(user.getUserId(), user);
    }

    /**
     * 查询所有用户
     *
     * @return
     */
    @Override
    public Collection<User> findAllUsers() {
        return userMap.values();
    }
}

3、controller层:

package com.cobra.freemarker.controller;

import com.cobra.freemarker.service.StaticService;
import com.cobra.freemarker.service.UserService;
import com.cobra.freemarker.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.Collection;
import java.util.Map;

/**
 * @Author: Baron
 * @Description: user的列表、添加、查看
 * @Date: Created in 2019/3/23 22:25
 */
@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;


    /**
     * 获取所有用户
     * @param modelAndView
     * @param map
     * @return
     */
    @GetMapping("/list")
    public ModelAndView index(ModelAndView modelAndView, Map<String, Object> map) {
        Collection<User> users = userService.findAllUsers();
        map.put("users",users);
        return new ModelAndView("list",map);
    }

    /**
     * 根据id获取单个用户详情
     * @param userId
     * @param map
     * @return
     */
    @GetMapping("/{userId}")
    public ModelAndView detail(@PathVariable("userId")Integer userId, Map<String, Object> map) {
        User user = userService.findById(userId);
        map.put("user", user);
        return new ModelAndView("detail",map);
    }

    /**
     * 返回添加用户页
     * @return
     */
    @GetMapping("/add")
    public ModelAndView add() {
        return new ModelAndView("add");
    }

    /**
     * 添加用户操作
     * @param user
     * @return
     */
    @PostMapping("/save")
    public ModelAndView save(User user) {
        userService.add(user);
        return new ModelAndView("success");
    }

}

三、前端视图模板的定义:

在resources下templates中定义模板,新建html文件,然后将文件名改为ftl(业务逻辑可以提前看第四部分测试):

1、用户list.ftl列表页:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>列表页</title>
</head>
<style>
    td{text-align: center; height: 30px;}
    .code{width:40px }
    .username{width: 80px}
    .address{width: 150px}
    .link{width: 100px}
</style>
<body>
    <h3>欢迎来到!</h3>
    <a href="/user/add">添加新用户</a>
    <br>
    <br>
    <table>
        <thead>
            <tr>
                <td class="code">编号</td>
                <td class="username">姓名</td>
                <td class="address">地址</td>
                <td class="link">详情</td>
            </tr>
        </thead>
        <tbody>
            <#list users as user>
            <tr>
                <td class="code">${user.userId}</td>
                <td class="username">${user.username}</td>
                <td class="address">${user.address}</td>
                <td class="link"><a href="/user/${user.userId}" target="_blank">查看详情</a></td>
            </tr>
            </#list>
        </tbody>
    </table>
</body>
</html>

2、用户add.ftl添加页:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>新建用户</title>
</head>
<body>
    <h3>新建用户</h3>
    <form action="/user/save" method="post">
        <label>姓名:</label><input type="text" name="username" /><br><br>
        <label>地址:</label><input type="text" name="address" /><br><br>
        <label>邮箱:</label><input type="text" name="email" /><br><br>
        <input type="submit" value="提交" />
    </form>

</body>
</html>

3、用户success.ftl添加成功页,添加用户成功后返回列表页:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加成功</title>
</head>
    <body>
        <h4>添加成功!</h4><a href="http://localhost:8080/user/list" class="alert-link">三秒后自动跳转!</a>
    </body>
    <script>
        setTimeout('location.href="http://localhost:8080/user/list"',3000)
    </script>
</html>

4、单个用户detail.ftl详情页:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户详情</title>
    <style>
        td{height: 30px;}
        .left{width: 50px;}
        .right{width: 150px;}
    </style>
</head>
<body>
    <h3>用户详情</h3>
    <table>
        <tr><td class="left">编号:</td><td class="right">${user.userId}</td>
        <tr><td>姓名:</td><td>${user.username}</td>
        <tr><td>地址:</td><td>${user.address}</td>
        <tr><td>邮编:</td><td>${user.email}</td>
    </table>
</body>
</html>

四、测试freemarker的模板引擎功能:

1、启动工程,访问http://localhost:8080/user/list

SpringBoot2.X之旅,FreeMarker模板及静态化(Web Project)

2、点击添加用户,并填写信息:

SpringBoot2.X之旅,FreeMarker模板及静态化(Web Project)

3、提交,显示添加成功:

SpringBoot2.X之旅,FreeMarker模板及静态化(Web Project)

4、返回列表:

SpringBoot2.X之旅,FreeMarker模板及静态化(Web Project)

5、点击查看详情:

SpringBoot2.X之旅,FreeMarker模板及静态化(Web Project)

五、页面静态化实现模板转化为html页面,可以让前端直接浏览html页面,减少对数据库的请求:

1、定义静态化服务接口StaticService 及将list.ftl生成index.html的方法:

package com.cobra.freemarker.service;

/**
 * @Author: Baron
 * @Description: 生成静态页面service
 * @Date: Created in 2019/3/24 2:04
 */
public interface StaticService {

    /**
     * 生成首页静态页面
     */
    void createIndexHtml();

}

2、实现类:

package com.cobra.freemarker.service.impl;

import com.cobra.freemarker.service.StaticService;
import com.cobra.freemarker.service.UserService;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: Baron
 * @Description:
 * @Date: Created in 2019/3/24 2:07
 */
@Service
public class StaticServiceImpl implements StaticService {

    @Autowired
    private UserService userService;

    @Autowired
    private Configuration configuration;

    /**
     * 生成首页静态页面
     */
    @Override
    public void createIndexHtml() {
        try {
            /**获取输出目标文件输出流------开始*/
            String filepath = this.getClass().getResource("/").toURI().getPath()+"static/";
            File folder = new File(filepath);
            //如果文件夹不存在
            if (!folder.exists()) {
                folder.mkdir();
            }
            String indexFileName = "index.html";
            File indexHtml = new File(folder, indexFileName);
            //如果html文件不存在
            if (!indexHtml.exists()) {
                indexHtml.createNewFile();
            }
            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(indexHtml),"UTF-8"));
            /**获取输出目标文件输出流------结束*/

            //获取数据
            Map<String, Object> map = new HashMap<>();
            map.put("users", userService.findAllUsers());

            //获取模板
            Template template = configuration.getTemplate("list.ftl");

            //把数据和输出文件信息交给模板得到静态html文件
            template.process(map,out);

            out.flush();
            out.close();}
        catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }
    }
}

3、在UserController中,添加用户时,调用静态方法,实现每次添加用户,都会生成最新的静态页面,如果有修改、删除操作,也要调用静态化方法:

package com.cobra.freemarker.controller;

import com.cobra.freemarker.service.StaticService;
import com.cobra.freemarker.service.UserService;
import com.cobra.freemarker.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.Collection;
import java.util.Map;

/**
 * @Author: Baron
 * @Description: user的列表、添加、查看
 * @Date: Created in 2019/3/23 22:25
 */
@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private StaticService staticService;

    /**
     * 获取所有用户
     * @param modelAndView
     * @param map
     * @return
     */
    @GetMapping("/list")
    public ModelAndView index(ModelAndView modelAndView, Map<String, Object> map) {
        Collection<User> users = userService.findAllUsers();
        map.put("users",users);
        return new ModelAndView("list",map);
    }

    /**
     * 根据id获取单个用户详情
     * @param userId
     * @param map
     * @return
     */
    @GetMapping("/{userId}")
    public ModelAndView detail(@PathVariable("userId")Integer userId, Map<String, Object> map) {
        User user = userService.findById(userId);
        map.put("user", user);
        return new ModelAndView("detail",map);
    }

    /**
     * 返回添加用户页
     * @return
     */
    @GetMapping("/add")
    public ModelAndView add() {
        return new ModelAndView("add");
    }

    /**
     * 添加用户操作
     * @param user
     * @return
     */
    @PostMapping("/save")
    public ModelAndView save(User user) {
        userService.add(user);
        staticService.createIndexHtml();
        return new ModelAndView("success");
    }

}

4、测试:

添加一个用户之后,在static下会生成一个index.html页面:

SpringBoot2.X之旅,FreeMarker模板及静态化(Web Project)

直接访问生成的html页面:http://localhost:8080/index.html

SpringBoot2.X之旅,FreeMarker模板及静态化(Web Project)

六、代码地址:https://github.com/yaobaron/freemarkerdemo

上一篇:java – Freemarker For循环


下一篇:freemarker list size问题