- JSP for the view.
- Spring MVC for server side REST services
- JQuery AJAX for communication between the browser and the server
- JSON for the data interchange between the client browser and the server
- Maven as the build environment
Dependency Architecture
So we have a well structured project we need the following modules. These will be individual Maven projects.website.war– Contains JSP pages and the Spring and web.xml configuration files.website-services.jar– Contains the interface class that specifies the service we want provided by the business service layer. This also includes the corresponding POJOs we’ll work with.domain.jar– This contains the implementation of the services defined inweb-services.jarand all other business logic.
Directory Structure

Maven Project Structure
The Parent pom.xml
pom.xml<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>uk.co.jeeni</groupId> <artifactId>parent</artifactId> <version>1.0</version> <packaging>pom</packaging> <name>Parent for full Website</name> <modules> <module>website</module> <module>website-services</module> <module>domain</module> </modules> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins> </build> </project>
website Project
Website Project Structure/website/pom.xml<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>uk.co.jeeni</groupId> <artifactId>website</artifactId> <version>1.0.0</version> <packaging>war</packaging> <name>Web Application</name> <parent> <groupId>uk.co.jeeni</groupId> <artifactId>parent</artifactId> <version>1.0</version> </parent> <dependencies> <dependency> <groupId>uk.co.jeeni</groupId> <artifactId>website-services</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>uk.co.jeeni</groupId> <artifactId>domain</artifactId> <version>1.0.0</version> </dependency> </dependencies> <build> <finalName>website</finalName> </build> </project>
The above maven project structure is only concerned with the view. As such it only contains index.jsp, the web configuration web.xml fie and the Spring mvc-dispatcher-servlet.xml file. On a larger web app it would also contain css, JavaScript and the public image files.
Although it would seem natural to include the service definition files, maven will not allow the domain module to have a war project as a dependency. Therefor we need to put the service interface files into a separate Maven jar project.
index.jsp
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 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <link rel="stylesheet" type="text/css" href="style.css" /> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script> <script> $(document).ready(function() { $("#addBtn").click(function(){ postNewAddress(); }); $("#findBtn").click(function(){ findAddress(); }); }); function postNewAddress(){ var data = "address=" + ($("#addrId").val()) + "&postcode=" + $("#pcId").val(); postAjax("address/add", data, showResult); } function postAjax(url, dataString, callBackMethod) { $.ajax({type:"POST", dataType: "json", url: url, data: dataString, success: callBackMethod }); } function showResult(data){ $("#result").text(data.result); } function findAddress(){ var url = "address/findByPostcode/" + $("#queryPc").val(); getAjax("GET", url, showFoundAddress); } function getAjax(type, url, callBackMethod) { $.ajax({type:"GET", dataType: "json", url: url, success: callBackMethod }); } function showFoundAddress(address){ $("#fddrId").val(address.address); $("#fpc").val(address.postcode); } </script> <style> label{ width:75px; display:inline-block; } input, textarea { margin:2px; border: solid 1px #696969; } </style> </head> <body style="padding:10px;"> <div style="width:auto;float:left;"> <h1>Add AddressXX</h1> <div style="padding:10px;border: 1px solid #696969;width:300px;"> <label style="vertical-align:top" for="address">Address:</label><textarea id="addrId" name="address" rows="5"></textarea> <label for="postcode">Postcode:</label><input id="pcId" name="postcode"/></br> <button id="addBtn">Add</button></br></br> <div>Result: <span id="result"> </span></div> </div> </div> <div style="width:auto;float:left;margin-left:20px;"> <h1>Find Address</h1> <div style="padding:10px;border: 1px solid #696969;width:300px;"> <label for="postcode">Postcode:</label><input id="queryPc" name="postcode"/><button id="findBtn">Find</button></br> <label style="vertical-align:top" for="fddrId">Address:</label><textarea id="fddrId" disabled="true" name="address" rows="5"></textarea> <label for="fpc">Postcode:</label><input id="fpc" disabled="true" name="postcode"/></br> </div> </div> </body> </html> |
web.xml
<web-app id="WebApp_ID" version="2.4" 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"> <display-name>Spring Web MVC Application</display-name> <servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
mvc-dispatcher-servlet.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <context:component-scan base-package="uk.co.jeeni.domain"/> <mvc:annotation-driven/> </beans> |
website-services
Maven Project Structure
The project contains only an interface for the services which the web application requires from the domain/business tier. It is important the services are specified as part of the application tier module. This is so the lower-level domain module depends on the higher level application module and not the other way round. The application must be the driver for the design and implementation of the domain.
Lets firstly look at the service specification interface.
PublicService.java
1 2 3 4 5 6 |
package uk.co.jeeni.application; public interface PublicService { String addAddress(Address address); Address findByPostcode(String postcode); } |
Address.java
1 2 3 4 5 6 7 8 |
package uk.co.jeeni.application; public interface Address { String getAddress(); void setAddress(String address); String getPostcode(); void setPostcode(String postcode); } |
Address object, then they must both be interfaces. If we use concrete classes then the domain/code> will have a problem because it will have to cater for two types. In the design we are using here the domain can create a single Address class and represent both form via implementing both interfaces. Then application1 will only need to know about it's interface, and application2 will only need to know about it's.
The Domain Project
Thedomain project is structured like this:
It’s only purpose is to provide implementations and business functionality to support the applications which use it. Anything else is unnecessary.
pom.xml
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 |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>uk.co.jeeni</groupId> <artifactId>domain</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <name>Domain Implementation</name> <properties> <spring.version>3.2.1.RELEASE</spring.version> </properties> <parent> <groupId>uk.co.jeeni</groupId> <artifactId>parent</artifactId> <version>1.0</version> </parent> <dependencies> <dependency> <groupId>uk.co.jeeni</groupId> <artifactId>website-services</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.9.12</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> </dependencies> </project> |
PublicServiceImpl.java
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 |
package uk.co.jeeni.domain; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import uk.co.jeeni.application.Address; import uk.co.jeeni.application.PublicService; import java.util.Hashtable; import java.util.Map; @Controller @RequestMapping("/address") public class PublicServiceImpl implements PublicService { private static Map<String, Address> data = new Hashtable<String, Address>(); static { loadSomeData(); } private static void loadSomeData(){ data.put("BS21 7XS", new AddressImpl("Knowles Road", "BS21 7XS")); data.put("BS21 7SF", new AddressImpl("Hallam Road", "BS21 7SF")); } @Override @RequestMapping(value = "/add", method = RequestMethod.POST) @ResponseBody public String addAddress(@ModelAttribute("address")Address address) { String postcode = address.getPostcode(); if(postcode != null && !data.containsKey(postcode)){ data.put(postcode.toUpperCase(), address); } return "{\"result\":\"OK\"}"; } @Override @RequestMapping(value="/findByPostcode/{postcode}", method = RequestMethod.GET) public @ResponseBody Address findByPostcode(@PathVariable String postcode){ Address found = data.get(postcode.toUpperCase()); return found; } @ModelAttribute public Address getAddress(){ return new AddressImpl(); } } |
addAddress method take an Address as a parameter and this is an interface, Spring will throw a exception unless you tell it which concrete class to use. This is done by the annotated method at lines 44 to 47:
@ModelAttribute public Address getAddress(){ return new AddressImpl(); }
AddressImpl.java
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 |
package uk.co.jeeni.domain; import uk.co.jeeni.application.Address; public class AddressImpl implements Address { private String address; private String postcode; public AddressImpl() {} public AddressImpl(String address, String postcode) { this.address = address; this.postcode = postcode; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getPostcode() { return postcode; } public void setPostcode(String postcode) { this.postcode = postcode; } } |
Download this project here:
WIPE_I8190
Pingback: Dependency Inversion Example | Jeeni Software