forked from thymeleaf/thymeleaf.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy paththvsjsp.html
More file actions
executable file
·528 lines (420 loc) · 19.5 KB
/
thvsjsp.html
File metadata and controls
executable file
·528 lines (420 loc) · 19.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Spring MVC view layer: Thymeleaf vs. JSP - Thymeleaf: java XML/XHTML/HTML5 template engine</title>
<link rel="stylesheet" type="text/css" media="all" href="css/thymeleaf.css" />
<link rel="shortcut icon" href="http://www.thymeleaf.org/favicon.ico" />
<script type="text/javascript" src="https://apis.google.com/js/plusone.js">
{lang:'en', parsetags:'explicit'}
</script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-1276954-9']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<script type="text/javascript" src="sh/scripts/shCore.js"></script>
<script type="text/javascript" src="sh/scripts/shBrushXml.js"></script>
<script type="text/javascript" src="sh/scripts/shBrushJava.js"></script>
<link href="sh/styles/shCore.css" rel="stylesheet" type="text/css" />
<link href="sh/styles/shThemeThymeleaf.css" rel="stylesheet" type="text/css" />
</head>
<body lang="en" dir="ltr">
<div id="page">
<div id="menu">
<ul>
<li><a href="index.html" title="Home">Home</a></li>
<li><a href="features.html" title="Features">Features</a></li>
<li><a href="download.html" title="Download">Download</a></li>
<li><a href="documentation.html" title="Documentation">Documentation</a></li>
<li><a href="ecosystem.html" title="Ecosystem">Ecosystem</a></li>
<li><a href="http://forum.thymeleaf.org" title="User Forum">User Forum</a></li>
<li><a href="issuetracking.html" title="Issue Tracking">Issue Tracking</a></li>
</ul>
</div>
<div id="header">
<a href="index.html" title="Thymeleaf home"><img src="images/thymeleaflogonameverysmall.png" class="logo" alt="Thymeleaf Template Engine"/></a>
</div>
<div id="breadcrumb">
<a href="index.html">thymeleaf</a>
::
<a href="documentation.html">documentation</a>
::
articles
::
<span class="current">spring mvc view layer: thymeleaf vs jsp</span>
</div>
<div id="content">
<h1>Spring MVC view layer: Thymeleaf vs. JSP</h1>
<p>
In this article we will compare the same page (a subscription form) created
twice for the same Spring MVC application: once using Thymeleaf and another time using
JSP, JSTL and the Spring tag libraries.
</p>
<p>
All the code seen here comes from a working application. You can download the source code
of such application from the <a href="documentation.html">documentation page</a>.
</p>
<h2>Common requirements</h2>
<p>
Our customers need a form for subscribing new members to a message list, with two fields:
</p>
<ul>
<li>Email address</li>
<li>Type of subscription (receive all emails, daily digest)</li>
</ul>
<p>
We will also need this page to be HTML5 and completely internationalizable, extracting all texts and messages
from resource files already configured in our <kbd>MessageSource</kbd> objects in our Spring
infrastructure.
</p>
<p>
Our application will have two <kbd>@Controller</kbd>s, which will contain exactly the same code
but will forward to different view names:
</p>
<ul>
<li><kbd>SubscribeJsp</kbd> for the JSP page (the <kbd>subscribejsp</kbd> view).
<li><kbd>SubscribeTh</kbd> for the Thymeleaf page (the <kbd>subscribeth</kbd> view).
</ul>
<p>
We will have the following classes in our model:
</p>
<ul>
<li><kbd>Subscription</kbd> form-backing bean with two fields: <kbd>String email</kbd> and
<kbd>SubscriptionType subscriptionType</kbd>.</li>
<li><kbd>SubscriptionType</kbd> an enum modeling the <kbd>subscriptionType</kbd> field of the form,
with values <kbd>ALL_EMAILS</kbd> and <kbd>DAILY_DIGEST</kbd>.</li>
</ul>
<p>
<i>(In this article we will just focus on the JSP/Thymeleaf template code. If you want
to know implementation details for the controller code or Spring configuration, check
the source code in the downloadable package)</i>
</p>
<h2>Doing it with JSP</h2>
<p>
This is our page:
</p>
<div class="caption">
<img src="images/thvsjsp_jsp1.png" />
</div>
<p>
And this is our JSP code, making use of both JSTL (<kbd>core</kbd>) and Spring (<kbd>tags</kbd> and
<kbd>form</kbd>) JSP tag libraries:
</p>
<script type="syntaxhighlighter" class="brush:html"><![CDATA[
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %><%@
taglib prefix="s" uri="http://www.springframework.org/tags" %><%@
taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><%@
page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><!DOCTYPE html>
<html>
<head>
<title>Spring MVC view layer: Thymeleaf vs. JSP</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all" href="<s:url value='/css/thvsjsp.css' />"/>
</head>
<body>
<h2>This is a JSP</h2>
<s:url var="formUrl" value="/subscribejsp" />
<sf:form modelAttribute="subscription" action="${formUrl}">
<fieldset>
<div>
<label for="email"><s:message code="subscription.email" />: </label>
<sf:input path="email" />
</div>
<div>
<label><s:message code="subscription.type" />: </label>
<ul>
<c:forEach var="type" items="${allTypes}" varStatus="typeStatus">
<li>
<sf:radiobutton path="subscriptionType" value="${type}" />
<label for="subscriptionType${typeStatus.count}">
<s:message code="subscriptionType.${type}" />
</label>
</li>
</c:forEach>
</ul>
</div>
<div class="submit">
<button type="submit" name="save"><s:message code="subscription.submit" /></button>
</div>
</fieldset>
</sf:form>
</body>
</html>
]]></script>
<h2>Doing it with Thymeleaf</h2>
<p>
Now, let's do the same with Thymeleaf. This is our page:
</p>
<div class="caption">
<img src="images/thvsjsp_th1.png" />
</div>
<p>
And this is our Thymeleaf code:
</p>
<script type="syntaxhighlighter" class="brush:html"><![CDATA[
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring MVC view layer: Thymeleaf vs. JSP</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all"
href="../../css/thvsjsp.css" th:href="@{/css/thvsjsp.css}"/>
</head>
<body>
<h2>This is a Thymeleaf template</h2>
<form action="#" th:object="${subscription}" th:action="@{/subscribeth}">
<fieldset>
<div>
<label for="email" th:text="#{subscription.email}">Email: </label>
<input type="text" th:field="*{email}" />
</div>
<div>
<label th:text="#{subscription.type}">Type: </label>
<ul>
<li th:each="type : ${allTypes}">
<input type="radio" th:field="*{subscriptionType}" th:value="${type}" />
<label th:for="${#ids.prev('subscriptionType')}"
th:text="#{'subscriptionType.'+${type}}">First type</label>
</li>
<li th:remove="all"><input type="radio" /> <label>Second Type</label></li>
</ul>
</div>
<div class="submit">
<button type="submit" name="save" th:text="#{subscription.submit}">Subscribe me!</button>
</div>
</fieldset>
</form>
</body>
</html>
]]></script>
<p>
Things to notice here:
</p>
<ul>
<li>This looks much more HTML-ish than the JSP version — no strange tags, just some
meaningful attributes.</li>
<li>Variable expressions (<kbd>${...}</kbd>) are Spring EL and execute on model attributes, asterisk expressions
(<kbd>*{...}</kbd>) execute on the form backing bean, hash expressions (<kbd>#{...}</kbd>)
are for internationalization and link expressions (<kbd>@{...}</kbd>) rewrite URLs.
(<i>If you want to know more about this, have a look at the
<a href="standarddialect5minutes.html">"Getting started with the Standard Dialect in 5 minutes"</a>
guide</i>).</li>
<li>We are allowed to have prototype code there: for example, we can set an <kbd>Email:</kbd> text in the label
for the first field, knowing that Thymeleaf will substitute it with the internationalized
text with key <kbd>subscription.email</kbd> when it executes the page.</li>
<li>We have even been able to add an <kbd><li></kbd> for a second radiobutton
just for prototyping pleasure. It will be removed when Thymeleaf executes our page.</li>
</ul>
<h2>Let's change the page style!</h2>
<p>
Imagine that now our pages are written we suddenly decide that we do
not want green anymore for that region around the submit button, but rather a pale blue.
Anyway we're not sure about the exact tone of blue that will fit better, so
we will have to try some combinations before deciding on a specific one.
</p>
<p>
Let's see the steps we would have to take with each technology:
</p>
<h3>Changing the page style using JSP</h3>
<p>
<b>Step 1</b>: <i>Deploy the application into our development server and start it up</i>.
Our JSP page will not render without starting the server, so this will be a requirement.
</p>
<p>
<b>Step 2</b>: <i>Navigate through the pages until we find the one to change</i>. Normally,
the page to change will be one among several dozen pages in our application, and it is quite possible that
in order to reach it we will need to click links, submit forms and/or query databases.
</p>
<p>
<b>Step 3</b>: <i>Fire up firebug, dragonfly, or our favourite in-browser web development
tool</i>. This will allow us to modify our styles acting directly on the browser's DOM, and
thus see immediate results.
</p>
<p>
<b>Step 4</b>: <i>Make the colour changes</i>. Probably trying a couple of different tones
of blue before deciding on the one we like.
</p>
<div class="caption">
<img src="images/thvsjsp_jsp2.png" />
</div>
<p>
<b>Step 5</b>: <i>Copy-paste the changes into our CSS files</i>.
</p>
<p>
Done!
</p>
<h3>Changing the page style using Thymeleaf</h3>
<p>
<b>Step 1</b>: <i>Double-click on the <kbd>.html</kbd> template file itself and
let our browser open it</i>. Being a Thymeleaf template, it will show just fine, only
with template/prototype data (note the subscription type options):
</p>
<div class="caption">
<img src="images/thvsjsp_th2.png" />
</div>
<p>
<b>Step 2</b>: <i>Open the <kbd>.css</kbd> file with our favourite text editor</i>. The
template file statically links to the CSS in its <kbd><link rel="stylesheet" ...></kbd> tag
(with an <kbd>href</kbd> that Thymeleaf substitutes when executing the template by the one
generated by <kbd>th:href</kbd>). So any changes we make to that CSS will be applied
to the static page our browser is displaying.
</p>
<p>
<b>Step 3</b>: <i>Make the colour changes</i>. As was the case with JSP, we will probably
have to try several colour combinations, which will be refreshed in our browser just by
pressing F5.
</p>
<p>
Done!
</p>
<h3>That was a big difference!</h3>
<p>
The difference in the number of steps is not really important here (we could also have used
firebug with the Thymeleaf template). What is really important is the complexity,
the effort and the time required by each of the steps required for JSP. Having to deploy and start the whole
application made JSP just lose.
</p>
<p>
What's more: think of how this difference would evolve if:
</p>
<ul>
<li>Our development server was not local but remote.</li>
<li>Changes didn't involve only CSS but also adding/removing some HTML code.</li>
<li>We still hadn't implemented the required logic in our application to actually
<i>reach that page</i> once deployed.</li>
</ul>
<p>
This last point is an important one: what if our application was still being developed,
the Java logic needed to show this or other previous pages wasn't still working alright, and
we had to show the new colour to our customer? (or even let him/her choose on the fly!)...
</p>
<h3>And what about trying to use the JSP as a static prototype?</h3>
<p>
OK, you can say now, <i>but why did we start the application to modify the JSP instead
of just opening it like you did with the Thymeleaf one? Can't we just do that?</i>.
</p>
<p>
The short answer is NO.
</p>
<p>
But let's try anyway: of course, we will have to rename our file so that its name ends
in <kbd>.html</kbd> instead of <kbd>.jsp</kbd>, but let's see what happens when we
open our browser:
</p>
<div class="caption">
<img src="images/thvsjsp_jsp3.png" />
</div>
<p>
<i>WHAT? Where's our page gone?</i> Well, it's still there, but in order to make our page work
as JSP we had to add lots of JSP tags and features that made it work wondefully when executed
by our web server... but at the same time made it be HTML no more. And therefore made it
undisplayable for a browser.
</p>
<p>
Again, let's remind ourselves what the Thymeleaf template looked
like when we double-clicked on it:
</p>
<div class="caption">
<img src="images/thvsjsp_th2.png" />
</div>
<p>
Definitely not in the same league, right?
</p>
<h2>Got HTML5?</h2>
<p>
But hey — we said at the beginning that our page was going to be HTML5, so...
why don't we use some of the cool new HTML5 form-related features?
</p>
<p>
For example, there is now an <kbd><input type="email" ...></kbd>, which will
make our browser check that the text input by users has the shape of an email address. And
also, there is a new property for all inputs called <kbd>placeholder</kbd> which shows a
text in the field that automatically dissapears when the input gains focus (usually by
the user clicking on it).
</p>
<p>
Sounds good, doesn't it? Unfortunately not all browsers support this yet (as of 2011,
Opera 11 and Firefox 4 do), but we are safe using these features anyway because all
browsers will treat an input of a type they do not understand (<kbd>email</kbd>) as a
<kbd>text</kbd> input, and will silently ignore the <kbd>placeholder</kbd> attribute in
the same way they ignore Thymeleaf's <kbd>th:*</kbd> ones.
</p>
<h3>Doing HTML5 with JSP</h3>
<h4>Before Spring 3.1</h4>
<p>
Spring MVC's JSP tag libraries didn't offer complete support for HTML5 until Spring 3.1, so before
this version there was no way to write an <i>email type</i> input tag other than do it in plain HTML,
like:
</p>
<script type="syntaxhighlighter" class="brush:html;gutter:false"><![CDATA[
<input type="email" id="email" name="email" placeholder="your@email" value="" />
]]></script>
<p>
But this was not correct! In Spring MVC we should never write a JSP input field like that,
because we wouldn't be correctly <i>binding</i> our input to the <kbd>email</kbd> property of
the form-backing bean. In order to do so, we would need to use an <kbd><s:eval/></kbd> tag, which
will apply all the required transformations (like <i>property editors</i>) and make our
plain-HTML tag work as if an <kbd><sf:email/></kbd> tag existed:
</p>
<script type="syntaxhighlighter" class="brush:html;gutter:false"><![CDATA[
<input type="email" id="email" name="email" placeholder="your@email"
value="<s:eval expression='subscription.email' />" />
]]></script>
<h4>Since Spring 3.1</h4>
<p>
In Spring 3.1 there still is no <kbd><sf:email ...></kbd> tag, but the existing
<kbd><sf:input ...></kbd> allows us to specify a <kbd>type</kbd> attribute with value
<kbd>email</kbd>, which will work just fine:
</p>
<script type="syntaxhighlighter" class="brush:html;gutter:false"><![CDATA[
<sf:input path="email" type="email" />
]]></script>
<p>
And this will correctly perform our <i>form bindings</i> :-).
</p>
<h3>Doing HTML5 with Thymeleaf</h3>
<p>
Thymeleaf has complete support for HTML5 (even with Spring 3.0), so we will only have to
change the <kbd>type</kbd> of our input and add a <kbd>placeholder</kbd>, and it will work
out of the box, correctly binding our property and integrating with Spring MVCs
<i>property editors</i> and more importantly, being displayed as a usual <kbd>input</kbd> box
when displayed as a prototype —something the <kbd>sf:input</kbd> tag won't—:
</p>
<script type="syntaxhighlighter" class="brush:html;gutter:false"><![CDATA[
<input type="email" th:field="*{email}" placeholder="your@email" />
]]></script>
<p>
Done!
</p>
<div class="caption">
<img src="images/thvsjsp_th3.png" />
</div>
</div>
<div id="footer">
<div id="googleplus">
<div id="plusone-div" class="plusone"></div>
<script type="text/javascript">
gapi.plusone.render('plusone-div',{"size": "small", "count": "true", "href": "http://www.thymeleaf.org"});
</script>
</div>
<div id="twitter">
<a href="http://twitter.com/thymeleaf" class="twitter-follow-button" data-show-count="false">Follow @thymeleaf</a>
<script src="http://platform.twitter.com/widgets.js" type="text/javascript"></script>
</div>
<div id="copy">
Copyright © The <a href="team.html">THYMELEAF Team</a>. See <a href="documentation.html">applicable licenses</a>.
</div>
</div>
</div>
<script type="text/javascript">
SyntaxHighlighter.all();
</script>
</body>
</html>