We were building a new web application recently, and being in a position to influence the development, I promptly chose to use theMaven2 web application directory structure. Spring is now our web application framework of choice, so that was a given. We have also had very good results with Tiles in our legacy web application, thanks to the efforts of one of my colleagues, so we also wanted to use Tiles here. I had set up a web application at my previous job to work with Spring and Tiles about 3 years ago, but the details were hazy, and the Tiles integration in our legacy application uses some proprietary components, so I decided to figure this out afresh for this project. Surprisingly, there does not seem to be much documentation about Spring/Tiles integration on the web, but I was able to build an example by piecing together information from various sources, which I describe here. Hopefully, the information will help someone in a similar situation.
We start off with a standard Spring application, with the web.xml containing a reference to the Spring DispatcherServlet, as shown below. The web.xml file lives in src/main/webapp/WEB-INF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <servlet> <servlet-name>myapp</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet><servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
</web-app>
|
The myapp-servlet.xml referenced by the web.xml above. The myapp-servlet.xml also lives in src/main/webapp/WEB-INF and is shown below. The myapp-servlet.xml sets up the TilesConfigurer with the location of the tiles configuration file (tiles-def.xml), sets up the Tiles view resolver, and specifies the URL mappings to the respective Spring controllers. It also imports non-web bean definitions from the applicationContext.xml in src/main/resources. This is a personal preference, since I like to be able to unit test the non-web components using JUnit, and breaking this up into a separate configuration file makes this easier.
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
|
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd"> <import resource="classpath:applicationContext.xml" /> <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles.TilesConfigurer"> <property name="definitions"> <list> <value>/WEB-INF/tiles-def.xml</value> </list> </property> </bean> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="requestContextAttribute" value="requestContext"/> <property name="viewClass" value="org.springframework.web.servlet.view.tiles.TilesView"/> </bean> <!-- URL Mappings --> <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="alwaysUseFullPath" value="true"/> <property name="mappings"> <props> <prop key="/example.html">exampleController</prop> </props> </property> </bean></beans>
|
We then define the various tiles. Our example layout contains 4 tiles, one each for static content for the header and footer, one for the left navigation toolbar, and one for the main body of the page. The tiles-def.xml lives in src/main/webapp/WEB-INF and is shown below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd">
<tiles-definitions> <!-- Components --> <definition name="head-tile" path="/example/tiles/head.jsp"/> <definition name="left-nav-tile" path="/example/tiles/leftnav.jsp"/> <definition name="body-tile" path="/example/tiles/body.jsp"/> <definition name="foot-tile" path="/example/tiles/foot.jsp"/> <!-- Pages --><!-- Example -->
<definition name="example" path="/example/example.jsp">
<put name="head-position" value="head-tile"/>
<put name="left-nav-position" value="left-nav-tile"/>
<put name="body-position" value="body-tile"/>
<put name="foot-position" value="foot-tile"/>
</definition>
</tiles-definitions>
|
Currently the only dynamic component is the main body, which uses the "who" parameter to fill out the "Hello ${who}" header. All others are static. The example.jsp page appears below, followed by the different tiles. The locations are in the path attribute in the definitions in the tiles-def.xml file. The root is at src/main/webapp, so /example is actually src/main/webapp/example.
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
|
<%-- src/main/webapp/example/example.jsp --%>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>
<html>
<head><title>Example Page</title></head>
<body>
<table cellspacing="0" cellpadding="0" border="0">
<tr>
<td colspan="2">
<tiles:insert attribute="head-position"/>
</td>
</tr>
<tr>
<td width="25%">
<tiles:insert attribute="left-nav-position"/>
</td>
<td width="75%">
<tiles:insert attribute="body-position"/>
</td>
</tr>
<tr>
<td colspan="2">
<tiles:insert attribute="foot-position"/>
</td>
</tr>
</table>
</body>
</html>
|
I realize that using table tags to layout pages are kind of frowned upon nowadays, but if you have been reading my posts, you will realize that I am not exactly a UI guru. So please bear with me, and mentally replace the table tags with the appropriate CSS magic that is less offensive. The tiles are quite simple, and they are shown below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<!-- src/main/webapp/example/tiles/head.jsp -->
<h1>Da Korporate Header go here</h1>
<!-- src/main/webapp/example/tiles/leftnav.jsp -->
<ol>
<li>Foo</li>
<li>Bar</li>
</ol>
<!-- src/main/webapp/example/tiles/body.jsp -->
<h2>Hello ${who}</h2>
... body text filler ...<!-- src/main/webapp/example/tiles/foot.jsp -->
<h1>Da Korporate Footer go here</h1>
|
So in effect, tiles have given us the ability to reuse JSP snippets on different pages. It is quite likely that the header and footer tiles, and perhaps the left nav tile, will be used across the entire application. So we need to create new tiles for only the body element for different applications.
The controller that backs this is referenced as exampleController in the myapp-servlet.xml and defined more fully in the applicationContext.xml file in src/main/resources. This file currently contains only the controller definition, but could be used to declare beans that the controller(s) depend on as well.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd"><!-- ExampleController -->
<bean id="exampleController" class="com.mycompany.myapp.example.ExampleController">
<property name="viewName" value="example"/>
</bean>
</beans>
|
The actual Java code is quite simple. All it does is pick up the parameter "who" from the URL, and pass it through to the view as a ModelAndView attribute. The java tree is rooted at src/main/java, in case you did not already know.
1
2
3
4
5
6
7
8
9
10
11
12
|
public class ExampleController extends ParameterizableViewController {public ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response) throws Exception {
String who = ServletRequestUtils.getStringParameter(request, "who");
ModelAndView mav = new ModelAndView();
mav.addObject("who", (who == null ? "NULL" : who));
mav.setViewName(getViewName());
return mav;
}
}
|
Start up the web application from the command line with "mvn jetty6:run" and hit the URL: http://localhost:8081/myapp/example.html?who=Sujit to see the following page:
We can also make certain tiles "smarter", in the sense that the Java logic backing these components need not be supplied by the main Spring controller, but can be specified separately. This can be useful when designing widgets for your web pages, which need to do significant processing on the request parameters before rendering the output. We could also refactor the logic out to some kind of service and have the main controller make a single call into it to get the renderable data, but this still means that we have to remember to pull data for each component in every new page controller we write. Specifying a controller for a tile is done in the controllerClass attribute in the tiles definitions.
For our example, we will make the left nav component smart. Depending on the value of the parameter "type" in the URL, it will display different lists. This requires specifying the controllerClass in the tiles definition for left-nav-tile in tiles-def.xml.
1
2
|
<definition name="left-nav-tile" path="/example/tiles/leftnav.jsp"
controllerClass="com.mycompany.myapp.example.LeftNavController"/>
|
The controller is not a Spring controller, but a Tiles Controller. The code for that is shown below:
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
|
public class LeftNavController extends ControllerSupport { private String[][] menuItems = { new String[] {"Foo", "Bar"}, new String[] {"London", "New York", "San Francisco", "Brussels"}, new String[] {"Engineering", "Finance", "Marketing"} };public void execute(ComponentContext tileContext,
HttpServletRequest request,
HttpServletResponse response,
ServletContext servletContext) throws Exception {
// decide what kind of menu to show based on parameter "type"
String menuTypeStr = (String) tileContext.getAttribute("type");
int menuType = 0;
try {
menuType = Integer.parseInt(menuTypeStr);
} catch (NumberFormatException e) {}
if (menuType < 0 || menuType > (menuItems.length - 1)) {
menuType = 0;
}
String[] selectedMenuItem = menuItems[menuType];
request.setAttribute("menu", selectedMenuItem);
}
}
|
One small wrinkle. The type parameter has to be injected into the tile context for the left-nav tile. This is done by setting it in the layout example.jsp file from the request using a tiles:put element, like so:
1
2
3
4
5
6
7
8
9
10
11
12
|
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles" %>
<html>
...
<td width="25%">
<tiles:insert attribute="left-nav-position">
<tiles:put name="type" value="${param.type}"/>
</tiles:insert>
</td>
...
</html>
|
The corresponding tile leftnav.jsp in src/main/webapp/example/tiles also has to be modified to show the "menu" object we just pushed into the context using the LeftNavController. Here it is:
1
2
3
4
5
6
7
|
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<ol>
<c:forEach var="menuItem" items="${menu}">
<li>${menuItem}</li>
</c:forEach>
</ol>
|
Now, hitting the application with http://localhost:8081/aetna/example.html?who=Sujit&type=1 will produce the following page, which shows us that the type parameter is being correctly interpreted.
So this is it. The whole thing is not terribly complicated, but requires you to mess with a lot of XML files. The good news is that working with Tiles can significantly speed up your web application development and make it easier, as well as enforce a uniform look and feel to your web application. And once you set it up, and developers get used to the process of adding components and layouts, it will just become second nature and you will wonder how you worked without Tiles.
출처 : http://sujitpal.blogspot.com/2007/06/springtiles-example-web-application-on.html