精通 Grails: 用 Groovy 服务器页面(GSP)改变视图
Groovy 服务器页面(Groovy Server Pages,GSP)将 Web 置于 Grails Web 框架之内。在 精通 Grails 系列的第三期中,Scott Davis 介绍了如何使用 GSP 工作。您将了解到可以非常轻松地使用 Grails TagLibs、将 GSP 的部分片断组合在一起以及为自动生成(搭建)的视图自定义默认模板。
查看本系列更多内容 | 评论:
Scott Davis, 主编, AboutGroovy.com
2008 年 4 月 01 日
+
内容
本系列的前两篇文章介绍了 Grails Web 框架的基本构建块。我曾反复强调过 —Grails 基于模型-视图-控制器(Model-View-Controller,MVC)架构模式(请参阅 参考资料),Grails 利用约定优于配置 将框架的各个部分组合在一起。Grails 用命名直观的文件和目录代替了更容易出错的在外部配置文件中手工对这些链接进行归类的老方法。例如,在 第一篇文章 可以看到控制器拥有 Controller 后缀,存储在 grails-app/controller 目录。在 第二篇文章 了解到可以从 grails-app/domain 目录找到域模型。
在本月的文章中,我将通过讨论 Grails 视图进一步介绍 MVC。视图(正如您所料)存储在 grails-app/views 目录内。但是视图远不止直观的目录名称这么简单。本文将讨论 Groovy 服务器页面(GSP)并介绍许多替代的视图选项。在本文中将学习标准的 Grails 标记库(TagLibs),并了解到创建自定义 TagLib 有多么容易。还会看到如何将 GSP 的常用片断提取出来放在自己的片段模板(partial template)内,从而遵循 DRY(Don't Repeate Yourself,不要重复自己)(请参阅 参考资料)原则。最后,将学习如何为搭建的视图调整默认模板,从而在方便地自动创建视图和跳出 Grail 应用程序默认外观之间进行平衡。
查看 Grails 应用程序
Grails 使用 GSP 作为表示层。Groovy 服务器页面中的 Groovy 不仅代表底层技术,还代表可以快速编写一两个 scriptlet 的语言。从这方面来说,GSP 非常类似于 Java 服务器页面(JSP)技术,JSP 允许在 Web 页面上混合使用一些 Java 代码,也和 RHTML(Ruby on Rails 的核心视图技术)非常相像,RHTML 允许在 HTML 标记之间插入一些 Ruby 代码。
当然,Java 社区长期以来都不欣赏小脚本。scriptlet 会导致最低形式的技术重用 —复制与粘贴 — 以及其他一些在技术方面为人所不齿的恶行(因为你能 和因为你应该 之间有巨大区别)。GSP 中的 G 对优秀、正直的 Java 人员来说只应该表示一种实现语言而不是其他。Groovy TagLibs 和片段模板提供了在 Web 页面之间共享代码和行为的一种更成熟的方式。
GSP 是 Grails 以页面为中心的 MVC 观点的基础。页面是基本衡量单位。列表页面提供了到 Show 页面的链接。Show 页面支持单击到编辑页面,诸如此类。不论是熟练的 Struts 开发人员还是最近的 Rails 爱好者,都熟悉这种 Web 生命周期。
之所以提到这点,是因为近几年出现了大量不以页面为中心的视图技术(请参阅 参考资料)。面向组件的 Web 框架(例如 JavaServer Faces (JSF) 和 Tapestry 越来越受到青睐。Ajax 革命派生出大量基于 JavaScript 的解决方案,例如 Dojo 和 Yahoo! UI (YUI) 库。富 Internet 应用程序(RIA)平台,例如 Adobe Flash 和 Google Web Toolkit (GWT) 承诺能够实现方便的 Web 部署,并提供更加丰富、与桌面类似的用户体验。幸运的是,Grails 能够轻松地处理所有这些视图技术。
MVC 关注点隔离的整体要点在于:它能够使您轻松地用自己喜欢的任何视图作为 Web 应用程序的外观。Grails 流行的插件基础设施意味着许多 GSP 替代物不过是 grails 安装的插件(请参阅 参考资料 获得可用插件的完整列表的链接,或者在命令行下输入 grails list-plugins)。许多插件都是由社区驱动的,是那些希望将 Grail 与他们喜欢的表示层技术一起使用的人们的努力结果。
虽然 Grails 没有内置 JSF 的自动挂勾(hook),但是仍然可以结合使用这两种技术。Grails 应用程序是标准的 Java EE 应用程序,因此可以将相应的 JAR 放在 lib 目录内,将需要的设置放在 WEB-INF/web.xml 配置文件内,并像平常一样编写应用程序。Grails 应用程序部署在标准的 servlet 容器内,所以 Grails 对 JSP 的支持同对 GSP 的支持一样好。Grails 有针对 Echo2 和 Wicket 的插件(两者都是面向组件的 Web 框架),所以在使用 JSF 或 Tapestry 插件方面没有任何障碍。
类似地,向 Grails 添加 Ajax 框架(例如 Dojo 和 YUI)的步骤也没有什么特别之处:只要将它们的 JavaScript 库复制到 web-app/js 目录即可。Prototype 和 Scriptaculous 是 Grails 的默认安装。RichUI 插件则从各种 Ajax 库选择 UI 部件。
如果查看插件列表,那么就会看到对 RIA 客户机的支持 —— 例如 Flex、OpenLazlo、GWT 和 ZK。显然,Grails 应用程序并不缺少备选的视图解决方案。但是在这里我们还是采用 Grail 直接支持的视图技术 — GSP。
回页首
GSP 101
可以使用多种方法查找 GSP 页面。文件扩展名 .gsp 就是一种很明显的方法,就好像很多以 <g: 开头的标记一样。事实上,GSP 不过是标准 HTML 加上一些提供动态内容的 Grails 标记而已。在前一节提到的某些备选的视图技术是一层不透明的抽象层,完全将 HTML、CSS 和 JavaScript 的细节隐藏在 Java、ActionScript 或其他编程语言层之后。GSP 是在标准 HTML 上的薄薄的一层 Groovy 层,因此在必要时,可以轻松地将它从框架中取出来,并使用原生的 Web 技术。
但是要在目前的 Trip Planner 应用程序中查找 GSP 的话,则会比较费力(这个系列的前两篇文章开始构建 Trip Planner 程序。如果没有跟上进度,那么现在是赶上来的好时机)。现在视图正在使用动态搭建(scaffold),所以 trip-planner/grails-app/views 目录还是空的。请在文本编辑器打开如清单 1 所示的 grails-app/controller/TripController.groovy,查看用来启用动态搭建的命令:
清单 1. TripController 类
class TripController{
def scaffold = Trip
}
def scaffold = Trip 行告诉 Grails 在运行的时候动态地生成 GSP。这非常适合自动保持视图与域模型修改同步,但是如果正在学习该框架,那么它没有提供太多可学习的东西。
请在 trip-planner 根目录下输入 grails generate-all Trip。当询问是否覆盖现有控制器时,回答 y(也可以回答 a 表示全部,这将覆盖所有内容,这样就不会反复出现提示)。现在应该看到完整的 TripController 类,带有名为 create、edit、list 和 show 闭包(以及其他闭包)。还应该看到 grails-app/views/trip 目录下有四个 GSP:create.gsp, edit.gsp, list.gsp, and show.gsp.
在这里起作用的是 “约定优于配置”。当访问 http://localhost:9090/trip-planner/trip/list 时,就是要求 TripController 填充域模型对象 Trip 的列表,并将列表传递给 trip/list.gsp 视图。请在文本编辑器中查看 TripController.groovy,如清单 2 所示:
清单 2. 完全填充的 TripController 类
class TripController{
...
def list = {
if(!params.max) params.max = 10
[ tripList: Trip.list( params ) ]
}
...
}
这个简单的闭包从数据库中检索到 10 条 Trip 记录,将它们转换为 POGO,并将它们保存在名为 tripList 的 ArrayList 内。list.gsp 页面随后将遍历列表,逐行构建 HTML 表格。
下一节研究许多流行的 Grails 标记,包括用来在 Web 页面上显示每条 Trip 的 <g:each> 标记。
回页首
Grails 标记
<g:each> 是常用的 Grails 标记。它将遍历列表中的每个 项。要查看它的效果,请在文本编辑器中打开 grails-app/views/trip/list.gsp(如清单 3 所示):
清单 3.list.gsp 视图
<g:each in="${tripList}" status="i" var="trip">
<tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
<td><link action="show" id="${trip.id}">${trip.id?.encodeAsHTML()}</g:link></td>
<td>${trip.airline?.encodeAsHTML()}</td>
<td>${trip.name?.encodeAsHTML()}</td>
<td>${trip.city?.encodeAsHTML()}</td>
<td>${trip.startDate?.encodeAsHTML()}</td>
<td>${trip.endDate?.encodeAsHTML()}</td>
</tr>
</g:each>
<g:each> 标记的 status 属性是个简单的计数器字段(请注意这个值用在下一行的 ternary 语句中,用来将 CSS 样式设为 even 或 odd)。var 属性允许命名用来保存当前项的变量。如果将名称改为 foo,那么需要将后面的行改为 ${foo.airline?.encodeAsHTML()},依次类推( ?. 操作符是 Groovy 用来避免 NullPointerException 的方法。它可以快捷地表示 “只有在 airline 不为 null 时才调用 encodeAsHTML() 方法,否则返回空字符串”)。
另一个常用 Grails 标记是 <g:link>。顾名思义,它的作用是构建一个 HTML <a href> 链接。当然也可以直接使用 <a href> 标记,但是这个方便的标记还接受属性,例如 action、id 和 controller。如果只考虑 href 的值而不考虑它前后的 anchor 标记,那么可以改用 <g:createLink> 。在 list.gsp 顶部能看到返回链接的第三个标记: <g:createLinkTo>。这个标记接受 dir 和 file 属性而不是 controller、action 和 id 属性。清单 4 显示了 link 和 createLinkTo 的作用:
清单 4. link 标记 vs. createLinkTo 标记
<div class="nav">
<span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
<span class="menuButton"><link class="create" action="create">New Trip</g:link></span>
</div>
注意,在清单 4 中,可以交替使用两种不同的形式调用 Grails 标记 — 一种是在尖括号内包含标记,一种是仿效方法调用在大括号内包含标记。当在另一个标记的属性中调用方法时,大括号表示法(正式名称为表达式语言或 EL 语法)更合适。
在 list.gsp 下面的几行,能够看到另一个流行的 Grails 标记:<g:if>,如清单 5 所示。在这个示例中,它的意思是 “如果 flash.message 属性不为 null,就显示它。”
清单 5. <g:if> 标记
<h1>Trip List</h1>
<if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
在浏览生成的视图时,还会看到其他许多 Grails 标记。<g:paginate> 标记在数据库包含的 Trip 比当前显示的 10 条记录多时,显示 “前一个” 和 “下一个” 链接。<g:sortable> 标记使列标题变为可单击,从而支持排序。看看其他 GSP 页面中与 HTML 表单有关的标记,例如 <g:form> 和 <g:submit>。Grails 的联机文档中列出了所有可用的 Grails 标记,并提供了它们的用法示例(请参阅 参考资料)。
回页首
自定义标记库
虽然标准 Grails 标记很有帮助,但是最终会遇到需要自定义标记的情况。许多资深 Java 开发人员(包括我自己)公开表示,“自定义 TagLib 是合适的架构性解决方案。”,然后却偷偷地编写 scriptlet,以为别人看不到。编写自定义 JSP TagLib 需要的工作太多,所以难以抵抗 scriptlet 的诱惑。scriptlet 并不是正确的方法,但不幸的是,它是一种容易实现的方法。
Scriptlet 破坏了 HTML 基于标记的范式,将原始代码直接引入视图。错误的并不是代码本身,而是它们缺少封装和重用的潜力。重用 Scriptlet 的惟一方式就是 “复制-粘贴”。这导致 bug、代码膨胀,并严重违背了 DRY 原则。更不用说 Scriptlet 在可测试性方面的匮乏了。
这就是说,我必须坦白,随着开发期限越来越紧迫,我写的 JSP scriptlet 的比例也相当大。JPS 标准标记库(JSP Standard Tag Library,JSTL)在这方面帮助了我很多,但是编写我自己的自定义 JSP 标记则完全是另一回事。在我用 Java 代码编写自定义 JSP 标记、编译标记,并将大量时间浪费在将标记库描述符(Tag Library Descriptor,TLD)设置为正确的格式和位置时,我已经完全忘记了当初编写这个标记的理由是什么。编写测试来验证我的新 JSP 标记是否正确也同样麻烦 — 只能说我的出发点是好的。
对比之下,用 Grails 编写自定义 TagLibs 简直就是举手之劳。Grails 框架使得做正确的事(包括编写测试)变得很容易。例如,我经常需要在 Web 页面底部加上标准的版权声明。版权声明应该是这样的: 2002 - 2008, FakeCo Inc. All Rights Reserved.。问题在于,我希望第二个年份总是当前的年份。清单 6 显示了用 scriptlet 如何完成这个任务:
清单 6. 用 scriptlet 完成的版权声明
<div id="copyright">
© 2002 - ${Calendar.getInstance().get(Calendar.YEAR)},
FakeCo Inc. All Rights Reserved.
</div>
既然知道了如何处理当前年份,那么下面就要创建一个执行同样任务的自定义标记。请输入 grails create-tag-lib Date,这会创建两个文件:grails-app/taglib/DateTagLib.groovy(TagLib)和 grails-app/test/integration/DateTagLibTests.groovy(测试)。将清单 7 中的代码添加到 DateTagLib.groovy:
清单 7.简单的自定义 Grails 标记
class DateTagLib {
def thisYear = {
out << Calendar.getInstance().get(Calendar.YEAR)
}
}
清单 7 创建一个 <g:thisYear> 标记。可以看到,年份直接写入输出流。清单 8 显示了新标记的效用:
清单 8.使用自定义标记的版权声明
<div id="copyright">
© 2002 - <thisYear />, FakeCo Inc. All Rights Reserved.
</div>
您可能以为这就完成了。非常抱歉,这只完成了一半。
回页首
测试 TagLibs
即使现在看起来一切正常,还是应该编写一个测试,确保这个标记日后不会出错。Working Effectively with Legacy Code 的作者 Michael Feathers 说过,任何没有测试的代码都是遗留代码。为了防止 Feathers 先生大发雷霆,请将清单 9 的代码添加到 DateTagLibTests.groovy:
清单 9.自定义标记的测试
class DateTagLibTests extends GroovyTestCase {
def dateTagLib
void setUp(){
dateTagLib = new DateTagLib()
}
void testThisYear() {
String expected = Calendar.getInstance().get(Calendar.YEAR)
assertEquals("the years don't match", expected, dateTagLib.thisYear())
}
}
GroovyTestCase 是在 JUnit 3.x TestCase 之上一层薄薄的 Groovy 层。为只有一行代码的标记编写测试看起来似乎有些过分,但是很多时候问题的源头正是这一行代码。编写测试并不难,而且保证安全要比说抱歉更好。请输入 grails test-app 运行测试。如果一切正常,应该看到如清单 10 所示的信息:
清单 10.在 Grails 中通过测试
-------------------------------------------------------
Running 2 Integration Tests...
Running test DateTagLibTests...
testThisYear...SUCCESS
Running test TripTests...
testSomething...SUCCESS
Integration Tests Completed in 506ms
-------------------------------------------------------
如果 TripTests 的样子让您感到惊讶,请不要担心。在输入 grails create-domain-class Trip 时,将会为您生成一个测试。实际上,每个 Grails create 命令都会生成对应的测试。确实,测试在现代软件开发中如此 之重要。如果以前没有编写测试的习惯,那么 Grails 将优雅地将您带到正确的方向上来,您肯定不会后悔。
grails test-app 命令除了运行测试之外,还会创建很好的 HTML 报告。请在浏览器中打开 test/reports/html/index.html,查看标准的 JUnit 测试报告,如图 1 所示。
图 1.单元测试报告
Unit test report
编写并测试过简单的自定义标记之后,现在要构建一个略微复杂一些的标记。
回页首
高级自定义标记
更复杂的标记中可以处理属性和标记体。例如,现在的版权标记还需要许多复制/粘贴工作才能满足需求。我想像下面这样将当前的行为放在真正可重用的标记内: <g:copyright startYear="2002">FakeCo Inc.</g:copyright>。 清单 11 显示了代码:
清单 11.处理属性和标记体的 Grails 标记
class DateTagLib {
def thisYear = {
out << Calendar.getInstance().get(Calendar.YEAR)
}
def copyright = { attrs, body ->
out << "<div id='copyright'>"
out << "© ${attrs['startYear']} - ${thisYear()}, ${body()}"
out << " All Rights Reserved."
out << "</div>"
}
}
请注意:attrs 是标记属性的 HashMap。在这里用它提取 startYear 属性。我将以闭包形式调用 thisYear 标记(这与我用大括号时从 GSP 页面所做的闭包调用相同)。类似地,body 也以闭包的形式传递给标记,所以调用它的方式与调用其他标记的方式相同。这样确保了我的自定义标记可以按照任意深度嵌套到 GSP 中。
您可能注意到,自定义 TagLibs 使用与标准 Grails TagLibs 相同的名称空间 g:。如果需要将自己的 TagLibs 放在自定义名称空间内,请向 DateTagLib.groovy 中添加 static namespace = 'trip'。在 GSP 内,TagLib 现在应该是 <trip:copyright startYear="2002">FakeCo Inc.</trip:copyright>。
回页首
片断模板
自定义标记是重用简短代码的好方法,从而避免成为只能复制/粘贴的 scriptlet。但是对于更大块的 GSP 标记来说,可以使用片断模板。
片断模板在 Grails 文档中的官方称谓是模板。惟一的问题是模板 这个词用得太多了,在 Grails 中有许多不同的意义。下一节就会看到,将安装改变搭建视图的默认模板。对这些模板的修改也包括本节要讨论的片断模板。为了减少混淆,我从 Rails 社区借用了一个术语,将要表达的内容称为片断模板,或者就称为片断。
片断模板是一大块能够在多个 Web 页面之间共享的 GSP 代码。例如,假设我要在所有页面底部使用一个标准的页脚。为了实现这一目的,我要创建一个名为 _footer.gsp 的代码片断。前面的下划线是对框架的提示(对开发人员也是个明显的提示),告诉框架这不是个完整的格式良好的 GSP。如果我在 grails-app/views/trip 目录中创建这个文件,那么只有 Trip 视图才会看到它。我要将它保存在 grails-app/views 目录内,这样就能供所有页面全局共享。清单 12 显示了全局共享页脚的片断模板:
清单 12. Grails 片断模板
<div id="footer">
<copyright startYear='2002'>FakeCo, Inc.</g:copyright>
<div id="powered-by">
<img src="${createLinkTo(dir:'images', file:'grails-powered.jpg')}" />
</div>
</div>
可以看到,片断模板支持用 HTML/GSP 语法进行表达。对比之下,自定义 TagLib 是用 Groovy 编写的。简要来说,TagLibs 一般情况下用来封闭小行为更合适,而片断模板更适于重用布局元素。
为了让这个示例能正常工作,还需要将 “Powered by Grails” 按钮下载到 grails-app/web-app/images 目录(请参阅 参考资料)。在下载页面上会看到其他许多附属内容,从高分辨率的 logo 到 16x16 大小的 favicons(浏览网站时在浏览器地址栏前显示的图标)。
清单 13 显示了如何在 list.gsp 页面底部包含新建的页脚:
清单 13.呈现片断模板
<html><body>
...
<render template="/footer" />
</body></html>
请注意,在呈现模板时,要去掉下划线。如果在 trip 目录下保存 _footer.gsp,那么前面的斜杠也要省略。可以这样认为:grails-app/views 目录是视图层次结构的根。
回页首
自定义默认搭建
有了一些良好的、可测试的、可重用的组件之后,可以将它们做为默认搭建的一部分。这部分内容是在将 def scaffold = Foo 放入控制器之后动态生成的。默认搭建也是输入 grails generate-views Trip 或 grails generate-all Trip 时生成 GSP 的来源。
要定制默认搭建,请输入 grails install-templates。这样会在项目中加入新的 grails-app/src/templates 目录。应该看到三个目录,名为 artifacts、scaffolding 和 war。
artifacts 目录容纳各种 Groovy 类的模板: Controller、DomainClass、 TagLib,等等。例如,如果想让所有控制器都扩展一个抽象父类,那么可以在这里进行修改。全部新控制器都将基于修改过的模板代码(有些人会加入 def scaffold = @artifact.name@,这样动态搭建就会成为所有控制器的默认行为)。
war 目录包含所有 Java EE 开发人员都熟悉的 web.xml 文件。如果需要添加自己的参数、过滤器或 servlet,请在这里进行操作(JSF 爱好者们:注意到了吗?)在输入 grails war 时,这里的 web.xml 文件就会被包含到生成的 WAR 内。
scaffolding 目录包含动态生成的视图的原始内容。请打开 list.gsp 并将 <render template="/footer" /> 添加到文件底部。因为这些模板是所有视图共享的,所以一定要使用全局片断模板。
调整了列表视图之后,现在需要验证修改是否生效。对默认模板的修改是少数需要重新启动服务器的操作之一。Grails 重新启动之后,请用浏览器访问 http://localhost:9090/trip-planner/airline/list。如果正在使用 AirlineController 的默认搭建,那么在页面底部就应该出现新的页脚。
回页首
结束语
本期文章总结了 精通 Grails 的另一篇文章。现在您对 GSP 以及 Grails 可以使用的其他视图技术应该有了进一步的了解,并更好地理解了在生成的众多页面中使用的默认标记。下次您再编写 scriptlet 时,肯定会觉得有点 “不舒服”,因为通过编写自定义 Taglib 可以更轻松地完成正确的事情。您看到了如何创建片断模板,还看到了将它们添加到默认搭建视图有多么容易。
下个月的 Grails Web 框架之旅的重点是 Ajax。不用重新加载整个页面就能发送 “微型” HTTP 请求,这一能力是 Google Maps、Flickr 以及其他许多流行 Web 站点背后的诀窍。下个月您将在 Grails 中体会到相同的魔力。具体来讲,将创建一个多对多关系,并通过 Ajax 使用户体验变得自然而有趣。
现在要说再见了,希望您喜欢 “精通 Grails”系列。
精通 Grails: 用 Groovy 服务器页面(GSP)改变视图的更多相关文章
- vue中滚动页面,改变样式&&导航栏滚动时,样式透明度修改
vue中滚动页面,改变样式&&导航栏滚动时,样式透明度修改.vue <div class="commonHeader" v-bind:class=" ...
- SNF开发平台-SNF.CodeGenerator-升级生成BS页面代码-支持视图-数据库配置-快速开发者的利器
有一段时间没有进行总结SNF快速开发平台了,这段时间把今年在框架升级部分进行整理说明. 下面就把代码生成器升级部分介绍一下: 1.新增BS页面生成代码 2.新增视图支持 3.新增 数据库配置 1.新增 ...
- iOS10 UI教程基改变视图的外观与视图的可见性
iOS10 UI教程基改变视图的外观与视图的可见性 视图是应用程序的界面,是用户在屏幕上看到的对象.用户可以通过触摸视图上的对象与应用程序进行交互,所以视图界面的优劣会直接影响到了客户体验的好坏.和视 ...
- Xamarin.Forms的基本页面和基本视图
Xamarin.Forms的基本页面和基本视图 在Xamarin.Forms中,每个App的界面都是一个页面Page.页面的种类有很多种.其中,最常见的页面就是内容页面ContentPage.项目 ...
- 页面可见性改变事件 : visibilitychange 详解
1.Page Visibility API标准概述 查看W3C的官方文档时候看到这个属性 标准时间线是这样介绍的: Page Visibility Level 2 W3C Proposed Recom ...
- web服务器页面错误代码集
HTTP 1xx-信息提示 这些状态代码表示临时的响应.客户端在收到常规响应之前,应准备接收一个或多个1xx响应. 100-继续. 101-切换协议. 2xx-成功 这类状态代码表明服务器成功地接受了 ...
- grails的layouts模板页面使用
使用方式1: layouts文件夹下新建文件,名称和Controller名称相同,例如UserController,layouts下面创建user.gsp,此时,user站点下所有的页面都将套用 us ...
- HTML5事件—visibilitychange 页面可见性改变事件
转:https://blog.csdn.net/yusirxiaer/article/details/73480916 又看到一个很有意思的HTML5事件 visibilitychange事件是浏览器 ...
- IDEA Tomcat Web项目修改了代码,重新部署页面没改变
今天被IDEA坑的不浅直接说一下问题: 这是html页面不管我怎么修改重启服务器在浏览器中还是一点都不变化,甚至把一些内容都删了都没有变化,target可执行文件是最新的没问题,找了点资料发现是浏览器 ...
- JSP Java服务器页面
大家好!好久不见!今日我们开始学习JSP了,一些记录基础性的知识在这里与大家分享. 先说下URL(Uniform Resource Locator 统一资源定位符). URL包括传输协议(http:/ ...
随机推荐
- Python中用With open as 实现对文件的操作
with open as f在Python中用来读写文件(夹). 基本写法如下: with open(文件名,模式)as f: f.write(内容)#写操作 例:with open ('这个文章.t ...
- Java中各种比较对象方式对比
1.介绍 比较对象是面向对象编程语言的一个基本特征.在本教程中,我们将介绍Java语言的一些特性,这些特性允许我们比较对象.此外,我们还将研究外部库中的这些特性. 2.==和!=操作符 让我们从==和 ...
- Vue+SpringBoot+ElementUI实战学生管理系统-8.班级管理模块
1.章节介绍 前一篇介绍了专业管理模块,这一篇编写班级管理模块,需要的朋友可以拿去自己定制.:) 2.获取源码 源码是捐赠方式获取,详细请QQ联系我 :)! 3.实现效果 班级列表 修改班级 4.模块 ...
- java ArrayList排序不区分大小写
最近在做代码勇士codewars的题目,顺便记录一下自己的解决方案. 1.排序类 1.1 不使用预定义比较器 package com.dylan.practice; import java.util. ...
- eclipse解决maven不自动下载jar包问题
今天在eclipse中import一个maven项目,由于我手工中断了相关pom.xml中配置的jar包下载. 导致项目找不到一些jar包.具体错误见下图: 原因就是有些jar包下载没有完成导致的. ...
- B - Bracket Sequence题解
B - Bracket Sequence 思路: 用一个flag来标记括号的数目,如果括号数目是个偶数的话,就代表当前要执行'+'操作,反之就是'*'操作.对于最外层的数,是没有计算的. 所以最后要单 ...
- 编译静态库遇到的 LNK2019 报错
前文提到了 CMake 学习 文末基本涵盖了我遇到的编译问题,但是在得到一个编译好的 .lib 文件后,还需要放到项目中引用成功后才算真正的完成静态库的编译 嗯,我之所以说这些是因为我在项目中链接静态 ...
- 团队协作如何确保项目Node版本的一致性?
前言 想必大家在工作过程中都遇到过node版本带来的各种各样的问题,对于团队协作项目,你不能保证所有人的本地node版本都相同,所以在项目文档中往往会写上以下内容: 为与线上环境一致,请保证以下版本 ...
- webservice之jax-ws实现方式(服务端)
1.什么是webservice? webservice是一种远程资源调用技术,它的实现方式主要分为两种, 第一种是jaxws方式,它是面向方法的,它的数据类型是xml是基于soap实现传输: 第二种是 ...
- 【Azure Batch】在中国区批处理服务(Mooncake Batch Account)上实验自动池(Auto Pool)的创建/删除
问题描述 在Azure Batch的介绍文档中,提出了自动池的概念, 它可以在任务完成后,自动删除Pool资源,详细介绍:https://docs.azure.cn/zh-cn/batch/nodes ...