04《Spring Boot 入门教程》使用模板引擎开发 Web 项目

栏目:资讯发布:2023-09-22浏览:8收藏

04《Spring Boot 入门教程》使用模板引擎开发 Web 项目,第1张

模板引擎这个词,咋听起来,有点高大上的意味。

实际上,模板引擎是非常平易近人的技术。譬如大家可能都比较熟悉的 JSP ,就是一种比较典型的模板引擎。

当浏览器将请求抛给控制器,控制器处理好数据后,就跳转 JSP 等模板引擎页面。注意在跳转的同时,还会将数据组装好,也交给模板引擎处理。

模板引擎会根据数据,和模板引擎的规则,动态生成 HTML 页面,最后返回给浏览器显示。

我们使用 Spring Boot 开发 Web 项目,大体上有两种方式。

第一种方式,是后端服务化的方式,也是当前的主流方式。前端是静态的 HTML 页面,通过 Ajax 请求 Spring Boot 的后端接口。 Spring Boot 返回数据一般采用 JSON 格式,前端接收后将数据显示。

第二种方式,是采取模板引擎的方式。前端的请求,到达 Spring Boot 的控制器后,控制器处理请求,然后将返回数据交给模板引擎。模板引擎负责根据数据生成 HTML 页面,最后将 HTML 返回给浏览器。

我个人比较推荐第一种方式,说一下该方式的几个优点:

本篇是讲模板引擎,也得说说模板引擎的优点,王婆卖瓜不能光夸草莓啊。模板引擎开发的页面,对搜索引擎 SEO 比较友好;还有就是简单的页面,如果用模板引擎开发速度比较快,毕竟模板化的方法,目的就是减少重复提高效率。

Spring Boot 支持的模板引擎种类很多,常见的有 FreeMarker 、 Thymeleaf 、 JSP 。

因为这些模板引擎使用的用户都不少,所以我们逐一介绍下其实现过程。

至于孰优孰劣,请各位看官自行评价。正所谓:尺有所短,寸有所长,各取所爱,万物生长!

本篇我们开发一个商品浏览项目实例。

此处说一个我个人的经验:在做一个项目或一个模块的时候,不要一开始就动手写代码,最好是谋定而后动。

我们作为程序员,实际上是整个程序世界的总指挥。应该先整体规划,再实现局部。这种总分型的开发方法便于我们理顺思路,提高编码效率!

好的,我们来思考下,实现商品浏览项目实例的整体流程:

整体流程

可以看到,我们是先建立了控制器方法和页面,再去实现其中的具体细节。这样可以让我们的思维保持连贯性和整体性,在做一些页面和方法较多的项目时,会感觉更加顺畅。

我们按整体流程,使用 FreeMarker 模板引擎,来实现商品浏览功能。

使用 Spring Initializr 创建项目,Spring Boot 版本选择 225 , Group 为 comimooc , Artifact 为 spring-boot-freemarker ,生成项目后导入 Eclipse 开发环境。

引入 Web 项目及 FreeMarker 模板相关的依赖项,代码如下:

实例:

创建控制器类,由于是商品相关的控制器,所以命名为 GoodsController ,代码如下:

实例:

我们具体解释下该类的作用。

我们 resource/templates 目录下新建商品页面 goodsftl ,先不必实现具体功能,代码如下:

实例:

此时我们启动项目,然后访问 http://127001:8080/goods ,即可显示对应页面内容。

定义商品类 GoodsDo 用来描述商品信息,注意 Do 表示数据模型对象(Data Object),代码如下:

实例:

然后我们编写服务类 GoodsService ,提供获取商品列表的方法。注意此处仅仅是演示模板引擎,并不需要访问数据库,直接返回一个指定内容的商品列表。

实例:

此时,我们的控制器就可以注入 GoodsService 类型的组件,然后调用其方法了。

实例:

注意 modeladdAttribute("goodsList", goodsServicegetGoodsList()); ,我们将商品列表相关的数据交给模板引擎去处理。

此时我们可以根据 FreeMarker 模板引擎,按模板规则显示商品信息了。

实例:

注意我们通过 FreeMarker 的模板语法,输出了商品列表信息。关于 FreeMarker 模板引擎更多的语法规则,感兴趣的同学可以后续查阅更多资料。

启动项目,打开浏览器访问 http://127001:8080/goods ,即可查看输出结果。

Thymeleaf 和 FreeMarker ,都是模板引擎,使用方法基本类似。此处我们仅仅是给出一个范例,不再做过多的解释。

使用 Spring Initializr 创建项目, Spring Boot 版本选择 225 , Group 为 comimooc , Artifact 为 spring-boot-thymeleaf ,生成项目后导入 Eclipse 开发环境。

引入 Web 项目及 Thymeleaf 模板相关的依赖项。

实例:

创建控制器类, GoodsController , Thymeleaf 直接使用 HTML 作为模板页面,故代码如下:

实例:

我们在 resource/templates 目录下新建商品页面 goodshtml ,先不必实现具体功能,代码如下:

实例:

此时我们启动项目,然后访问 http://127001:8080/goods ,即可显示对应页面内容。

商品类 GoodsDo ,服务类 GoodsService ,这两个类与上面没有区别直接放出代码。

实例:

实例:

好的,此时我们的控制器就可以注入 GoodsService 类型的组件,然后调用其方法了。

实例:

此时我们可以根据 Thymeleaf 模板引擎,按模板规则显示商品信息了。

实例:

注意我们通过 Thymeleaf 的模板语法,输出了商品列表信息。关于 Thymeleaf 模板引擎更多的语法规则,感兴趣的同学可以后续查阅更多资料。

启动项目,打开浏览器访问 http://127001:8080/goods ,即可查看输出结果。

到此,大家基本上也能发现,这两种方式除了模板页面文件内容不同,其他地方基本都是一模一样的。

也就是说,模板引擎主要负责通过一些模板标签,将控制器返回的数据解析为网页。

注意 Spring Boot 官方已经不推荐使用 JSP 了,确实操作起来也比较麻烦。但是由于 JSP 用户体量还是比较大的,所以此处还是简单演示下,开发步骤与 FreeMarker / Thymeleaf 基本一致。

使用 Spring Initializr 创建项目, Spring Boot 版本选择 225 , Group 为 comimooc , Artifact 为 spring-boot-jsp ,生成项目后导入 Eclipse 开发环境。

引入 Web 项目及 JSP 模板相关的依赖项。

实例:

创建控制器类, GoodsController ,代码如下:

实例:

手工添加 src/main/webapp 及子目录如下,同时目录下放一个 goodsjsp 用于测试。注意该目录是一个 Source Folder 源代码目录,不是普通文件夹目录。

spring-boot-jsp 项目结构

实例:

注意,我们还需要添加一个视图解析器,实现 JSP 页面往指定目录跳转。

实例:

此时我们启动项目,然后访问 http://127001:8080/goods ,即可显示对应页面内容。

商品类 GoodsDo ,服务类 GoodsService ,这两个类与上面没有区别直接放出代码。

实例:

实例:

好的,此时我们的控制器就可以注入 GoodsService 类型的组件,然后调用其方法了。

实例:

此时我们可以根据 JSP 模板引擎,按模板规则显示商品信息了。

实例:

注意我们通过 JSP 的模板语法,输出了商品列表信息。关于 JSP 模板引擎更多的语法规则,感兴趣的同学可以后续查阅更多资料。

启动项目,打开浏览器访问 http://127001:8080/goods ,即可查看输出结果。

最后大家应该也发现了, FreeMarker 和 Thymeleaf 的用法几乎是一模一样的,而 JSP 还需要手工添加一些目录和配置。

三种方式各有优劣, FreeMarker 模板语法比较简洁, Thymeleaf 可以直接使用 HTML 作为模板文件, JSP 用户群体广泛。

但是三种方式,都是一种模板引擎而已,将控制器返回数据转化为 HTML 页面显示,本质上没啥区别,大家对模板引擎有一个了解即可。

spring和spring boot区别如下:

1、Spring框架:

就像一个家族有众多衍生产品例如boot、security、jpa等等但他们的基础都是Spring的ioc和aopioc提供了依赖注入的容器aop解决了面向横切面的编程然后在此两者的基础上实现了其他延伸产品的高级功能。

2、spring boot:

是基于Servlet的一个MVC框架主要解决WEB开发的问题因为Spring的配置非常复杂各种XML、JavaConfig、hin处理起来比较繁琐于是为了简化开发者的使用,从而创造性地推出了Springboot,约定优于配置简化了spring的配置流程。

spring短语搭配:

Leaf Spring 钢板弹簧 ; 叶片弹簧 ; [机] 板弹簧 ; [机] 片簧

Early Spring 早春图 ; 早春 ; 早春二月 ; 烟花三月

Spring rolls 春卷 ; 脆皮春卷 ; 第三道春卷 ; 秋卷

Silent Spring 寂静的春天 ; 沉寂的春天 ; 无声的春天 ; 沉静的春天

Air Spring [机] 空气弹簧 ; 气垫 ; 空翻 ; 春秋航空

Spring Boot 提供了一套额外的工具,可以提升应用程序开发的体验,只用在项目中包含 spring-boot-devtools 模块就行。该模块会在开发模式下设置一些默认的属性,例如:禁用缓存、设置日志等级;还会监听 classpath 的变化,自动的重启应用。

spring-boot-devtools 给开发带来了很多的便利,这篇文章总结可它的一些用法。

要使用 devtools,需要先添加模块依赖,Maven 和 Gradle 的配置如下:

Maven:

Gradle:

运行完整打包的应用时,开发工具会被禁用。当使用 java -jar 或者特殊的类加载器运行时,会被认为是生产环境。在 Maven 中使用 <optional>true</optional> 标记该依赖时可选的,或者在 Gradle 中使用自定义的 developmentOnly 配置可以防止开发工具应用到项目的其他模块。

Spring Boot 中的一些库会通过缓存来提升性能。比如:模块引擎缓存已编译的模板,已避免重复解析模板文件;Spring MVC 在响应静态资源时设置 HTTP 缓存头。

启用缓存在开发环境中非常有用,但是在生产环境下却适得其反。因此 spring-boot-devtools 默认会为我们禁用掉缓存,而不用再去手动禁用了。

同时,开发工具还会将 Web 日志的等级设置为 DEBUG ,这样就可以看到更加详细的请求和响应信息。如果要记录所有请求详细信息(包括可能的敏感信息),可以启用 springhttplog-request-details 。

如果不想使用默认的配置,可以设置 springdevtoolsadd-properties 为 false 。

当 classpath 上的文件发生修改时,使用了 spring-boot-devtools 的应用会自动重启。在开发过程中,会是一个有用的功能,因为可以快速地看到修改导致的变化。

由于 DevTools 监视 classpath 资源,因此触发重新启动的唯一方法是更新 classpath。在 Eclipse 中,默认在修改文件并保存时会自动编译,从而触发 classpath 更新,而 IntelliJ 中需要手动构建项目( Build -> Build Project )才行。当然,IntelliJ 中也可以 设置自动编译 ,但我觉得没有必要。

Devtools 通过两个类加载器来完成重启,对于第三方库等不会发生修改的类,使用 base 类加载器,用户编辑的类则使用 restart 类加载器。当应用重启时, restart 类加载器会被丢弃然后创建一个新的,这样会加快重启的速度。如果觉得重启不够快,可以考虑使用 JRebel ,它通过重新载入类来实现热更新。

默认情况下,每次应用程序重新启动时,都会输出条件评估增量的报告。该报告显示了修改应用程序时自动配置的变化,例如:添加或删除 Bean 以及配置属性。

添加下面的配置可以关闭该日志:

某些资源在更改时不需要触发重启。默认情况下, /META-INF/maven 、 /META-INF/resources 、 /resources , /static 、 /public 或 /templates 中的资源发生修改时不会触发重启(但是会触发 live reload,见下文)。

如果想要自定义需要排除的目录,可以通过 springdevtoolsrestartexclude 属性配置(逗号分隔多个)。如果想要维持默认的配置额外的排除其他目录,可以通过 springdevtoolsrestartadditional-exclude 属性配置。

通过 springdevtoolsrestartadditional-paths 可以配置额外的路径以监听变化。

如果不想使用重启功能,可以通过 springdevtoolsrestartenabled 属性设置。大多数情况下,直接在 applicationproperties 中设置即可。如果想要完全禁用重启,需要在调用 SpringApplicationrun(…) 之前设置 springdevtoolsrestartenabled System 属性为 false :

如果使用不断编译已更改文件的 IDE,可能更喜欢仅在特定时间触发重新启动。我们可以使用“触发器文件”,这是一个特殊文件,当想要实际触发重新启动检查时必须修改该文件。更改文件只会触发检查,只有在 Devtools 检测到必须执行某些操作时才会重新启动。触发器文件可以手动更新,也可以使用 IDE 插件更新。

将 springdevtoolsrestarttrigger-file 属性设为触发文件路径。

spring-boot-devtools 模块包含一个嵌入式 LiveReload 服务器,可用于在资源更改时触发浏览器刷新。浏览器需要安装 LiveReload 插件 。

如果不想启用 LiveReload 服务器,设置 springdevtoolslivereloadenabled 属性为 false 。

一次只能运行一个 LiveReload 服务器。如果从 IDE 启动多个应用程序,则只有第一个具有 LiveReload 功能。

在 $HOME 目录下创建一个 spring-boot-devtoolsproperties 文件,在里面添加全局的 devtools 设置。添加到此文件的任何属性都会应用到本机所有使用 devtools 的 Spring Boot 应用程序中。

你先要了解spring-boot是怎么出来的,你就不会问这种问题。以前的框架,如ssm,struts的配置文件一堆,spring的配置文件一堆,mbybatis/ibatis的配置文件一堆,各种配置文件xml一大堆。终于有人说,受不了了,整个破项目要这么多配置文件,烦死了,springboot就出生了。就是简化了例行配置和应用程序配置,利用starter把之前的一些东西自动化了。

只要你学Java,你基本上就绕不开spring,springMVC是spring的一个组件,spring的对象管理,切面编程,IOC,DI,都成基本操作了。你学了springboot,还是跟这些东西打交道

1 概述

笔者从2014年开始接触SaaS(Software as a Service),即多租户(或多承租)软件应用平台;并一直从事相关领域的架构设计及研发工作。机缘巧合,在笔者本科毕业设计时完成了一个基于SaaS的高效财务管理平台的课题研究,从中收获颇多。最早接触SaaS时,国内相关资源匮乏,唯一有的参照资料是《互联网时代的软件革命:SaaS架构设计》(叶伟等著)一书。最后课题的实现是基于OSGI(Open Service Gateway Initiative)Java动态模块化系统规范来实现的。

时至今日,五年的时间过去了,软件开发的技术发生了巨大的改变,笔者所实现SaaS平台的技术栈也更新了好几波,真是印证了那就话:“山重水尽疑无路,柳暗花明又一村”。基于之前走过的许多弯路和踩过的坑,以及近段时间有许多网友问我如何使用Spring Boot实现多租户系统,决定写一篇文章聊一聊关于SaaS的硬核技术。

说起SaaS,它只是一种软件架构,并没有多少神秘的东西,也不是什么很难的系统,我个人的感觉,SaaS平台的难度在于商业上的运营,而非技术上的实现。就技术上来说,SaaS是这样一种架构模式:它让多个不同环境的用户使用同一套应用程序,且保证用户之间的数据相互隔离。现在想想看,这也有点共享经济的味道在里面。

笔者在这里就不再深入聊SaaS软件成熟度模型和数据隔离方案对比的事情了。今天要聊的是使用Spring Boot快速构建独立数据库/共享数据库独立Schema的多租户系统。我将提供一个SaaS系统最核心的技术实现,而其他的部分有兴趣的朋友可以在此基础上自行扩展。

2 尝试了解多租户的应用场景

假设我们需要开发一个应用程序,并且希望将同一个应用程序销售给N家客户使用。在常规情况下,我们需要为此创建N个Web服务器(Tomcat),N个数据库(DB),并为N个客户部署相同的应用程序N次。现在,如果我们的应用程序进行了升级或者做了其他任何的改动,那么我们就需要更新N个应用程序同时还需要维护N台服务器。接下来,如果业务开始增长,客户由原来的N个变成了现在的N+M个,我们将面临N个应用程序和M个应用程序版本维护,设备维护以及成本控制的问题。运维几乎要哭死在机房了…

为了解决上述的问题,我们可以开发多租户应用程序,我们可以根据当前用户是谁,从而选择对应的数据库。例如,当请求来自A公司的用户时,应用程序就连接A公司的数据库,当请求来自B公司的用户时,自动将数据库切换到B公司数据库,以此类推。从理论上将没有什么问题,但我们如果考虑将现有的应用程序改造成SaaS模式,我们将遇到第一个问题:如果识别请求来自哪一个租户?如何自动切换数据源?

3 维护、识别和路由租户数据源

我们可以提供一个独立的库来存放租户信息,如数据库名称、链接地址、用户名、密码等,这可以统一的解决租户信息维护的问题。租户的识别和路由有很多种方法可以解决,下面列举几个常用的方式:

解决了上述问题后,我们再来看看如何获取客户端传入的租户信息,以及在我们的业务代码中如何使用租户信息(最关键的是DataSources的问题)。

我们都知道,在启动Spring Boot应用程序之前,就需要为其提供有关数据源的配置信息(有使用到数据库的情况下),按照一开始的需求,有N个客户需要使用我们的应用程序,我们就需要提前配置好N个数据源(多数据源),如果N<50,我认为我还能忍受,如果更多,这样显然是无法接受的。为了解决这一问题,我们需要借助Hibernate 5提供的动态数据源特性,让我们的应用程序具备动态配置客户端数据源的能力。简单来说,当用户请求系统资源时,我们将用户提供的租户信息(tenantId)存放在ThreadLoacal中,紧接着获取TheadLocal中的租户信息,并根据此信息查询单独的租户库,获取当前租户的数据配置信息,然后借助Hibernate动态配置数据源的能力,为当前请求设置数据源,最后之前用户的请求。这样我们就只需要在应用程序中维护一份数据源配置信息(租户数据库配置库),其余的数据源动态查询配置。接下来,我们将快速的演示这一功能。

4 项目构建

我们将使用Spring Boot 215版本来实现这一演示项目,首先你需要在Maven配置文件中加入如下的一些配置:

然后提供一个可用的配置文件,并加入如下的内容:

接下来,我们需要关闭Spring Boot自动配置数据源的功能,在项目主类上添加如下的设置:

最后,让我们看看整个项目的结构:

5 实现租户数据源查询模块

我们将定义一个实体类存放租户数据源信息,它包含了租户名,数据库连接地址,用户名和密码等信息,其代码如下:

持久层我们将继承JpaRepository接口,快速实现对数据源的CURD操作,同时提供了一个通过租户名查找租户数据源的接口,其代码如下:

业务层提供通过租户名获取租户数据源信息的服务(其余的服务各位可自行添加):

接下来是配置自定义的数据源,其源码如下:

在改配置类中,我们主要提供包扫描路径,实体管理工程,事务管理器和数据源配置参数的配置。

6 实现租户业务模块

在此小节中,租户业务模块我们仅提供一个用户登录的场景来演示SaaS的功能。其实体层、业务层和持久化层根普通的Spring Boot Web项目没有什么区别,你甚至感觉不到它是一个SaaS应用程序的代码。

首先,创建一个用户实体User,其源码如下:

业务层提供了一个根据用户名检索用户信息的服务,它将调用持久层的方法根据用户名对租户的用户表进行检索,如果找到满足条件的用户记录,则返回用户信息,如果没有找到,则返回null;持久层和业务层的源码分别如下:

7 配置拦截器

我们需要提供一个租户信息的拦截器,用以获取租户标识符,其源代码和配置拦截器的源代码如下:

8 维护租户标识信息

在这里,我们使用ThreadLocal来存放租户标识信息,为动态设置数据源提供数据支持,该类提供了设置租户标识、获取租户标识以及清除租户标识三个静态方法。其源码如下:

9 动态数据源切换

要实现动态数据源切换,我们需要借助两个类来完成,CurrentTenantIdentifierResolver和AbstractDataSourceBasedMultiTenantConnectionProviderImpl。从它们的命名上就可以看出,一个负责解析租户标识,一个负责提供租户标识对应的租户数据源信息。

首先,我们需要实现CurrentTenantIdentifierResolver接口中的resolveCurrentTenantIdentifier()和validateExistingCurrentSessions()方法,完成租户标识的解析功能。实现类的源码如下:

有了租户标识符解析类之后,我们需要扩展租户数据源提供类,实现从数据库动态查询租户数据源信息,其源码如下:

最后,我们还需要提供租户业务模块数据源配置,这是整个项目核心的地方,其代码如下:

10 应用测试

最后,我们通过一个简单的登录案例来测试本次课程中的SaaS应用程序,为此,需要提供一个Controller用于处理用户登录逻辑。在本案例中,没有严格的对用户密码进行加密,而是使用明文进行比对,也没有提供任何的权限认证框架,知识单纯的验证SaaS的基本特性是否具备。登录控制器代码如下:

在启动项目之前,我们需要为主数据源创建对应的数据库和数据表,用于存放租户数据源信息,同时还需要提供一个租户业务模块数据库和数据表,用来存放租户业务数据。一切准备就绪后,启动项目,在浏览器中输入:http://localhost:8080/loginhtml

在登录窗口中输入对应的租户名,用户名和密码,测试是否能够正常到达主页。可以多增加几个租户和用户,测试用户是否正常切换到对应的租户下。

总结

注解:

该注解主要是用于rest风格的搭配使用,在请求路径中不再以k : v的形式给出请求参数与值;而是直接给定一个值。如果方法参数是一个Map<String, String>将会包含路径中所有的变量与值。

访问 :浏览器输入路径变量即可,以下是rest风格的get请求的展示,直接在地址栏发起请求就是一个get请求

获取所有访问路径上的请求参数:localhost:8080/ car/{id}/owner/{username}age=19&inters=

该注解主要用于获取请求头header中的数据,客户端请求之后可以拿到一些头部携带的参数。支持传统的SpringMVC,也支持WebFlux响应式。如果方法参数是一个Map<String, String>将会包含所有的请求头与值

测试:

主要用于获取请求参数名称,设置参数是否可有可无以及默认值。

@RequestParam注解详解地址: blogcsdnnet/weixin_4380 …

主要用于获取Cookie值

主要用在 请求转发 时,如果页面无法直接跳转(如WEB-INF下的success页面)可以使用转发的手段。当进行转发时可以在请求中携带上请求的参数,转发会携带上一次请求的参数(一次完整的请求包括转发)

由于是同一次请求,因此也可以直接拿到原生的HttpServletRequest,然后从这里面拿参数和属性也都是可以的获取request域属性。

下面就体现了一种转发的思想

结果

主要获取表单或者ajax提交的内容,将表单中提交的参数与值获取全部获取出来。即获取请求体所以请求必须是post请求--@PostMapping,一般情况下都会使用@RequestBody注解将参数映射到pojo类的能力,但是要保证前后传入的参数名是一样的

上述是最常见的三种请求方式;而矩阵变量请求是今天的主角一种新的请求风格,严格来说矩阵变量的请求需要用到rest风格但是又不同于rest

面试官:页面开发当中把cookie禁用了,session里面的内容怎么使用(找到)???

由上面源码知道首先可以知道这个注解是一个修饰在参数上的注解,并且可以在运行时被JVM虚拟机加载到。

由于SpringBoot中默认并没有开启矩阵变量的支持,直接关闭了矩阵变量。因此在使用的时候我们需要对SpringBoot自动装配的Bean对象进行手动的配置更改。

真正使得无法使用矩阵变量的原因是UrlPathHelper类中的removeSemicolonContent默认为true,即移除分号内容。

方法 :参照第一章说的SpringBoot三个自配置规则,利@configuration+WebMvcConfigurer自定义规则即可,因此为了开启矩阵变量的使用必须实现WebMvcConfigurer接口,完成对configurePathMatch方法的手动实现。而WebMvcConfigurer接口中所有的方法都是default的默认方法,因此可以使用适配器模式单独对该方法进行重写。所以可以在配置类中手动向IOC容器中配置一个Bean对象,该对象只对这一个方法进行重写实现即可。

重写如下

首先必须手动配置对矩阵变量的支持,其次矩阵变量是绑定在rest路径变量中的

上述两种路径都会使用同一个Controller进行处理,path拿到的就是 / 到第一个 ; 号之间的路径。

Gage绑定{gege}中的age参数,Dage绑定{didi}中的age;这样就能准确的区分出到底获取哪个age。

摘自:blogcsdnnet/qq_20957669…

现在大多数互联网项目都是采用前后端分离的方式开发,前端人员负责页面展示和数据获取,后端负责业务逻辑处理和接口封装。当与前端交互的过程当中,常用json数据与前端进行交互,这样想取出前端传送过来的json数据的时候,就需要用到@RequestBody这个注解。@RequestBody注解用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容转换为json、xml等格式的数据并绑定到controller方法的参数上。

提交方式为 POST 时,

获取参数的几种常用注解

ElasticSearch被命名为大数据搜索引擎,在文件检索、数据存储方面具有天然的优势。而SpringBoot作为服务整合中间件,在服务组装方面是一款万能粘合器,本文主要提供Spring Boot整合ElasticSearch基本增删改示例。

ElasticSearch安装过程可参考博主之前笔文:

https://wwwtoutiaocom/i6827758978567504392/

ElasticSearch基本介绍可参考博主之前笔文:

https://wwwtoutiaocom/i6884427730096488971/

Maven工程引入:

orgspringframeworkboot

spring-boot-starter-data-elasticsearch

注意:ElasticSearch版本号与Spring Boot版本号是有关联的,本文笔者Spring Boot版本号是216,因此安装的ElasticSearch版本号是770。

package comopendigeneratorelasticsearchmodel;

import lombokData;

import orgspringframeworkdataannotationId;

import orgspringframeworkdataelasticsearchannotationsDocument;

import orgspringframeworkdataelasticsearchannotationsField;

import orgspringframeworkdataelasticsearchannotationsFieldType;

import javautilList;

@Data

@Document(indexName="post", type="post",shards=1,replicas = 0)

public class Post {

@Id

private String id;

private String title;

@Field(type=FieldTypeNested)

private List tags;

}

package comopendigeneratormodel;

import lombokData;

import orgspringframeworkdataannotationId;

import orgspringframeworkdataelasticsearchannotationsDocument;

import orgspringframeworkdataelasticsearchannotationsField;

import orgspringframeworkdataelasticsearchannotationsFieldType;

@Data

@Document(indexName = "book", type = "_doc", shards = 1, replicas = 0)

public class BookBean {

@Id

private String id;

@Field(type = FieldTypeKeyword)

private String title;

@Field(type = FieldTypeKeyword)

private String author;

@Field(type = FieldTypeKeyword)

private String postDate;

public BookBean(){}

public BookBean(String id, String title, String author, String postDate){

thisid=id;

thistitle=title;

thisauthor=author;

thispostDate=postDate;

}

public String getId() {

return id;

}

public void setId(String id) {

thisid = id;

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

thistitle = title;

}

public String getAuthor() {

return author;

}

public void setAuthor(String author) {

thisauthor = author;

}

public String getPostDate() {

return postDate;

}

public void setPostDate(String postDate) {

thispostDate = postDate;

}

@Override

public String toString() {

return "BookBean{" +

"id='" + id + ''' +

", title='" + title + ''' +

", author='" + author + ''' +

", postDate='" + postDate + ''' +

'}';

}

}

package comopendigeneratorelasticsearchmapper;

import comopendigeneratorelasticsearchmodelPost;

import orgspringframeworkdatadomainPage;

import orgspringframeworkdatadomainPageable;

import orgspringframeworkdataelasticsearchrepositoryElasticsearchRepository;

public interface PostRepository extends ElasticsearchRepository {

Page findByTitle(String title, Pageable pageable);

}

package comopendigeneratordao;

import comopendigeneratormodelBookBean;

import orgspringframeworkdatadomainPage;

import orgspringframeworkdatadomainPageable;

import orgspringframeworkdataelasticsearchrepositoryElasticsearchRepository;

/

接口关系:

ElasticsearchRepository --> ElasticsearchCrudRepository --> PagingAndSortingRepository --> CrudRepository

/

public interface BookRepository extends ElasticsearchRepository {

//Optional findById(String id);

Page findByAuthor(String author, Pageable pageable);

Page findByTitle(String title, Pageable pageable);

}

使用了ElasticSearch里面的操作工具对数据进行操作。

package comopendigeneratorelasticsearchservice;

import comopendigeneratorelasticsearchmodelPost;

import orgspringframeworkdatadomainPage;

import orgspringframeworkdatadomainPageRequest;

import javautilOptional;

public interface PostService {

Post save(Post post);

Optional findOne(String id);

Iterable findAll();

Page findByTitle(String title, PageRequest pageRequest);

}

指定操作对象:

package comopendigeneratorelasticsearchserviceimpl;

import comopendigeneratorelasticsearchmapperPostRepository;

import comopendigeneratorelasticsearchmodelPost;

import comopendigeneratorelasticsearchservicePostService;

import orgspringframeworkbeansfactoryannotationAutowired;

import orgspringframeworkdatadomainPage;

import orgspringframeworkdatadomainPageRequest;

import orgspringframeworkstereotypeService;

import javautilOptional;

@Service

public class PostServiceImpl implements PostService {

@Autowired

private PostRepository postRepository;

@Override

public Post save(Post post) {

postRepositorysave(post);

return post;

}

@Override

public Optional findOne(String id) {

return postRepositoryfindById(id);

}

@Override

public Iterable findAll() {

return postRepositoryfindAll();

}

@Override

public Page findByTitle(String title, PageRequest pageRequest) {

return postRepositoryfindByTitle(title, pageRequest);

}

}

package comopendigeneratorservice;

import comopendigeneratormodelBookBean;

import orgspringframeworkdatadomainPage;

import orgspringframeworkdatadomainPageRequest;

import javautilList;

import javautilOptional;

public interface BookService {

Optional findById(String id);

BookBean save(BookBean blog);

void delete(BookBean blog);

Optional findOne(String id);

List findAll();

Page findByAuthor(String author, PageRequest pageRequest);

Page findByTitle(String title, PageRequest pageRequest);

}

package comopendigeneratorserviceimpl;

import comopendigeneratordaoBookRepository;

import comopendigeneratormodelBookBean;

import comopendigeneratorserviceBookService;

import orgspringframeworkbeansfactoryannotationAutowired;

import orgspringframeworkbeansfactoryannotationQualifier;

import orgspringframeworkdatadomainPage;

import orgspringframeworkdatadomainPageRequest;

import orgspringframeworkstereotypeService;

import javautilList;

import javautilOptional;

@Service("blogService")

public class BookServiceImpl implements BookService {

@Autowired

@Qualifier("bookRepository")

private BookRepository bookRepository;

@Override

public Optional findById(String id) {

//CrudRepository中的方法

return bookRepositoryfindById(id);

}

@Override

public BookBean save(BookBean blog) {

return bookRepositorysave(blog);

}

@Override

public void delete(BookBean blog) {

bookRepositorydelete(blog);

}

@Override

public Optional findOne(String id) {

return bookRepositoryfindById(id);

}

@Override

public List findAll() {

return (List ) bookRepositoryfindAll();

}

@Override

public Page findByAuthor(String author, PageRequest pageRequest) {

return bookRepositoryfindByAuthor(author,pageRequest);

}

@Override

public Page findByTitle(String title, PageRequest pageRequest) {

return bookRepositoryfindByTitle(title,pageRequest);

}

}

会调用ElasticSearch底层功能进行操作,能够实现ElasticSearch增删改操作。

连接服务器ElasticSearch连接配置:

spring:

data:

elasticsearch:

cluster-name: es2018

cluster-nodes: 101728154:9300

repositories:

enabled: true

elasticsearch:

jest:

uris: http:// 101728154:9200

增加连接配置,这样就能连接ElasticSearch客户端了。

ElasticSearch在Controller操作:

package comopendigeneratorcontroller;

import comopendigeneratormodelBookBean;

import comopendigeneratorserviceBookService;

import orgspringframeworkbeansfactoryannotationAutowired;

import orgspringframeworkwebbindannotationPathVariable;

import orgspringframeworkwebbindannotationRequestMapping;

import orgspringframeworkwebbindannotationResponseBody;

import orgspringframeworkwebbindannotationRestController;

import javautilOptional;

@RestController

public class ElasticController {

@Autowired

private BookService bookService;

@RequestMapping("/book/{id}")

@ResponseBody

public BookBean getBookById(@PathVariable String id){

Optional opt =bookServicefindById(id);

BookBean book=optget();

Systemoutprintln(book);

return book;

}

@RequestMapping("/save")

@ResponseBody

public void Save(){

SystemsetProperty("essetnettyruntimeavailableprocessors", "false");

BookBean book=new BookBean("1","ES入门教程","程裕强","2018-10-01");

Systemoutprintln(book);

bookServicesave(book);

}

}

package comopendigeneratorelasticsearchcontroller;

import comopendigeneratorelasticsearchmodelPost;

import comopendigeneratorelasticsearchmodelTag;

import comopendigeneratorelasticsearchservicePostService;

import ioswaggerannotationsApi;

import ioswaggerannotationsApiOperation;

import orgspringframeworkbeansfactoryannotationAutowired;

import orgspringframeworkdatadomainPage;

import orgspringframeworkdatadomainPageRequest;

import orgspringframeworkwebbindannotationGetMapping;

import orgspringframeworkwebbindannotationRestController;

import javautilArrayList;

import javautilArrays;

import javautilList;

@Api(tags="ElasticSearch示例")

@RestController

public class PostController {

@Autowired

private PostService postService;

@ApiOperation(value="得到ES结果")

@GetMapping(value="getList")

public List<page > getList()</page

{

List<page > list = new ArrayList<page >();</page </page

Tag tag = new Tag();

tagsetId("1");

tagsetName("tech");

Tag tag2 = new Tag();

tag2setId("2");

tag2setName("elasticSearch");

Post post = new Post();

postsetId("1");

postsetTitle("This is post");

postsetTags(ArraysasList(tag,tag2));

postServicesave(post);

Post post2 = new Post();

post2setId("2");

post2setTitle("Biding2");

post2setTags(ArraysasList(tag));

postServicesave(post2);

Page posts = postServicefindByTitle("This is post", new PageRequest(0, 10));

Page posts2 = postServicefindByTitle("Biding2", new PageRequest(0,10));

Page posts3 = postServicefindByTitle("Biding2", new PageRequest(0,10));

listadd(posts);

listadd(posts2);

listadd(posts3);

return list;

}

}

控制台打印:

1 Spring Boot是什么,解决哪些问题

     1) Spring Boot使编码变简单

     2) Spring Boot使配置变简单

     3) Spring Boot使部署变简单

     4) Spring Boot使监控变简单

     5) Spring Boot的不足

2 Spring Boot在平台中的定位,相关技术如何融合

     1) SpringBoot与SEDA +MicroService + RESTful

     2) SpringBoot与Mock

3 采用了SpringBoot之后,技术管理应该如何进行

首先,我们来看一下spring boot是什么,它帮助我们解决了哪些问题:

SpringBoot是伴随着Spring40诞生的;

从字面理解,Boot是引导的意思,因此SpringBoot帮助开发者快速搭建Spring框架;

SpringBoot帮助开发者快速启动一个Web容器;

SpringBoot继承了原有Spring框架的优秀基因;

SpringBoot简化了使用Spring的过程。

Spring由于其繁琐的配置,一度被人认为“配置地狱”,各种XML、Annotation配置,让人眼花缭乱,而且如果出错了也很难找出原因。

Spring Boot更多的是采用Java Config的方式,对Spring进行配置。

可以看到,采用了spring-boot-start-actuator之后,直接以REST的方式,获取进程的运行期性能参数。

当然这些metrics有些是有敏感数据的,spring-boot-start-actuator为此提供了一些Basic Authentication认证的方案,这些方案在实际应用过程中也是不足的。

Spring Boot作为一个微框架,离微服务的实现还是有距离的。

没有提供相应的服务发现和注册的配套功能,自身的acturator所提供的监控功能,也需要与现有的监控对接。没有配套的安全管控方案,对于REST的落地,还需要自行结合实际进行URI的规范化工作。

下面,我们研究一下Spring Boot在平台中的定位,相关技术如何融合。

上图比较复杂,整体是采用SEDA,也就是Stage-EDA。可以看到,整体是以处理顺序进行展示的,响应过程类似。在处理过程中,主要会有前置过滤,核心功能处理,后置过滤几大部分。

图中的过滤器都是可插拔式的,并且可以根据实际场景进行扩展开发。每个过滤器都是Stage,比如ClientInstance合法性检查、调用鉴权、解密、限流等等。

一个请求Stage与Stage的转换,实现上是切换不同的线程池,并以EDA的方式驱动。

对于业务逻辑的开发者而言,只需要关心CORE部分的业务逻辑实现,其他的非功能都由框架进行统一实现。

Mock不应当再是测试的专有名词了,当然对于测试这个角色而言,mockito这样的工具,依然可以为他们提升不少效率。

SpringBoot为创建REST服务提供了简便的途径,相比之下,采用阿里的dubbo在做多团队、多进程联调时,mock的难度就陡增。

Mock是解耦并行开发的利器,在理性的情况下,软件从开发期Mock联调,到开发与开发的真实联调,只需要切换一个依赖的域名即可,比如:

mockURI:http://mockservicenet/v1/functionparam1=value1

devURI:http://devservicenet/v1/functionparam1=value1

而上述的域名切换,只需要在开发期定义好一个配置项,在做环境切换的时候自动注入即可,省时、省心、省力。

如上图和docker的集成可以有AB两种方案:

A方案的核心是,把docker作为操作系统环境的交付基线,也就是不同的fat jar 使用相同的操作系统版本、相同的JVM环境。但对于docker image来说都是一样的。

B方案的核心是,不同的fat jar,独立的编译为docker image,在启动时直接启动带有特定版本的image。

A相比与B方案的特点是对于docker registry(也就是docker的镜像仓库)的依赖性较低,对于前期编译过程的要求也较低。

采用了Spring Boot之后,技术管理应该如何进行?

正因为Spring Boot是与Spring一脉相承的,所以对于广大的Java开发者而言,对于Spring的学习成本几乎为零。

在实践Spring Boot时学习重点,或者说思维方式改变的重点在于:

1)对于REST的理解,这一点尤为重要,需要从设计、开发多个角色达成共识,很多时候都是对于HTTP 11协议以及REST的精髓不理解,导致REST被“盲用”而产生一些不好的效果。

2)对于YAML的理解和对于JavaConfig的理解,这两点相对较为简单,本质上是简化了xml文件,并提供等价的配置表述能力。

1 丰富的工具链为SpringBoot的推广带来了利好。

2 SpringBoot的工具链主要来自于两个方面:

    1) 原有Spring积累的工具链;

    2) SpringMVC或者其他REST框架使用HTTP协议,使得HTTP丰富的工具成为SpringBoot天然的资源。

SpringBoot自身对于前面提到的配置文件:“applicationyml”提供了多个“Profile”,可以便于开发者描述不同环境的配置,这些配置例如数据库的连接地址、用户名和密码。

但是对于企业用户而言,把不同环境的配置,写到同一个配置文件中,是极其不安全的,是一个非常危险的动作。

有一个经常被提及的例子是,随着开源的进行,很多互联网公司,都由于把相关的代码提交到github之类的开源代码社区,并且没有对代码进行严格的配置审查,导致一些”password”被公开。有些不良用心的人,就利用搜索工具,专门去挖掘这些关键字,进而导致数据库被“拖库”。

所以对于企业用户,更多的应该是采用集中式的配置管理系统,将不同环境的配置严格区分地存放。

虽然SpringBoot的actuator自身提供了基于“用户名+口令”的最简单的认证方式,但它保护的是对框架自身运行期的性能指标敏感数据的最基本的保护。这种保护在实际应用过程中,“用户名+口令”的管理是缺乏的,“用户名+口令”的安全配置过程是缺失的。

SpringBoot也不提供对于我们自己开发的功能的任何防护功能。

一般来讲,一个安全的信道(信息传输的通道),需要通信双方在进行正式的信息传输之前对对方进行身份认证,服务提供方还需要在此基础之上,对请求方的请求进行权限的校验,以确保业务安全。这些内容也需要基于SpringBoot进行外围的安全扩展,例如采用前面提到的S-EDA进行进程级别的安全管控。这些还需要配套的安全服务提供支持。

一般来说,只要企业与互联网对接,那么随便一个面向消费者的“市场活动”,就有可能为企业带来井喷的流量。

传统企业内,更多的系统是管理信息类的支撑系统,这类系统在设计时的主要用户是企业内部员工以及有限的外部供应商。这类系统存在于企业内部的时间一直很长,功能耦合也很多,在功能解耦前,是非常不适合的,或者说绝对不可以直接为互联网的用户进行服务的。

SpringBoot自身并没有提供这样的流控措施,所以需要结合前面提到的S-EDA进行流量的控制,并结合下层的水平扩展能力(例如,Kubernets)进行流量负载合理的动态扩容。

另外,在长业务流程的设计上,也尽可能地采用异步的方式,比如接口调用返回的是一个“受理号”,而不是业务的处理结果,避免井喷业务到来时,同步调用所带来的阻塞导致系统迅速崩溃,这些也都是SpringBoot自身并不解决的问题。

下面我们总结一下:

04《Spring Boot 入门教程》使用模板引擎开发 Web 项目

模板引擎这个词,咋听起来,有点高大上的意味。 实际上,模板引擎是非常平易近人的技术。譬如大家可能都比较熟悉的 JSP ,...
点击下载
热门文章
    确认删除?
    回到顶部