有关Spring Roo的本系列文章的第6部分介绍了Spring Roo 1.2中引入的许多新功能。 我谈论的功能之一是对构建MongoDB应用程序的支持。 Spring Roo MongoDB旨在为MongoDB应用程序带来经典的Spring命题,即提高生产率和一致的编程模型。 Spring MongoDB是Spring Data的子项目。 Spring Data是一个伞形开源项目,其中包含许多特定于给定数据存储的子项目。 我将首先介绍MongoDB,然后您将使用Spring Roo构建Spring MongoDB应用程序。
在单一的一行中,MongoDB是一个开源的,面向文档的,无模式的,快速且可水平扩展的NoSQL数据存储。 此描述包含五个非常重要的关键字。 让我们一一看一下:
面向文档的 :在MongoDB中,没有关系数据库中的行概念,但是有文档的概念。 MongoDB以二进制JSON文档或BSON的形式存储数据,该文档与域对象非常紧密地映射。 MongoDB没有联接的概念,但是您有嵌套的对象。 以具有两个域对象的Blog应用程序为例:blog和comment。 在关系数据库中,博客将与评论表具有一对多关系,并且您需要联接才能获取博客的评论。 在MongoDB中,可通过使用嵌套文档来避免联接。 清单1中的示例是一个具有注释数组的博客文档。 没有任何加入,您可以获取博客的所有评论。 MongoDB提供了丰富的查询功能,因此您可以轻松过滤出必填字段和元素。
无模式 :MongoDB将文档存储在集合中,因为行存储在RDBMS中的表中。 但是MongoDB不会强迫您为集合定义严格的架构。 MongoDB中的每个文档都可以与集合中已保存的文档完全不同。 MongoDB集合中的每个文档都是异构的,并且与其他文档相比可以具有完全不同的结构。 这意味着您可以将博客和作者文档存储在与清单2相同的集合中。
快速 :无论是在写入还是读取方面,MongoDB都是高性能的数据存储。 在默认配置下,与RDBMS相比,MongoDB的写入性能更高,因为它会被遗忘,先写入RAM再写入磁盘。 要控制MongoDB中的写行为,请在写操作中指定WriteConcern的值。 查看WriteConcern的不同值。
正常 :这是默认选项,每个写入操作都会被触发而忘记,这意味着它只是写入驱动程序并返回。 它不等待服务器上的写入可用。 因此,如果在写完文档后另一个线程试图读取该文档,则可能找不到它。 使用此选项极有可能导致数据丢失。 在数据持久性很重要并且您仅使用MongoDB服务器的单个实例的情况下,请勿考虑此选项。
无 :这与“普通”几乎相同,只是有所不同。 在“正常”中,如果网络出现故障或发生其他网络问题,则会出现异常。 使用“无”,您将不会遇到网络问题。 这使其非常不可靠。
安全 :顾名思义,此选项比“正常”或“无”选项更安全。 写入操作等待MongoDB服务器确认写入,但数据仍未写入磁盘。 使用Safe,您将不会遇到另一个线程试图读取您刚刚编写的对象而找不到它的问题。 它提供了保证,一旦写入对象,就将找到它们。 很好,但是您仍然会丢失数据,因为如果没有将数据写入磁盘,并且服务器由于某种原因而死亡,则数据将丢失。
Journal Safe :在讨论此选项之前,让我们先讨论一下MongoDB中的日志记录。 日志记录是MongoDB的一项功能,其中为所有操作维护一个预写日志文件。 在未彻底关闭MongoDB的情况下(例如使用kill -9命令),可以从日志文件中恢复数据。 默认情况下,每隔100毫秒(ms)将数据写入日志文件。 您可以将其更改为2毫秒至300毫秒。 对于2.0版,默认情况下在64位MongoDB服务器上启用日记功能。 使用“日志安全写关注”选项,您的写操作将一直等到日志文件被更新。
Fysnc :考虑到Fsync的写操作,写操作将等待服务器将数据刷新到磁盘。 这是“单个”节点上最安全的选项,因为只有在硬盘崩溃时才会丢失数据。
我写了一篇详细的博客文章,标题为“ MongoDB的不同写入关注点值如何影响单个节点上的性能?” (请参阅参考资料 )。 请阅读以获取更多详细信息。
水平可伸缩 :MongoDB是一个水平可伸缩的数据存储,这意味着它可以处理更多的读/写操作,并且可以向集群添加更多的MongoDB服务器。 为了实现水平可伸缩性,MongoDB使用分片(即增加更多MongoDB实例)来处理不断增加的负载和数据,而不影响应用程序性能。 最好的部分是应用程序不必进行分片。 MongoDB自动处理它。 自动分片不在本文讨论范围之内。 请参阅相关信息的链接,上分片MongoDB的文档。
NoSQL :在过去的两年或更长时间里,您可能听说过NoSQL这个术语。 NoSQL是“不仅SQL”的缩写。 NoSQL指的是广泛的数据存储,这些数据存储不遵循RDBMS模型并且不使用SQL作为其主要查询语言。 MongoDB支持查询语言,但是它不像SQL,而是基于JSON文档。
MongoDB支持与RDBMS类似的操作,例如索引,查询,解释计划以及诸如group之类的聚合功能,以保持与RDBMS的距离并降低学习曲线。 通过MongoDB的支持另一个很酷的功能是执行的Map Reduce作业和地理空间索引和查询(见文档的能力相关主题 )。
MongoDB术语非常简单,易于理解和记住。 表1显示了与关系数据库的比较。
数据库
数据库
表
采集
行
文件
在MongoDB中,所有内容都驻留在数据库中,您可以使用命令创建数据库。 正如RDBMS中的数据库可以具有多个表一样,MongoDB中的数据库可以具有多个集合。 就像RDBMS中的表可以具有多行一样,MongoDB中的集合可以具有多个文档。 区别在于,在RDBMS中,行具有固定的结构,所有成员都适用;而在MongoDB中,文档的结构是松散的,而集合中的两个文档可以具有完全不同的结构。
在使用MongoDB之前,请按照四步过程下载并运行MongoDB服务器。
下载MongoDB:您可以从MongoDB网站上为您的操作系统下载最新版本(当前为2.0.4)的MongoDB的tarball或zip(请参阅参考资料 )。
下载MongoDB之后,将zip解压缩到方便的位置并使Shell文件可执行。
在命令行中,进入解压缩的MongoDB安装的bin文件夹,然后执行mongod 。 默认情况下,MongoDB将所有数据存储在/ data / db中,因此您可能需要创建目录并使用户成为所有者。 您还可以通过使用dbpath属性指定位置来选择不使用默认的/ data / db目录,如清单3所示 。
启动MongoDB服务器后,您可以在其Web控制台上查看一些基本信息,该控制台位于http:// localhost:28017 /。 要执行有用的操作,您需要一个客户端。 MongoDB提供了一个名为mongo的命令行客户端,您可以在bin文件夹中找到它。
T0启动客户端,执行./mongo ,您将在清单4中看到输出。
清单4中的输出显示mongo客户端已连接到测试数据库。 与服务器连接后,您可以执行各种操作,例如将文档插入集合中并查找文档。
首先,创建一个数据库。 MongoDB没有创建数据库的命令。 您只需要use命令选择一个数据库,就会为您创建数据库,如清单5所示 。
清单5中的命令创建一个名为springroopart7的数据库,并将上下文切换到新创建的数据库。 该命令仅在不存在时才创建数据库,否则仅将客户端切换到现有数据库。
创建数据库后,下一步的逻辑步骤是在数据库内部创建一个集合。 您可以使用db.createCollection()操作创建集合,如清单6所示 。
在清单6中 ,您执行了创建名为“ blogs”的集合的操作,MongoDB回答说它已成功创建了该集合。
创建集合的另一种方法是将文档保存到集合中。 然后,如果MongoDB不存在,它将创建一个集合。 否则,MongoDB会将文档添加到现有集合中。 接下来介绍。
现在您已经拥有一个博客集合,在集合中插入一个博客。 您需要创建JSON文档并插入该文档,如清单7所示 。
清单7中的命令创建一个新的博客文档,并将其插入到blogs集合中。 如您所见,该文档非常丰富,因为它包含数组字段标签。 您可以轻松查询与特定标签相对应的所有博客。 您可以具有文本类型,数字类型,日期类型,数组等字段。
由于只保留了一个文档,因此可以使用findOne操作查看该文档,该操作将返回此集合中的任何一个文档或与指定查询匹配的第一个文档。 要在blogs集合中找到任何一个文档,请执行清单8中的命令。
如果只想查找一个作者为“ shekhar”的文档,请执行db.blogs.findOne({"author":"shekhar"})
要查询标签,请执行清单9中的命令。 如您所见,它看起来与先前的查询相同。
以上查询将仅返回一个文档。 如果要查找所有具有mongodb标签的作者为shekhar的文档,则可以使用find方法: > db.blogs.find({"author":"shekhar","tags":"mongodb"})
您应该知道的一件事是, find方法返回的是游标而不是文档。 默认情况下,在MongoDB客户端中,您会看到前十个文档。 要查看所有文档,请遍历光标(请参见清单10 )。
您可以在MongoDB上执行许多其他命令。 要查看集合上所有可用的操作,请输入清单11所示的help命令。 为简便起见,我没有显示所有命令。 在自己的mongo客户端中执行时,您将看到它们的全部。
现在,您已经对MongoDB有了基本的了解,可以继续使用MongoDB作为数据存储来创建Spring应用程序。 您可以参考在developerWorks上发布的MongoDB文档或其他MongoDB文章(请参阅参考资料 )。
您将创建的应用程序称为ShortNotes。 它具有两个域对象:Notebook和Note。 笔记本可以有很多笔记。 有几种方法可以设计此应用程序的架构。 让我们看一下不同的模式设计选择。
带有嵌套的嵌入式Note文档阵列的Notebook文档似乎是文档数据存储区最明显的架构设计。 这种设计避免了加入,并提供了一个丰富的域模型。 清单12显示了一个笔记本集合,其中包含两个笔记本文档和一个嵌入式note文档数组。
清单12中的模式看起来不错,您可以查询笔记本和笔记。
要查找所有名称为MongoDB Notebook且作者为shekhar的笔记本,可以编写以下查询:
db.notebooks.find({"name":"MongoDB Notebook","author":"shekhar"})您也可以使用以下命令轻松查询注释:
db.notebooks.findOne({"notes._id":"be7ff4c332fceff338eb9a443"})当您必须更新数组中的特定注释文档或需要从数组中删除特定元素时,事情会变得有些复杂。 这些可以使用$set, $pull修饰符来实现,但是它们要求您对它们有充分的了解。 清单13中的示例删除了带有_id f7ff4c9c2fceff338eb9a443的笔记和带有作者shekhar的笔记本。
该模式存在一些问题。
模式的第一个问题是您无法检索特定的注释。 例如,如果要查找带有_id为“ f7ff4c9c2fceff338eb9a443”的注释,您可能会认为可以像清单14那样轻松地查询该注释。
这将返回其中包含所有注释的笔记本对象。 您将必须在客户端进行所有筛选,以获取所需的note对象。 此功能要求已经被记录在MongoDB的JIRA(见相关信息 ),你可能会看到此功能在未来。
您可能面临的第二个问题是,在一个笔记本文档中有数千个笔记。 由于单个MongoDB文档的大小限制为16 MB,因此写入MongoDB可能会失败。 如果单个文档大于16MB,请考虑规范化架构,以便拥有多个集合。
第三个问题是,当您有成千上万个音符并且每个音符都很大时。 您可能会在客户端遇到内存问题。 对于这种情况,请考虑使用MongoDB $slice运算符进行分页。
设计用例模式的第二种方法是为Notebook和Notes具有单独的集合,每个注释文档都包含对其笔记本的引用。 当您趋向RDBMS范例时,这可能与MongoDB模式设计背道而驰。 但这是某些用例中建议的方法之一。 关键是您不会被迫使用任何一种方法。 设计应由您的应用程序用例控制。 由于Notebook是一个非常简单的文档,只有几个字段,因此每个便笺文档可能包含一个笔记本文档,而不只是一个笔记本标识符。 这避免了交叉收集交互,并简化了查询。 清单15显示了笔记本文档。
“ MongoDB Notebook”中的注释类似于清单16中的注释。
通过这种设计,您可以轻松地查询便笺和笔记本收藏。 要在名为“ MongoDB Notebook”的笔记本中查找所有笔记,请编写以下查询: db.notes.findOne({"notebook.name":"MongoDB Notebook"})
您还可以在notebook.name上创建索引以使查询读取非常快: db.notes.ensureIndex({"notebook.name":1})
此架构设计有一个问题-如果您在Notebooks集合中修改文档,则必须在Notes集合中手动修改其引用。
对于此应用程序,您将关系方法与Notes和Notebook的两个单独的集合一起使用。
在开始构建应用程序之前,请确保您的计算机上装有Spring Roo 1.2.1。 要安装独立的Spring Roo:
下载Roo独立命令行外壳程序或使用内置的Roo SpringSource工具套件(STS)插件(请参阅参考资料 )。 我建议您下载两者并一起使用,因为STS为基于Spring的应用程序提供了超过Eclipse的许多附加功能。
将Spring Roo解压缩到您选择的位置。
设置环境变量:
在Windows计算机上,将%ROO_HOME%\ bin添加到该路径,其中ROO_HOME是解压缩后的Roo文件的路径。
在* nix机器上,创建到$ ROO_HOME / bin / roo.sh的符号链接(如sudo ln -s ~/spring-roo-1.xx/bin/roo.sh /usr/bin/roo -s〜 sudo ln -s ~/spring-roo-1.xx/bin/roo.sh /usr/bin/roo )
现在您已经启动并运行了Spring Roo,可以开始构建应用程序了。 请按照以下步骤创建ShortNotes应用程序。
打开操作系统命令行外壳,并使用mkdir命令创建一个名为shortnotes的目录。
进入shortnotes目录,然后键入roo (或roo.sh ),然后按Enter键。 这将加载Roo shell。
首先,使用project命令在Roo shell中创建项目:
project --topLevelPackage com.xebia.shortnotes --projectName shortnotesproject命令将创建Spring Maven模板项目。 此命令产生的主要工件是pom.xml和applicationContext.xml文件。 pom.xml将包含所有必需的Spring jar以及不同的maven插件和存储库声明。 applicationContext.xml文件是一个通用的Spring上下文文件,特定组件在com.xebia.shortnotes包上进行扫描,以便Spring可以找到所有带有@Component , @Service @Repository和@Repository批注的类。 组件扫描将排除所有带有@Controller注释的类,因为这些类将由Web应用程序上下文加载。
键入hint命令以建议下一个逻辑操作。 它将建议您执行jpa设置或mongo设置。 在构建支持MongoDB的应用程序时,请使用mongo setup命令:
mongo setup --databaseName shortnotes --host localhost --port 27017mongo setup命令没有任何必需的属性,但是在本演示中,我指定了databaseName , host和port属性。 如果不指定此命令,Spring Roo将输入与您指定的默认值相同的默认值。 您还可以指定三个属性: username , password和cloudfoundry 。 username和password用于访问MongoDB,以防您的数据存储区需要身份验证信息。 当您要将应用程序作为服务部署到cloudfoundry平台时,将使用cloudfoundry属性。 我将在文章稍后展示。
mongo setup命令执行三件事:
在pom.xml中添加所需的依赖项 创建一个新的Spring上下文文件applicationContext-mongo.xml 创建database.properties文件此命令添加的依赖项用于Spring MongoDB支持和Bean验证支持。 database.properties文件包含诸如host , port , databaseName类的属性。 applicationContextmongo.xml文件包含最重要的信息。 它利用了mongo名称空间,该名称空间避免了编写Spring bean声明,并使开发人员非常容易。 请参考清单17中的applicationContext-mongo.xml代码段。
applicationContext-mongo.xml有一个小错误。 错误是dbname对应于一个名为mongo.database的属性,但此属性在database.properties文件中不存在。 将mongo.database替换为mongo.name 。 查看清单17中 XML片段中声明的四行。
第一行创建一个工厂,该工厂将生成一个Mongo实例,用于连接名称为shortnotes的MongoDB数据库,该实例在端口27017的本地主机上运行。此声明也可以使用username和password进行身份验证。 此声明中未显示的另一个重要而有用的属性是写关注,我在本文前面已经提到过 。 它允许您更改写入行为。 默认值为NONE,它是“即发即弃”,您可能会丢失数据。
第二行确保扩展MongoRepository接口或其在com.xebia.shortnotes包中的任何其超类型的所有接口都将具有Spring存储库bean。 存储库实现Bean将动态创建,并将具有CRUD和分页支持。 这省去了开发人员的大量工作,并删除了许多与CRUD和分页方法有关的样板代码。 根据某些约定,这也可以生成CRUD方法和finder方法。 例如,在用例中,要搜索所有笔记本的作者,可以声明如下所示的方法,并在运行时生成该实现。 有关更多详细信息,请参阅参考资料中的Spring MongoDB文档。
public List<Notebook> findByAuthor(String author)第三行用于将任何RuntimeException转换为Spring异常层次结构中的适当异常。
最后一行创建一个MongoTemplate bean,可使用其他各种有用的方法来执行create , delete , find和update操作。 它还提供了您的域对象和MongoDB文档之间的映射。 您可以使用MongoTemplate完成与存储库相关的所有工作,反之亦然。 这是Spring MongoDB支持的最重要的类。 MongoTemplate类中公开的方法与将在MongoDB命令行客户端上执行的方法非常紧密地映射。 MongoTemplate类利用Query和Criteria API来构建查询。 在您的用例示例中,要搜索所有笔记本的作者,请输入:
List<Notebook> myNotebooks = mongoTemplate.find(Query.query(Criteria.where("author").is ("shekhar")),Notebook.class)设置MongoDB之后,创建Notebook和Note实体。 使用清单18中的mongo entity命令。
\清单18中的两个命令将创建Notebook.java和Note.java以及一些ITD和测试基础结构代码。 testAutomatically属性负责为各个域类创建集成测试。 mongo entity命令生成的重要工件是Notebook_Roo_Mongo_Entity.aj和Note_Roo_Mongo_Entity.aj。 这些ITD指定实体应具有@Persistent批注,并且id为BigInteger类型。 持久存储之前,Spring MongoDB会将BigInteger id转换为ObjectId 。 其余工件用于为实体字段和toString()方法指定getter和setter。
现在,您已经创建了实体,使用field命令向其中添加一些字段。 首先使用清单19中的命令将字段添加到Notebook实体。现在,使用清单20中的命令将字段添加到Note实体。
清单20中命令的有趣之处在于,在两个实体中创建的字段名称将是不可更新的字段。 这意味着,当您为这些实体使用用户界面时,您将无法编辑创建的字段。
到目前为止,您尚未创建域模型,因为您尚未定义Notebook和Note之间的关系。 如先前在架构设计中所讨论的,每个便笺文档将具有一个嵌入式笔记本文档。 这意味着,如果您有一个包含100个便笺的笔记本,则笔记本集合中将有一个原始笔记本文档,而在100个便笺中将有100个其他笔记本副本。 但是这种模式设计更简单,更适合您的要求。 要将笔记本字段添加到Note实体,请输入:
field reference --type ~.domain.Notebook --fieldName notebook --notNull此命令确保仅在已有笔记本时才创建便笺。
现在您的域模型已经完成,您可以为先前创建的实体创建存储库。 存储库由Spring MongoDB支持提供。 要为Notebook实体创建存储库,请执行清单21中的命令。 还显示了示例输出。
输出告诉您它创建了一个存储库文件夹,一个Java™文件和一个ITD。 NotebookRepository.java文件包含一个findAll()方法来查找所有笔记本,并带有@RooMongoRepository批注,它告诉Roo生成NotebookRepository_Roo_Mongo_Repository.aj ITD文件。 在编译时,来自ITD的代码将被推入接口。 ITD是所有代码所在的地方。 清单22显示了Spring Roo生成的ITD文件。
的ITD定义了NoteBookRepository接口与进行注释@Repository注释和该接口应该扩展PagingAndSortingRepository接口。 当NotebookRepository接口编译以后有@Repository注解,并且将延长PagingAndSortingRepository 。 如在步骤4中已经讨论的那样,存储库实现将在运行时创建。 在实现PagingAndSortingRepository接口时,将具有CRUD方法以及使用分页和排序来检索实体的其他方法。
在类似的行上,您可以为Note实体创建存储库:
repository mongo --interface ~.repository.NoteRepository --entity ~.domain.Note接下来,添加一个与Notebook和Note实体相对应的服务层。 这充当控制器层和存储库层之间的立面。 您可以在此处放置特定于应用程序的业务逻辑。 清单23显示了Notebook实体及其输出的服务命令。
在类似的行上,您可以为Note实体创建服务层: service --interface ~.service.NoteService --entity ~.domain.Note
最后,可以使用web mvc命令为应用程序创建Spring MVC控制器,如清单24所示 。
web mvc setup命令将通过在pom.xml中添加Spring MVC依赖项,创建一个名为webmvc-config.xml的Web应用程序上下文文件,生成标签库并设置国际化来设置Spring MVC应用程序所需的基础结构。 web mvc all命令将创建控制器类并生成相应的视图。
退出roo shell。
使用mongod可执行文件启动MongoDB服务器。
要运行该应用程序,请键入mvn tomcat:run,它将启动tomcat服务器。 打开网络浏览器,然后转到http:// localhost:8080 / shortnotes /。 您可以创建一个笔记本,然后创建一个注释以指定要使用的笔记本。 例如,我创建了一个名为MongoDB Notes和作者shekhar的笔记本。 然后,我创建了带有标题和内容的便笺,作为MongoDB入门,并为其分配了我们创建的笔记本。 清单25显示了Notebook文档和Notes文档。
如果将Notebook名称更新为MongoDB Cookbook,则会发现带有该Notebook的所有Notes都不会更新。 这是预期的行为。 要解决此问题,请在更新笔记本后更新所有笔记。 在NoteServiceImpl中创建一个新方法updateNotesWithNotebook ,如清单26所示 。
在代码清单26种用途updateMulti的方法MongoDBTemplate更新所有对应于更新的笔记本电脑的文件。 updateMulti()方法采用三个参数: query object , update object和collection entity type 。 查询对象指定选择所有具有给定id Notebook的Note文档。 然后,创建一个update对象来设置笔记本的作者和名称。
创建方法后,你需要调用这个更新的笔记本电脑后NotebookController (参见清单27 )。 确保将update方法从ITD推送到NotebookController java类。
删除笔记本时,您将面临同样的问题,因为Notes仍将引用该笔记本。 如果删除了特定笔记本的所有注释,则将其删除是有意义的。 如清单28所示 ,在NoteServiceImpl类中添加removeNotes()方法。
删除Notebook时将调用removeNotes()方法。 将调用添加到NotebookController中的removeNotes的delete()方法中,如清单29所示 。
您可以使用mvn tomcat再次运行该应用程序:运行并创建一个笔记本,然后创建便笺,然后更新笔记本。 您将看到这些注释也具有更新的笔记本文档。 您也可以尝试删除笔记本,并且笔记本的所有注释也会被删除。
既然您已经创建了简记应用程序,那么就可以部署它了。 可以将Spring应用程序部署到Cloud Foundry公共云,而无需进行任何修改。 我在第4 部分和第6 部分中详细介绍了Cloud Foundry。 如果您不熟悉,请参阅这些文章。 在本文中,您将使用vmc ruby gem在Cloud Foundry上部署会议应用程序。 跟着这些步骤:
需要再次运行mongo setup命令时,再次启动roo shell。 这次,您将切换到Cloud Foundry MongoDB实例。 在Roo shell上执行以下命令。 这将更新applicationContext-mongo.xml。
mongo setup --cloudFoundry在计算机上安装vmc客户端。 (有关如何安装命令行界面的分步教程,请参阅参考资料 。)
使用您在Cloud Foundry上注册的凭据登录到Cloud Foundry公共云。 输入vmc login命令,它将要求您提供电子邮件和密码,如清单30所示 。
一旦安装了vmc客户端,就可以对shortnotes应用程序进行maven构建。 键入mvn clean install
生成项目后,将应用程序推送到Cloud Foundry。 要从命令行推送代码,请导航至目标文件夹,然后键入vmc push命令。 vmc push命令将询问问题。 像清单31一样对它们进行响应。
最后,您将在http://shortnotes.cloudfoundry.com/上看到在云中运行的shortnotes应用程序
要获得shortnotes应用程序的源代码,克隆shortnotes GitHub的库(见相关主题 )。
本文向您介绍了MongoDB,以及如何使用Spring Roo构建Spring MongoDB应用程序。 您介绍了MongoDB的基础知识,选择了最适合您要求的架构,以及Spring Roo如何帮助快速引导Spring MongoDB应用程序。
在本文中,我没有介绍MongoDB MapReduce,数组修饰符,设置MongoDB复制,地理空间查询等功能。 还有很多值得探索的地方。 你会在列出的文档中找到很好的资源相关主题 。
翻译自: https://www.ibm.com/developerworks/java/library/os-springroo7/index.html
相关资源:微信小程序源码-合集6.rar