第7章 Spring Boot集成模板引擎

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 第7章 Spring Boot集成模板引擎因为Spring Boot其实是对Spring生态的封装整合打包,以简化开发中使用Spring框架。所以 Spring Boot在集成模板引擎过程中,其实就是对传统SpringMVC集成对应模板引擎的打包自动化配置。

第7章 Spring Boot集成模板引擎

因为Spring Boot其实是对Spring生态的封装整合打包,以简化开发中使用Spring框架。所以 Spring Boot在集成模板引擎过程中,其实就是对传统SpringMVC集成对应模板引擎的打包自动化配置。

在Spring MVC架构中:

“视图(View)”负责前端页面的展示。后端的数据对象怎样流转到前端,以及前后端数据的交互是怎样的呢? 这些都是由模板引擎来“牵线搭桥”的。

Spring Web MVC是一种基于Java实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行解耦,基于请求-响应模型帮助我们简化日常web系统的开发。

Spring Web MVC框架就是一种MVC框架。采用“约定优于配置”的契约式编程方式。主要有下面几大功能模块:

  • 前端控制器是DispatcherServlet主要用于控制流程;
  • 应用控制器为处理器映射器(Handler Mapping)进行处理器管理
  • 视图解析器(View Resolver)进行视图的解析;
  • 页面的url映射以及跳转控制由页面控制器(Controller)/动作跳转处理器(Action)来完成;
  • 本地化解析、文件上传等模块;
  • 数据校验、格式化和数据绑定模块

下面我们主要讲的是跟视图解析器(View Resolver)相关的模板引擎。

模板引擎是为了使用户界面与业务数据分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。

在MVC模式中,模板引擎的工作原理基本一样,比如说以freemarker为例,如下图:

可概括为一个公式:

模板 + 数据模型 = 输出

在Java Web开发领域,常用的模板引擎有jsp, velocity, freemarker, thymeleaf等。Spring Boot对这些模板引擎都支持集成。对spring-boot-starter-velocity的更新只维护到了1.4.6.RELEASE版本。

任何一个模板引擎(jsp,velocity,thymeleaf,freemarker等),都需要通过数据模型变量以及一些基本语法来实现数据到前端模板的输出。后端与前端隔离,其实并没有完全隔离,只是抽象分层了,把数据逻辑归后端Model,展现的逻辑归视图View,中间通过数据对象来作数据的传输通信。Model数据在视图View的解析展现,这个工作就是模板引擎来完成的。

7.1 Spring Boot集成jsp模板

JSP(Java Server Pages,Java服务端页面),是一种动态页面技术,它的主要目的是将表示逻辑从Servlet中分离出来,本质上可以看做是一个简化的Servlet。

JSP实现了Html语法中的java扩展(以 <%, %>形式)。它是在传统的网页HTML文件中插入Java程序段和JSP标记(tag),从而形成JSP文件,后缀名为(*.jsp)。

JSP与Servlet一样,是在服务器端执行的。通常返回给客户端的就是一个HTML文本,因此客户端只要有浏览器就能浏览。

JSP将网页逻辑与网页设计的显示分离,支持可重用的基于组件的设计,使基于Web的应用程序的开发变得更加简易[4]。

要使用jsp,我们首先需要添加其核心依赖

    compile('org.springframework.boot:spring-boot-starter-web')
//    JSP
    providedCompile('org.springframework.boot:spring-boot-starter-tomcat')
    providedCompile('org.apache.tomcat.embed:tomcat-embed-jasper')
    compile('javax.servlet:jstl')

其中, JSTL(JSP Standard TagLibrary)是JSP标准标签库。tomcat-embed-jasper是嵌入式tomcat支持。

在Spring Web工程中,默认的JSP的模板前缀是/WEB-INF/,后缀是.jsp。

这个是在org.springframework.web.servlet.config.annotation.ViewResolverRegistry中定义的 :

    public UrlBasedViewResolverRegistration jsp() {
        return jsp("/WEB-INF/", ".jsp");
    }

    public UrlBasedViewResolverRegistration jsp(String prefix, String suffix) {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix(prefix);
        resolver.setSuffix(suffix);
        this.viewResolvers.add(resolver);
        return new UrlBasedViewResolverRegistration(resolver);
    }

对应的工程目录如下

这样我们就可以在Controller里面写代码了。举例如下:

    @GetMapping("/blogs.do")
    fun listAll(model: Model): String {
        val authentication = SecurityContextHolder.getContext().authentication
        model.addAttribute("currentUser", if (authentication == null) null else authentication.principal as UserDetails)
        val allblogs = blogService.findAll()
        model.addAttribute("blogs", allblogs)
        return "jsp/blog/list"
    }

其中,"jsp/blog/list"就是对应的jsp文件的位置。

本小节具体实例工程可以参考:

https://github.com/Jason-Chen-2017/restfeel

7.2 Spring Boot集成thymeleaf模板

Thymeleaf是一个XML/XHTML/HTML5模板引擎,可用于Web与非Web环境中的应用开发。它是一个开源的Java库,基于Apache License 2.0许可,由Daniel Fernández创建,该作者还是Java加密库Jasypt的作者。

Thymeleaf提供了一个用于整合Spring MVC的可选模块,在应用开发中,你可以使用Thymeleaf来完全代替JSP,或其他模板引擎,如Velocity、FreeMarker等。Thymeleaf的主要目标在于提供一种可被浏览器正确显示的、格式良好的模板创建方式,因此也可以用作静态建模。你可以使用它创建经过验证的XML与HTML模板。相对于编写逻辑或代码,开发者只需将标签属性添加到模板中即可。接下来,这些标签属性就会在DOM(文档对象模型)上执行预先制定好的逻辑。

Thymeleaf官方文档:
http://www.thymeleaf.org/documentation.html

示例模板:

<table>
  <thead>
    <tr>
      <th th:text="#{msgs.headers.name}">Name</td>
      <th th:text="#{msgs.headers.price}">Price</td>
    </tr>
  </thead>
  <tbody>
    <tr th:each="prod : ${allProducts}">
      <td th:text="${prod.name}">Oranges</td>
      <td th:text="${#numbers.formatDecimal(prod.price,1,2)}">0.99</td>
    </tr>
  </tbody>
</table>

让我们开始吧!

Step1. html页面加入头文件 相应的schema

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:th="http://www.thymeleaf.org">

Step2.主页面模板


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">

<th:block th:include="common/header :: head"></th:block>

<body>
<th:block th:include="common/header :: header"></th:block>

<div th:if="${not #lists.isEmpty(customers)}">
    <h2>Customer List</h2>
    <table id="customersTable" class="table table-striped">
        <thead>
        <tr>
            <th>Id</th>
            <th>FirstName</th>
            <th>LastName</th>
            <th>Created Time</th>
        </tr>
        </thead>

        <tbody>
        <tr th:each="customer : ${customers}">
            <td th:text="${customer.id}"><a href="/product/${product.id}">Id</a></td>
            <td th:text="${customer.firstName}">FirstName</td>
            <td th:text="${customer.lastName}">LastName</td>
            <td th:text="${customer.gmtCreated}">gmtCreated</td>
            <!--<td><a th:href="${ '/product/' + product.id}">View</a></td>-->
            <!--<td><a th:href="${'/product/edit/' + product.id}">Edit</a></td>-->
        </tr>
        </tbody>

    </table>

</div>

<th:block th:include="common/footer :: footer"></th:block>

</body>
</html>


Step3.include common模板说明

common/header.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en" th:fragment="head">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>

    <link href="http://cdn.jsdelivr.net/webjars/bootstrap/3.3.4/css/bootstrap.min.css"
          th:href="@{/webjars/bootstrap/3.3.4/css/bootstrap.min.css}"
          rel="stylesheet" media="screen"/>


    <link href="../../static/css/jquery.dataTables.min.css"
          th:href="@{css/jquery.dataTables.min.css}" rel="stylesheet" media="screen"/>

    <link href="../../static/css/mini_springboot.css"
          th:href="@{css/mini_springboot.css}" rel="stylesheet" media="screen"/>

    <title>Mini SpringBoot Tutorial</title>
</head>

<body>

<div th:fragment="header">
    <nav class="navbar navbar-default">
        <div class="container-fluid">
            <div class="navbar-header">
                <a class="navbar-brand" href="#" th:href="@{/}">Home</a>
                <ul class="nav navbar-nav">
                    <li><a href="#" th:href="@{/customers.do}">Customers</a></li>
                    <li><a href="#" th:href="@{/customer/new}">Create Customer</a></li>
                </ul>

            </div>
        </div>
    </nav>

    <div class="jumbotron">
        <div class="row text-center">
            <div class="">
                <h1 class="center">Springboot极简教程</h1>
                <h2>Mini SpringBoot Tutorial</h2>
                <h3>Spring Boot Kotlin Thymeleaf Web App</h3>
            </div>
        </div>
        <div class="row text-center">
            <iframe class="iframe" src="https://jason-chen-2017.github.io/Jason-Chen-2017/"></iframe>

            <!--![](../../static/images/home.png)-->
        </div>
    </div>

</div>

</body>
</html>

common/footer.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>


<div th:fragment="footer">

    <div class="footer">Springboot极简教程, Jason Chen, 2017</div>


    <script src="http://cdn.jsdelivr.net/webjars/jquery/2.1.4/jquery.min.js"
            th:src="@{/webjars/jquery/2.1.4/jquery.min.js}"></script>


    <script src="http://cdn.jsdelivr.net/webjars/bootstrap/3.3.4/js/bootstrap.min.js"
            th:src="@{/webjars/bootstrap/3.3.4/js/bootstrap.min.js}"></script>

    <script src="../../static/js/jquery.dataTables.min.js"
            th:src="@{js/jquery.dataTables.min.js}"></script>

    <script src="../../static/js/mini_springboot.js" th:src="@{js/mini_springboot.js}"></script>

</div>


</body>
</html>

重点看一下thymeleaf的语法设计风格。

写一个th:fragment="{id}"

<div th:fragment="footer">

    。。。

</div>

可以直接在其他页面 th:include

<th:block th:include="common/footer :: footer"></th:block>

Step4. 配置build.gradle,添加spring-boot-starter-thymeleaf

Spring Boot使用thymeleaf模板引擎的,只需要在build.gradle(pom.xml)加入依赖即可:

//thymeleaf
    compile("org.springframework.boot:spring-boot-starter-thymeleaf")

Step5. 新建标准springboot resources目录

Springboot web app有很多约定,根据这些约定,可以省去一大批繁冗的配置。请看标准的工程目录结构

.
├── META-INF
│   └── MANIFEST.MF
├── README.md
├── README_.md
├── build
│   └── kotlin-build
│       └── caches
│           └── version.txt
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── run.bat
├── run.sh
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   ├── kotlin
    │   │   └── jason
    │   │       └── chen
    │   │           └── mini_springboot
    │   │               ├── console
    │   │               │   └── HelloWorld.kt
    │   │               └── restful
    │   │                   ├── Application.kt
    │   │                   ├── biz
    │   │                   │   └── CustomerService.kt
    │   │                   ├── controller
    │   │                   │   └── CustomerController.kt
    │   │                   ├── entity
    │   │                   │   └── Customer.kt
    │   │                   └── utils
    │   │                       └── DateUtils.kt
    │   └── resources
    │       ├── application.properties
    │       ├── application.yml
    │       ├── static
    │       │   ├── css
    │       │   │   ├── jquery.dataTables.min.css
    │       │   │   └── mini_springboot.css
    │       │   ├── images
    │       │   │   ├── home.png
    │       │   │   ├── sort_asc.png
    │       │   │   ├── sort_both.png
    │       │   │   └── sort_desc.png
    │       │   └── js
    │       │       ├── jquery.dataTables.min.js
    │       │       └── mini_springboot.js
    │       └── templates
    │           ├── common
    │           │   ├── footer.html
    │           │   └── header.html
    │           ├── customers.html
    │           ├── index.html
    │           ├── productform.html
    │           ├── products.html
    │           └── productshow.html
    └── test
        ├── java
        ├── kotlin
        └── resources

30 directories, 35 files



Step5. 写Controller

package jason.chen.mini_springboot.restful.controller

import jason.chen.mini_springboot.restful.biz.CustomerService
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.ResponseBody

@Controller
class CustomerController(val customerService: CustomerService) {

    @GetMapping(value = "/")
    fun index(): String {
        return "index"
    }

    @GetMapping("/customers.do")
    fun listAll(model: Model): String {
        val allCustomers = customerService.findAll()
        model.addAttribute("customers", allCustomers)
        return "customers"
    }

    @GetMapping("/listCustomers")
    @ResponseBody
    fun listCustomers(model: Model) = customerService.findAll()

    @GetMapping("/{lastName}")
    @ResponseBody
    fun findByLastName(@PathVariable lastName: String)
            = customerService.findByLastName(lastName)
}

Step6.运行测试

运行./gradlew bootRun, 启动完毕后,访问http://127.0.0.1:9891/customers.do
效果如下

螢幕快照 2017-03-11 22.23.13.png

本章节工程源码:

https://github.com/MiniSpringBootTutorial/mini_springboot/tree/use_thymeleaf_2017.3.11

7.3 Spring Boot集成velocity模板

Velocity,中文翻译为:速度、速率、迅速。Velocity是一种Java模版引擎技术,该项目由Apache提出,由另外一种引擎技术Webmacro发展而来。Apache对它的定义是:一种基于Java的模板引擎,但允许任何人使用简单而强大的模板语言来引用定义在Java代码中的对象。目前,http://velocity.apache.org/download.cgi上面的最新的版本是1.7,还是2010-11-29发布的。现在已经不再维护了。

Velocity是MVC架构的中MV的实现,它的关注点在Model和View之间,作为它们的桥梁。

本节我们使用SpringBoot集成velocity开发一个极简的服务监控系统。

我们使用SpringBoot 1.4.5.RELEASE。数据库ORM层我们采用jpa。

默认情况下,Spring Boot会配置一个VelocityViewResolver,如果需要的是VelocityLayoutViewResolver,你可以自己创建一个名为velocityViewResolver的bean。你也可以将VelocityProperties实例注入到自定义视图解析器以获取基本的默认设置。

以下示例使用VelocityLayoutViewResolver替换自动配置的velocity视图解析器,并自定义layoutUrl及应用所有自动配置的属性:

@Bean(name = "velocityViewResolver")
public VelocityLayoutViewResolver velocityViewResolver(VelocityProperties properties) {
    VelocityLayoutViewResolver resolver = new VelocityLayoutViewResolver();
    properties.applyToViewResolver(resolver);
    resolver.setLayoutUrl("layout/default.vm");
    return resolver;
}

1.配置pom依赖

引入spring-boot-starter-parent

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <!-- Starter for building MVC web applications using Velocity views. Deprecated since 1.4 -->
        <version>1.4.5.RELEASE</version>
        <!--<version>1.5.2.RELEASE</version>-->
    </parent>

引入velocity-starter

<!--web容器支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>            
        </dependency>
   
        <!--Velocity starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-velocity</artifactId>
        </dependency>

默认配置下spring boot会从src/main/resources/templates目录中去找模板

引入jpa,mysql-connector

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

2.application.properties配置

# VELOCITY TEMPLATES (VelocityAutoConfiguration) 在SpringBoot
spring.velocity.templateEncoding=UTF-8
spring.velocity.properties.input.encoding=UTF-8
spring.velocity.properties.output.encoding=UTF-8
spring.velocity.resourceLoaderPath=classpath:/templates/
spring.velocity.suffix=.html

server.port=7001

# security
#security.user.name=admin
#security.user.password=admin

# Spring Boot log level
logging.config=logback.xml
logging.level.org.springframework.web: INFO

#mysql
spring.datasource.url = jdbc:mysql://127.0.0.1:3306/femon?useUnicode=true&characterEncoding=UTF8
spring.datasource.username = qa_conn
spring.datasource.password = qa_conn
spring.datasource.driverClassName = com.mysql.jdbc.Driver
# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = true
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = update
# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy

# stripped before adding them to the entity manager)
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect


3.Dao层代码示例

package com.femon.dao;

import java.util.Date;
import java.util.List;

import com.femon.entity.ServiceData;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;

/**
 * @author 一剑 2015年12月23日 下午9:34:15
 */
public interface ServiceDataDao extends PagingAndSortingRepository<ServiceData, Integer> {

    @Query("select h from ServiceData h where h.serviceId = ?1")
    List<ServiceData> findByServiceId(int serviceId, Pageable pageable);

    @Query("select h from ServiceData h where h.serviceId = ?1 and h.sampleTime between ?2 and ?3 ")
    List<ServiceData> findByServiceIdAndDay(int serviceId, Date dayStart, Date dayEnd, Pageable pageable);

    @Query("select h from ServiceData h where h.serviceId = ?1 and state=0 and h.sampleTime between ?2 and ?3 ")
    Page<ServiceData> findFailByServiceIdAndDay(int serviceId, Date dayStart, Date dayEnd, Pageable pageable);

    ServiceData save(ServiceData h);

}

4.Controller层代码示例

package com.femon.controller;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import com.alibaba.fastjson.JSON;

import com.femon.dao.ServiceDao;
import com.femon.dao.ServiceDataDao;
import com.femon.entity.Service;
import com.femon.entity.ServiceData;
import com.femon.result.ServiceDataResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author 一剑 2015年12月29日 下午5:23:22
 */
@Controller
public class ServiceController {
    @Autowired
    ServiceDataDao historyDao;
    @Autowired
    ServiceDao serviceDao;

    ......

    @RequestMapping(value = "/service", method = RequestMethod.GET)
    public ModelAndView list(Model model, @RequestParam(value = "page", defaultValue = "0", required = false) int page,
                             @RequestParam(value = "count", defaultValue = "10", required = false) int count,
                             @RequestParam(value = "order", defaultValue = "ASC", required = false)
                                 Sort.Direction direction,
                             @RequestParam(value = "sort", defaultValue = "gmtCreate", required = false)
                                 String sortProperty,
                             @RequestParam(value = "hostCode", defaultValue = "1", required = false) int hostCode) {
        ModelAndView modelAndView = new ModelAndView("/service");

        Page result = null;

        if (hostCode == 0) {
            result = serviceDao.findAll(new PageRequest(page, count, new Sort(direction, sortProperty)));
        } else {
            result = serviceDao.findByHost(hostCode, new PageRequest(page, count, new Sort(direction, sortProperty)));
        }
        long totalPages = result.getTotalPages();
        List<Integer> pageIndexList = new ArrayList<Integer>((int)totalPages);
        for (int i = 0; i < totalPages; i++) {
            pageIndexList.add(i);
        }
        int currentPage = page;
        List<Service> serviceList = result.getContent();
        model.addAttribute("serviceList", serviceList);
        model.addAttribute("pageIndexList", pageIndexList);
        model.addAttribute("currentPage", currentPage);
        model.addAttribute("currentHost", hostCode);

        return modelAndView;
    }

    ......

}

5.执行定时任务

简单的定时任务的执行,只需要使用@Scheduled注解即可。但是要在启动类上面加上注解@EnableScheduling。

支持cron表达式:

@Scheduled(cron="0 * * * * MON-FRI")

和fixedRate:

@Scheduled(fixedRate = 60000)

等使用方式。

代码示例

@Component
public class WatchService {
    @Scheduled(fixedRate = 60000)
    public void curlJob() {
        ...
    }
}

6.启动类代码

应用入口类:Application.java

package com.femon;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;

/**
 * @author 一剑
 */

@Configuration
@EnableAutoConfiguration
@ComponentScan(basePackages = "com.femon")
@EnableScheduling
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

7.运行测试

Run Application,你将看到如下的输出:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.4.5.RELEASE)

2017-04-05 14:08:35.456  INFO 93948 --- [           main] com.femon.Application                    : Starting Application on jacks-MacBook-Air.local with PID 93948 (/Users/jack/book/femon/target/classes started by jack in /Users/jack/book/femon)
2017-04-05 14:08:35.466  INFO 93948 --- [           main] com.femon.Application                    : No active profile set, falling back to default profiles: default
2017-04-05 14:08:35.742  INFO 93948 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@36c88a32: startup date [Wed Apr 05 14:08:35 CST 2017]; root of context hierarchy
2017-04-05 14:08:37.996  INFO 93948 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$e6b9fa58] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2017-04-05 14:08:38.673  INFO 93948 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 7001 (http)
2017-04-05 14:08:38.686  INFO 93948 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2017-04-05 14:08:38.687  INFO 93948 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.11
2017-04-05 14:08:38.811  INFO 93948 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2017-04-05 14:08:38.812  INFO 93948 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 3079 ms

.......

2017-04-05 14:08:45.135  INFO 93948 --- [           main] o.s.w.s.v.velocity.VelocityConfigurer    : ClasspathResourceLoader with name 'springMacro' added to configured VelocityEngine
2017-04-05 14:08:45.471  INFO 93948 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-04-05 14:08:45.486  INFO 93948 --- [           main] s.a.ScheduledAnnotationBeanPostProcessor : No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
2017-04-05 14:08:45.576  INFO 93948 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 7001 (http)
2017-04-05 14:08:45.582  INFO 93948 --- [           main] com.femon.Application                    : Started Application in 11.253 seconds (JVM running for 12.373)

访问:http://localhost:7001/

系统运行效果图:

服务列表
服务详情

源代码:https://github.com/EasySpringBoot/femon

7.4 Spring Boot集成freemarker模板

FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。

FreeMarker,其模板编写为FreeMarker Template Language(FTL),属于简单、专用的语言。

Freemarker基本语法

  • ${...}:FreeMarker将会输出真实的值来替换大括号内的表达式,这样的表达式被称为interpolation(插值)。
  • 注释:注释和HTML的注释也很相似,但是它们使用<#-- and -->来标识。不像HTML注释那样,FTL注释不会出现在输出中(不出现在访问者的页面中),因为FreeMarker会跳过它们。
  • FTL标签(FreeMarker模板的语言标签):FTL标签和HTML标签有一些相似之处,但是它们是FreeMarker的指令,是不会在输出中打印的。这些标签的名字以#开头。(用户自定义的FTL标签则需要使用@来代替#)

Freemarker特性[1]

  • 通用性

能够生成各种文本:HTML、XML、RTF、Java源代码等等。
  易于嵌入到产品中:轻量级;不需要Servlet环境。
  插件式模板载入器:可以从任何源载入模板,如本地文件、数据库等等。
  可以按所需生成文本:保存到本地文件;作为Email发送;从Web应用程序发送它返回给Web浏览器。

  • 模板语言

支持所有常用的指令:include、if/elseif/else、循环结构。
  在模板中创建和改变变量。
  几乎在任何地方都可以使用复杂表达式来指定值。
  命名的宏,可以具有位置参数和嵌套内容。
  名字空间有助于建立和维护可重用的宏库,或者将一个大工程分成模块,而不必担心名字冲突。
  输出转换块:在嵌套模板片段生成输出时,转换HTML转义、压缩、语法高亮等等;可以定义自己的转换。

  • 通用数据模型
      FreeMarker不是直接反射到Java对象,Java对象通过插件式对象封装,以变量方式在模板中显示。
      可以使用抽象(接口)方式表示对象(JavaBean、XML文档、SQL查询结果集等等),告诉模板开发者使用。方法,使其不受技术细节的打扰。

  • Web领域专业性
      在模板语言中内建处理典型Web相关任务(如HTML转义)的结构。
      能够集成到Model2 Web应用框架中作为JSP的替代。
      支持JSP标记库。
      为MVC模式设计:分离可视化设计和应用程序逻辑;分离页面设计员和程序员。

  • 智能的国际化和本地化
      字符集智能化(内部使用UNICODE)。
      数字格式本地化敏感。
      日期和时间格式本地化敏感。
      非US字符集可以用作标识(如变量名)。
      多种不同语言的相同模板。

  • XML处理能力
      <#recurse> 和<#visit>指令(2.3版本)用于递归遍历XML树。
      在模板中清楚和直接的访问XML对象模型 。

与JSP相比,FreeMarker的一个优点在于不能轻易突破模板语言开始编写Java代码,因此降低了领域逻辑漏进视图层的危险几率。

本节我们给出一个SpringBoot集成freemarker模板引擎的Demo,基于gradle构建。

1.添加spring-boot-starter-freemarker依赖到pom中

buildscript {
    ext {
        springBootVersion = '1.5.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.boot:spring-boot-starter-freemarker')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

2.写后端Controller

package com.example.controller;

import java.util.Date;
import java.util.Map;

import com.example.biz.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * Created by jack on 2017/4/5.
 */
@Controller
public class WelcomeController {

    @Value("${application.message:Hello,World}")
    private String message = "Hello,World";

    @Autowired
    BookService bookService;

    @GetMapping("/welcome")
    public String welcome(Map<String, Object> model) {
        model.put("time", new Date());
        model.put("message", this.message);
        model.put("books", bookService.findAll());
        return "welcome";
    }

}

3.写前端ftl代码

 <!DOCTYPE html>
<html lang="zh">
<body>
Date: ${time?date}
<br>
Time: ${time?time}
<br>
Message: ${message}
<div>
    <#list books as book>
        <li>书名: ${book.name}</li>
        <li>作者: ${book.author}</li>
        <li>出版社: ${book.press}</li>
    </#list>
</div>
</body>

</html>

4.运行测试

运行DemoApplication, 我们将看到如下日志:

2017-04-05 22:05:06.020  INFO 98366 --- [           main] com.example.DemoApplication              : Starting DemoApplication on jacks-MacBook-Air.local with PID 98366 (/Users/jack/book/demo/build/classes/main started by jack in /Users/jack/book/demo)

......

o.s.w.s.v.f.FreeMarkerConfigurer         : ClassTemplateLoader for Spring macros added to FreeMarker configuration
2017-04-05 22:05:08.848  INFO 98366 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-04-05 22:05:08.911  INFO 98366 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 5678 (http)
2017-04-05 22:05:08.931  INFO 98366 --- [           main] com.example.DemoApplication              : Started DemoApplication in 3.37 seconds (JVM running for 4.017)
2017-04-05 22:25:02.870  INFO 98366 --- [nio-5678-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2017-04-05 22:25:02.872  INFO 98366 --- [nio-5678-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2017-04-05 22:25:02.902  INFO 98366 --- [nio-5678-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 30 ms

浏览器访问:http://localhost:5678/welcome
我们将看到如下输出:

freemarker demo

本节工程源码 :

https://github.com/EasySpringBoot/HelloWorld/tree/freemarker_demo_2017.4.6

小结

参考资料:

1.http://baike.baidu.com/item/freemarker
2.http://freemarker.org/
3.http://velocity.apache.org/
4.http://baike.baidu.com/item/JSP/141543

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
27天前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
40 0
|
2月前
|
安全 Java 数据库
后端进阶之路——万字总结Spring Security与数据库集成实践(五)
后端进阶之路——万字总结Spring Security与数据库集成实践(五)
|
3月前
|
前端开发 Java 开发者
Spring Boot 3 集成 Thymeleaf
Thymeleaf是一款用于Web和独立环境的现代化服务器端Java模板引擎。它能够处理HTML、XML、JavaScript、CSS甚至纯文本。Thymeleaf的语法简单易懂,它允许开发者在模板中嵌入表达式,以便动态地渲染数据。
77 1
Spring Boot 3 集成 Thymeleaf
|
3月前
|
NoSQL Java Redis
Spring Boot集成Redisson详细介绍
Redisson是一个用于Java的分布式和高可用的Java对象的框架,它基于Redis实现。在Spring Boot应用程序中集成Redisson可以帮助我们更轻松地实现分布式锁、分布式对象、分布式集合等功能。本文将介绍如何在Spring Boot项目中集成Redisson,并展示一些基本用法。
478 2
Spring Boot集成Redisson详细介绍
|
6天前
|
安全 Java 应用服务中间件
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
24 0
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
|
8天前
|
XML Java C++
【Spring系列】Sping VS Sping Boot区别与联系
【4月更文挑战第2天】Spring系列第一课:Spring Boot 能力介绍及简单实践
【Spring系列】Sping VS Sping Boot区别与联系
|
1月前
|
Java 数据库连接 数据库
Spring Boot整合MyBatis Plus集成多数据源轻松实现数据读写分离
Spring Boot整合MyBatis Plus集成多数据源轻松实现数据读写分离
26 2
|
1月前
|
监控 NoSQL Java
Spring Boot集成Redis启动失败【Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.G】
Spring Boot集成Redis启动失败【Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.G】
|
1月前
|
存储 NoSQL Java
如何使用Spring Boot与Redis集成
2月更文挑战第12天】
53 0
|
2月前
|
XML 监控 druid
【Java专题_02】springboot+mybatis+pagehelper分页插件+druid数据源详细教程
【Java专题_02】springboot+mybatis+pagehelper分页插件+druid数据源详细教程

热门文章

最新文章