做一个Spring Boot小例子

在我的《Spring Boot 框架介绍和使用》里介绍了Spring Boot,但是没有例子。所以这一篇的主要内容就是来做一个小例子。结合我上面那篇一起看效果更佳。

运行项目

创建项目和上篇文章一样,我用了2.0的快照版本的Spring Boot,因为现版本1.5的Thymeleaf还是2.1的版本,比较旧。在Idea中运行Spring Boot项目不如Spring Tool Suite简单,因为在STS中直接保存文件即可触发devtools的重启,而在IDEA中只能手动点击build project命令。

在这里还遇到一点小情况。我原来不明白IDEA中有一个delegate to gradle有什么作用,就胡乱选上了。现在才发现,原来选中这个选项之后,在点击构建项目的时候不会调用IDEA自己的构建工具,而是使用gradle的构建。所以速度会更慢。如果使用IDEA的构建命令,速度会更快一些。

当然这样感觉还是稍微比STS慢一点。所以我又找到了另外一种方法,就是利用gradle的持续构建选项。首先打开一个终端,输入gradle assemble --continuous,这样gradle就会一直编译项目。如果检测到文件发生更改,就会自动重复编译项目,直到我们手动关闭了终端。然后在用gradle bootRun命令正常调试运行项目,当文件发生更改的时候gradle会自动编译项目,从而触发devtools自动重启服务器。

Spring Boot项目在运行的时候支持LiveReload,我们只要在浏览器上安装相应的插件并启用,服务器自动重启之后便会自动刷新浏览器。我们不必每次更改之后手动刷新了。

MVC

多个视图解析器

在Spring Boot中,错误页面可以放在下面的文件夹下。在使用Thymeleaf的时候,情况就变的稍微有点复杂了。

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 404.html
             +- <other templates>

我们希望将重复的模板代码抽出来组合成单独的文件,让其他页面引用。这样以后修改的时候只需要修改一处就可以更改所有页面的效果。但是Thymeleaf默认的代码块导入只能支持同级页面,像下面这样错误页面在单独一个文件夹、公用页面也在单独一个文件夹下的情况,默认的配置不能满足我们的需要。这时候就需要覆盖Spring Boot的自动配置了。

多层试图文件夹

经过一番查阅,我找到了解决办法。这种情况下需要配置的多个视图解析器。在Spring Boot中很简单,我们只需要定义自己的视图解析器,Spring就会自动屏蔽默认配置的。

配置代码如下。我们为代码段单独配置一个视图解析器。然后将这些视图解析器都添加到视图引擎中。这些必须都配置为Spring Bean。如果直接在templateEngine()中new视图解析器并添加,就会抛出ApplicationContext为空的异常。

最后要注意setCheckExistence方法也必须设置。不然的话视图解析器就会认为视图总是存在,所以渲染页面的时候会出现找不到视图文件的情况。所以设置了这个选项,解析器就会先检查文件是否存在,不存在的话就直接返回。这样另一个视图解析器就会寻找视图,最后我们两个文件夹下的视图就可以都找到了。

@Configuration
public class TemplateConfig {
    private boolean cacheable = false;
    private String templatesPrefix = "classpath:templates/";
    private String fragmentsPrefix = "classpath:templates/fragments/";
    private String suffix = ".html";
    private TemplateMode templateMode = TemplateMode.HTML;
    private String encoding = "UTF-8";
    private boolean checkExistence = true;

    @Bean
    public SpringResourceTemplateResolver templateResolver() {
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setPrefix(templatesPrefix);
        resolver.setSuffix(suffix);
        resolver.setCharacterEncoding(encoding);
        resolver.setCacheable(cacheable);
        resolver.setCheckExistence(checkExistence);
        return resolver;
    }

    @Bean
    public SpringResourceTemplateResolver fragmentResolver() {
        SpringResourceTemplateResolver fragmentResolver = new SpringResourceTemplateResolver();
        fragmentResolver.setPrefix(fragmentsPrefix);
        fragmentResolver.setSuffix(suffix);
        fragmentResolver.setCacheable(cacheable);
        fragmentResolver.setTemplateMode(templateMode);
        fragmentResolver.setCharacterEncoding(encoding);
        fragmentResolver.setCheckExistence(checkExistence);
        return fragmentResolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine() {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.addTemplateResolver(templateResolver());
        templateEngine.addTemplateResolver(fragmentResolver());
        templateEngine.setEnableSpringELCompiler(true);
        templateEngine.addDialect(new Java8TimeDialect());
        return templateEngine;
    }
}

MVC配置

spring自动配置的MVC基本够最基本的使用了,但是在做项目的话肯定要添加自己的配置。我们用Java配置的话也很简单。下面的例子很简单,添加了几个视图控制器,直接将请求和视图连在一起;还定义了两个格式化器。不知道为何Spring没有对这些新日期类的支持,所以我们只能自己写格式化器了。

如果我们只需要向下面添加几个自己的格式化器之类的,向下面这样继承WebMvcConfigurerAdapter即可。如果想完全控制Mvc的设置,可以添加@EnableMvc,这样Spring Boot的自动配置就会完全取消。

在此叨叨两句,如果是旧项目的话就算了。如果使用新项目的话我们在处理日期和时间的时候务必使用Java 8提供的新类,LocalDate、LocalDateTime这些,这些新类符合新标准,提供的新方法也更好用。

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/system-beans").setViewName("beans");
        registry.addViewController("/addUser").setViewName("addUser");
        registry.addViewController("/mvc").setViewName("mvc");
    }

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatter(new LocalDateFormatter());
        registry.addFormatter(new LocalDateTimeFormatter());
    }
}

class LocalDateFormatter implements Formatter<LocalDate> {

    @Override
    public LocalDate parse(String text, Locale locale) throws ParseException {
        return LocalDate.parse(text);
    }

    @Override
    public String print(LocalDate object, Locale locale) {
        return object.toString();
    }
}

class LocalDateTimeFormatter implements Formatter<LocalDateTime> {

    @Override
    public LocalDateTime parse(String text, Locale locale) throws ParseException {
        return LocalDateTime.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }

    @Override
    public String print(LocalDateTime object, Locale locale) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        return object.format(formatter);

    }
}

Spring Data

多数据源

在开发的时候我们一般有测试数据库和生产数据库,在测试的时候连接到测试数据库,部署的时候改为生产数据库。所以我们这里首先来配置一下多数据源。

application-test.properties中。其实这里什么也不写也可以,Spring 检测到H2 、HSQLDB或Derby的话就会自动创建一个内存嵌入式数据源。

# 数据库设置
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa

application-product.properties中,生产数据库为MySQL。

# 数据库设置
spring.datasource.username=root
spring.datasource.password=12345678
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

然后在application.properties中,激活配置即可。以后开发完成之后,改为product即可。

spring.profiles.active=test

使用Hikari连接池

Spring Boot会按照tomcat、HikariCP、DBCP2的顺序查找和使用连接池。不过我看了一下好像HikariCP的性能最好,所以这里我们直接使用HikariCP。

首先需要添加HikariCP的依赖。Spring Boot也包含了对HikariCP的版本号管理,不过它的版本比较低一点,所以我就干脆直接指定了最新的。

compile group: 'com.zaxxer', name: 'HikariCP', version: '2.6.1'

然后添加spring.datasource.type=com.zaxxer.hikari.HikariDataSource即可。

H2 web控制台

如果嵌入式数据库选择了H2,而且项目中添加了spring-boot-devtools。那么Spring还会启用H2的web控制台功能。

如果不需要这个功能可以直接关闭。

spring.h2.console.enabled=false

启用审计

最后我希望使用Spring Data的审计功能来帮我设置用户的注册时间。但是审计不是Spring Boot自动配置的内容。所以我们需要手动开启。

@Configuration
@EnableJpaAuditing
public class JpaConfig {
}

然后在实体类上添加EntityListeners注解。

@Entity
@EntityListeners(AuditingEntityListener.class)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    @Column(nullable = false, unique = true)
    private String username;
    @Column(nullable = false)
    private String password;
    @Column
    private String nickname;
    @Column
    private LocalDate birthday;
    @Column
    @CreatedDate
    private LocalDateTime registerTime;
}

这样,当我们处理数据的时候,注册时间就会由Spring自动填充了。

@Transactional
public interface UserRepository extends CrudRepository<User, Integer> {
    Optional<User> findByUsername(String username);
}

Actuator

在项目中添加Actuator的依赖项即可。这样Spring就会自动添加相关的路径映射。

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

为了保证安全,这些都需要验证才能访问。我们可以简单的关闭它,management.security.enabled=false。不过为了安全起见,实际开发中应该设置密码来保护这些敏感信息。

Beans可视化

本来我想将这些端点全做成可视化的,不过看了一些,大部分端点返回来的JSON都比较复杂,是个多层结构,所以最后只做了一个Beans的可视化。推荐一个Chrome插件,json-formatter。安装好之后,在查看这些端点的JSON,就不是糊成一团的了,而是格式化并且语法高亮的形式了。

Beans端点返回的JSON稍微有些奇怪,它是个类似下面这样的对象,也就是个数组,所以获取到数据之后必须使用data[0]这样的语法才能获取里层的对象。

[
  {
    context: "xxx",
    parent: "xxx",
    beans: [
        ...
...

然后页面就可以写成下面这样的。"[[@{/beans}]]"是Thymeleaf的语法,Thymeleaf引擎遇到它会转换为实际的URL。然后jquery获取到对象之后,使用了Knockout将数据绑定到页面上。详细使用方法请参考jQuery和Knockout的官方文档。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="_header::header('系统监控')">
</head>
<body>
<nav th:replace="_navbar::navbar"></nav>
<div class="container">
    <div class="row">
        <div class="col-md-12">
            <h2>Beans</h2>
            <h4><a th:href="@{/beans}">点击查看原格式</a></h4>
            <p><strong>context:</strong><span data-bind="text: context"></span></p>
            <p><strong>parent:</strong><span data-bind="text: parent"></span></p>
            <p><strong>数量:</strong>共有<span data-bind="text: beans.length"></span>个Beans</p>
            <table class="table table-bordered table-hover table-responsive table-striped">
                <thead>
                <tr>
                    <th>Beans</th>
                    <th>resource</th>
                    <th>scope</th>
                    <th>type</th>
                </tr>
                </thead>
                <tbody data-bind="foreach: beans">
                <tr>
                    <td data-bind="text: bean"></td>
                    <td data-bind="text: resource"></td>
                    <td data-bind="text: scope"></td>
                    <td data-bind="text: type"></td>
                </tr>
                </tbody>
            </table>
        </div>

    </div>
</div>
<div th:replace="_footer::footer"></div>
<script>
    $(document).ready(function () {
        $.getJSON("[[@{/beans}]]", function (data) {
            ko.applyBindings(data[0])
        })
    })
</script>
</body>
</html>

Hypermedia方式查看数据

如果我们添加 Spring HATEOAS的依赖,也就是下面这个。

compile 'org.springframework.hateoas:spring-hateoas'

然后启用Hypermedia功能。

endpoints.hypermedia.enabled=true

Spring就会在默认/actuator路径下生成一个发现页面,返回所有可用的端点和相应的URL。这个发现页面的URL可以使用endpoints.actuator.path设置,这个发现功能可以使用endpoints.actuator.enabled打开或关闭。

如果还存在HAL Browser的jar包,也就是添加下面的依赖,那么Spring还会开启一个可视化页面覆盖/actuator的JSON形式。可视化页面其实也没啥功能,会把所有的端点和输出以格式化之后的JSON形式输出,如果没安装JSON美化浏览器插件的话这个功能还是挺有用的。

 compile group: 'org.springframework.data', name: 'spring-data-rest-hal-browser', version: '2.6.1.RELEASE'

主页

用Markdown格式化

想了想也没啥写的了。最后就来说说Markdown把。我用的是marked。然后在resouces/statis/md/下建了markdown格式文件。然后页面可以写成类似这样的。同样是通过jQuery获取数据,然后转换为HTML。

<div class="container" id="content">

</div>
<div th:replace="_footer::footer"></div>
<script>
    $(document).ready(function () {
        $.get("[[@{/md/index.md}]]", function (data) {
            $("#content").html(marked(data));
        })
    })
</script>

完整的Spring Boot小例子代码在CSDN代码库上了,有兴趣的同学可以看看。由于项目是Gradle项目,所以可能有些同学不好编译打包。这里我还上传了完整的二进制程序Spring Boot小例子程序,可以直接使用java -jar XXX.jar来运行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容