I started Sleepless in Salt Lake City with an example of Spring MVC that used auto-wiring with Spring 2.5. Sadly, I have lost that code. On the bright side however, Spring has made some advances that I am hoping to catch up with a re-vamp of the original BLOG. This BLOG primarily hopes to demonstrate the use of Spring Java Config spanning different tiers of an application with a simple Spring MVC 3.1 example that one can use for learning as well.
Spring JavaConfig is a means of configuring the Spring Container using pure-java, i.e., without XML if one so desired. It relies on the features of Java 5.X+ such as Generics and Annotations to express what was previously done via XML. Java Config has the following advantages:
1. Injections, as it should be done using features like inheritance, polymorphism, etc.
2. Full control on creation and initialization of beans.
3. Makes refactoring easy without the need for something like Spring IDE
4. Ability to refrain from Classpath scanning as that can be expensive for container initialization
5. Use XML or property support in conjunction if desired
Most of this BLOG is in the form of code with a functional examples provided as well for download and execution. The code shown below assumes that a person is familiar with Spring MVC and does not delve into the basics of the same. The examples used in this BLOG are written with the following objectives:
1. Demonstrate Spring Java Config across the different tiers with no spring XML usage
2. Demonstrate transaction management
3. Demonstate a validator for the beans using JSR-303
4. Demonstate the REST features of Spring XML by having the application double as a RESTful Web Service.
5. Demonstate how Unit testing can be achieved across the tiers when using Java Config
6. Demonstates use of RestTemplate in an integration-test of the Web Service
In short, the making of a really long POST ;-)
The app references in this BLOG is a simple Flight reservation system where flights can be searched and booked. The web application itself is separated into 3 tiers, Web, Service and Data Access. Each of the tiers has its own Java Configuration as shown below with the WebConfig being the aggregator or top level Config:
With the interest of making unit testing easier, the Config classes are separated via interface/impl.
The data access tier defines a Java Config module as shown below:
Of interest in the above is the @EnableTransactionManagement annotation which enables Spring's annotation driven transaction management capabilities for services annotated with @Transactional. The @Transactional annotation instructs the Spring container to provide transactional semantics for the method. A service class annotated with @Transactional is shown below. Also note that the FlightDao and TicketDao beans are defined to be set explicitly via the constructor without the use of the @Autowired annotation. @Autowired could be used as well if desired for having a no-args constructor with the Spring Framework reflectively initializing the DAOs.
The Services tier also has a corresponding Config where the Services of the application are defined:
One might ask the question as to how can one mock out the DaoConfig for testing purposes as it is set to be auto-wired, surely not reflection? Well, on the Configs one cannot use constructor based initialization as Spring's container expects a no-arg constructor. One can however define a setter for the Config and be able to mock the same out. The following sample of the ControllerConfig demonstrates the same:
Annotating a bean with @Controller indicates to Spring that the same is part of the MVC family and to use the same for mapping request urls and servicing web requests. A few other things to note about the controller:
1. Does not need to extend any particular base class
2. The @RequestMapping annotation defines the HTTP Method and resource serviced. In the example shown, "/searchFlights" responds differently to GET and POST requests
All these tier configurations are wired together via WebConfig which also defines the different components required to setup the Spring MVC Servlet. The @Import annotation indicates to the Spring Container which Config's need to be included. Note the same can also be done in the web.xml as part of the Servlet definition if so desired.
Validation of Requests using JSR-303 Bean Validation API:
JSR-303 standardizes the validation for the Java Platform. Use it if you wish for validation. The annotations from the API are used to specify validation constraints and the runtime enforces the same. As this example utilizes Hibernate, adding Hibernate-Validator which is an implementation of the API is a natural choice. In the example provided the Reservation class is annotated with the annotations from the JSR to provide runtime validation and messages. The messages could be obtained from a resource bundle if desired:
Unit Testing:
Unit testing the tiers is very easily supported by the Spring Testing framework. When testing the service layer for example, one does not really need to integrate with the database and the corresponding interactions can be mocked out. For this reason, the objects in the DAO config are provided as mocks as shown below:
Spring MVC Restful Services:
One of the goals of this example was to demonstrate how the web application doubles as a service as well. Consider the following controller which provides representations for the web application and a service consumer:
For more information on the RestTemplate, check out my other BLOG on Rest Client Frameworks.
Side Note:
In this example, I have used a framework called Pojomatic for the convenience it provides in implementing equals(), hashCode() and toString() for my POJOs. I prefer its brevity over using the the tools provided by the Apache commons project which I have used in my previous BLOGs. Check the same out.
Running the Example:
The example provided herewith demonstrates the Flight Spring MVC example. It uses an in memory database which is primed with data on start up. The model package in the example is overloaded as far as its responsibilities go. The POJO's there in serve as database model objects and data transfer objects. The example, unit tests or integration tests are by no means designed to be comprehensive and are only present only for demonstration purposes.
1. Download the example from HERE
2. Execute a mvn jetty:run to start the application.
3. Execute a mvn integration:test to see the Web Service calls being exercised
4. The Flight Web Application is available at http://localhost:8080
5. Web service url's can be accessed directly via: http://localhost:8080/reservations , http://localhost:8080/reservations/{id}, http://locahost:8080/flights, http://localhost:8080/airports
Conclusion:
I find the clarity and control of object initialization and wiring really convenient with Spring Java Config. Walking the dependency tree is also very intuitive. In addition, due to the strong typing, refactoring is made easy even without a tool like Spring IDE. What I like about Spring MVC versus other web frameworks is its simplicity with the Front Controller/Dispatcher pattern. One can use whatever Ajaxian technology they like to enhance the user experience. The framework itself is very non-invasive, i.e., it does not require the use of Spring across all the tiers. The Restful support facilitates easy interaction with Ajax based technologies and makes the web app a true resource based system. I do not use Spring MVC on any production application sadly. If anyone who does have experience with the same stumbles upon this BLOG, I would love to hear of the same.
Take a look at the Spring Security Stateless Cookie Authentication BLOG for an example of the same MVC application but using Twitter Bootstrap and integrated with Spring Security.
Update - I have uploaded a simple presentation that I gave along with supporting code. You can view the same at Spring MVC 3.1 Presentation/Tutorial.
Spring JavaConfig is a means of configuring the Spring Container using pure-java, i.e., without XML if one so desired. It relies on the features of Java 5.X+ such as Generics and Annotations to express what was previously done via XML. Java Config has the following advantages:
1. Injections, as it should be done using features like inheritance, polymorphism, etc.
2. Full control on creation and initialization of beans.
3. Makes refactoring easy without the need for something like Spring IDE
4. Ability to refrain from Classpath scanning as that can be expensive for container initialization
5. Use XML or property support in conjunction if desired
Most of this BLOG is in the form of code with a functional examples provided as well for download and execution. The code shown below assumes that a person is familiar with Spring MVC and does not delve into the basics of the same. The examples used in this BLOG are written with the following objectives:
1. Demonstrate Spring Java Config across the different tiers with no spring XML usage
2. Demonstrate transaction management
3. Demonstate a validator for the beans using JSR-303
4. Demonstate the REST features of Spring XML by having the application double as a RESTful Web Service.
5. Demonstate how Unit testing can be achieved across the tiers when using Java Config
6. Demonstates use of RestTemplate in an integration-test of the Web Service
In short, the making of a really long POST ;-)
The app references in this BLOG is a simple Flight reservation system where flights can be searched and booked. The web application itself is separated into 3 tiers, Web, Service and Data Access. Each of the tiers has its own Java Configuration as shown below with the WebConfig being the aggregator or top level Config:
With the interest of making unit testing easier, the Config classes are separated via interface/impl.
The data access tier defines a Java Config module as shown below:
@Configuration @EnableTransactionManagement public class DefaultDaoConfig implements DaoConfig, TransactionManagementConfigurer { // Dao Initialization @Bean public FlightDao getFlightDao() { return new FlightDaoImpl(getSessionFactory()); } ...other DAOs // Session Factory configuration for Hibernate @Bean public SessionFactory getSessionFactory() { return new AnnotationConfiguration().addAnnotatedClass(Ticket.class) .addAnnotatedClass(Flight.class).addAnnotatedClass(Airport.class) .configure().buildSessionFactory(); } // Transaction manager being used @Override public PlatformTransactionManager annotationDrivenTransactionManager() { return new HibernateTransactionManager(getSessionFactory()); } }
Of interest in the above is the @EnableTransactionManagement annotation which enables Spring's annotation driven transaction management capabilities for services annotated with @Transactional. The @Transactional annotation instructs the Spring container to provide transactional semantics for the method. A service class annotated with @Transactional is shown below. Also note that the FlightDao and TicketDao beans are defined to be set explicitly via the constructor without the use of the @Autowired annotation. @Autowired could be used as well if desired for having a no-args constructor with the Spring Framework reflectively initializing the DAOs.
@Transactional(readOnly = true) public class ReservationServiceImpl implements ReservationService { private final FlightDao flightDao; private final TicketDao ticketDao; public ReservationServiceImpl(FlightDao flightDao, TicketDao ticketDao) {...} @Override @Transactional(rollbackFor = { NoSeatAvailableException.class }, readOnly=false) public Ticket bookFlight(Reservation booking) throws NoSeatAvailableException { ... } @Override public List<Ticket> getReservations() {...} }
The Services tier also has a corresponding Config where the Services of the application are defined:
@Configuration public class DefaultServiceConfig implements ServiceConfig { @Autowired private DaoConfig daoConfig; @Bean @Override public AirlineService getAirlineService() { return new AirlineServiceImpl(daoConfig.getFlightDao(), daoConfig.getAirportDao()); } .........other services ... }
One might ask the question as to how can one mock out the DaoConfig for testing purposes as it is set to be auto-wired, surely not reflection? Well, on the Configs one cannot use constructor based initialization as Spring's container expects a no-arg constructor. One can however define a setter for the Config and be able to mock the same out. The following sample of the ControllerConfig demonstrates the same:
@Configuration public class ControllerConfig { private ServiceConfig serviceConfig; @Autowired public void setServiceConfig(ServiceConfig serviceConfig) { this.serviceConfig = serviceConfig; } @Bean public FlightsController getFlightController() { return new FlightsController(serviceConfig.getAirlineService(), serviceConfig.getAirportService()); } ... other controllers... }The FlightController is shown below:
@Controller public class FlightsController { private final AirlineService airlineService; private final AirportService airportService; public FlightsController(AirlineService airlineService, AirportService airportService) {...} // Flight search page initialization @RequestMapping(value = "/searchFlights.html", method = RequestMethod.GET) public ModelAndView searchFlights() throws Exception { List<Airport> airports = airportService.getAirports(); ModelAndView mav = new ModelAndView("searchFlights"); mav.addObject("airports", airports); return mav; } // Searching for a flight. Note that the Critiera object is automatically bound with // the corresponding form post parameters @RequestMapping(value = "/searchFlights.html", method = RequestMethod.POST) public ModelAndView searchFlights(FlightSearchCriteria criteria) throws Exception { ModelAndView mav = new ModelAndView("searchFlights"); .... FlightSearchResults searchResult = airlineService.getFlights(criteria); mav.addObject("flightSearchResult", searchResult); return mav; } }
Annotating a bean with @Controller indicates to Spring that the same is part of the MVC family and to use the same for mapping request urls and servicing web requests. A few other things to note about the controller:
1. Does not need to extend any particular base class
2. The @RequestMapping annotation defines the HTTP Method and resource serviced. In the example shown, "/searchFlights" responds differently to GET and POST requests
All these tier configurations are wired together via WebConfig which also defines the different components required to setup the Spring MVC Servlet. The @Import annotation indicates to the Spring Container which Config's need to be included. Note the same can also be done in the web.xml as part of the Servlet definition if so desired.
@EnableWebMvc @Configuration @Import({ ControllerConfig.class, DefaultServiceConfig.class, DefaultDaoConfig.class }) public class WebConfig extends WebMvcConfigurerAdapter // Validator for JSR 303 used across the application @Override public Validator getValidator() {...} // View Resolver..JSP, Freemarker etc @Bean public ViewResolver getViewResolver() {...} // Mapping handler for OXM @Bean public RequestMappingHandlerAdapter getHandlerAdapter() {.. } @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } }The annotation @EnableWebMvc is required to tell Spring that this is a MVC application. Implementing the WebMvcConfigurerAdapter allows for customization the Spring Servlet and path handling. Alternatively, one could also extend WebMvcConfigurationSupport directly and provide the necessary customization
Validation of Requests using JSR-303 Bean Validation API:
JSR-303 standardizes the validation for the Java Platform. Use it if you wish for validation. The annotations from the API are used to specify validation constraints and the runtime enforces the same. As this example utilizes Hibernate, adding Hibernate-Validator which is an implementation of the API is a natural choice. In the example provided the Reservation class is annotated with the annotations from the JSR to provide runtime validation and messages. The messages could be obtained from a resource bundle if desired:
public class Reservation { @XmlElement @NotBlank(message = "must not be blank") private String reservationName; @Min(1) @XmlElement private int quantity = 1; @XmlElement @NotNull(message = "Flight Id must be provided") private Long flightId; .... }The web deployment descriptor is where the application converges and the DispatcherServlet is notified of the Context class to use and the location of the WebConfig. As previously mentioned, it is possible to provide a comma separated list of Configs in the web.xml if desired versus importing them in the WebConfig:
<web-app> <servlet> <servlet-name>springExample</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet </servlet-class> <init-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </init-param> <!-- Tell Spring where to find the Config Class This is the hook in to the application. --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>com.welflex.web.WebConfig</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> .... </web-app>
Unit Testing:
Unit testing the tiers is very easily supported by the Spring Testing framework. When testing the service layer for example, one does not really need to integrate with the database and the corresponding interactions can be mocked out. For this reason, the objects in the DAO config are provided as mocks as shown below:
@Configuration @EnableTransactionManagement public class MockDaoConfig implements DaoConfig { @Bean public FlightDao getFlightDao() { return Mockito.mock(FlightDao.class); } ..// Other mocks } @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {MockDaoConfig.class, DefaultServiceConfig.class }, loader = AnnotationConfigContextLoader.class) public class AirportServiceTest { @Autowired private AirportDao airportDaoMock; @Autowired private ApplicationContext ctx; @Test public void getAirportCode() { // Obtain Airport service from the context AirportService a = ctx.getBean(AirportService.class); // Alternatively, instatiate an Airport Service by providing a mock AirportService a2 = new AirportServiceImpl(airportDaoMock); } ... }In the above test, the Config's being used are the concrete implementation of ServiceConfig and a mock of DaoConfig. This enables the testing of the Service tier by mocking the Data access tier. Some people prefer to test each class in isolation by wiring the same up with the required dependencies explicitly while others prefer to obtain the class from the container with all dependencies wired up. The choice of style of testing is a topic for another BLOG but it is sufficient to accept that either style is supported. It is to be noted that one does not need to mock all the beans for testing and can define specific Config objects as required. As with the Service tier mocking out the Data Access tier, the Controller tier can also be tested by mocking out the Service tier objects.
Spring MVC Restful Services:
One of the goals of this example was to demonstrate how the web application doubles as a service as well. Consider the following controller which provides representations for the web application and a service consumer:
@Controller public class ReservationsController { private final AirlineService airlineService; private final ReservationService reservationService; public ReservationsController(ReservationService reservationService, AirlineService airlineService) {...} @RequestMapping("/reservations.html") public ModelAndView getReservationsHtml() {...} @RequestMapping(value = "/reservations/{reservationId}.html", method = RequestMethod.GET) public ModelAndView getReservationHtml(@PathVariable Long reservationId) {...} // Web Service Support to provide XML or JSON based of the Accept Header attribute @RequestMapping(value = "/reservations", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE }) @ResponseBody public Reservations getReservations() {...} @RequestMapping(value = "/reservations/{reservationId}", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE }) @ResponseBody public Ticket getReservation(@PathVariable Long reservationId) {...} .... }In the above shown controller, the getReservationsHtml() returns a HTML representation of the reservations while the call to getReservations() returns an XML or JSON representation of the reservations. The type of representation returned depends on the Accept header provided to the service. The @ResponseBody annotation indicates that a view is not targetted but that the resulting object should be translated directly. The example itself uses Spring OXM for marshalling and unmarshalling XML via JAXB. An integration test for the above controller using RestTemplate is shown below:
public class WebServiceIntegrationTest { private RestTemplate template; private static final String BASE_URL = "http://localhost:9091"; @Before public void setUp() { // Create Rest Template with the different converters template = new RestTemplate(); List<HttpMessageConverter<?>> converters = ...; template.setMessageConverters(converters); } private static final String FLIGHT_NUMBER = "LH 235"; @Test public void reservations() { Flight singleFlight = template.getForObject(BASE_URL + "/flights/" + FLIGHT_NUMBER, Flight.class); Reservation res = new Reservation(); res.setFlightId(singleFlight.getId()); res.setQuantity(10); res.setReservationName("Sanjay Acharya") // Post to create a ticket Ticket ticket = template.postForObject(BASE_URL + "/bookFlight", res, Ticket.class); assertNotNull(ticket); System.out.println("Ticket reserved:" + ticket); // All Reservations Reservations reservations = template.getForObject(BASE_URL + "/reservations", Reservations.class); assertTrue(reservations.getTickets().size() == 1); // Single reservation assertNotNull(template.getForObject(BASE_URL + "/reservations/" + ticket.getId(), Ticket.class)); } }
For more information on the RestTemplate, check out my other BLOG on Rest Client Frameworks.
Side Note:
In this example, I have used a framework called Pojomatic for the convenience it provides in implementing equals(), hashCode() and toString() for my POJOs. I prefer its brevity over using the the tools provided by the Apache commons project which I have used in my previous BLOGs. Check the same out.
Running the Example:
The example provided herewith demonstrates the Flight Spring MVC example. It uses an in memory database which is primed with data on start up. The model package in the example is overloaded as far as its responsibilities go. The POJO's there in serve as database model objects and data transfer objects. The example, unit tests or integration tests are by no means designed to be comprehensive and are only present only for demonstration purposes.
1. Download the example from HERE
2. Execute a mvn jetty:run to start the application.
3. Execute a mvn integration:test to see the Web Service calls being exercised
4. The Flight Web Application is available at http://localhost:8080
5. Web service url's can be accessed directly via: http://localhost:8080/reservations , http://localhost:8080/reservations/{id}, http://locahost:8080/flights, http://localhost:8080/airports
Conclusion:
I find the clarity and control of object initialization and wiring really convenient with Spring Java Config. Walking the dependency tree is also very intuitive. In addition, due to the strong typing, refactoring is made easy even without a tool like Spring IDE. What I like about Spring MVC versus other web frameworks is its simplicity with the Front Controller/Dispatcher pattern. One can use whatever Ajaxian technology they like to enhance the user experience. The framework itself is very non-invasive, i.e., it does not require the use of Spring across all the tiers. The Restful support facilitates easy interaction with Ajax based technologies and makes the web app a true resource based system. I do not use Spring MVC on any production application sadly. If anyone who does have experience with the same stumbles upon this BLOG, I would love to hear of the same.
Take a look at the Spring Security Stateless Cookie Authentication BLOG for an example of the same MVC application but using Twitter Bootstrap and integrated with Spring Security.
Update - I have uploaded a simple presentation that I gave along with supporting code. You can view the same at Spring MVC 3.1 Presentation/Tutorial.
9 comments:
Great example. You should put your code on github, then you won't lose it. :-)
@Dave, thanks for the suggestion. However, if I did not lose my code every once so often, I would not be tempted to make "Great Examples" like this ;-) Kidding of course.
Great by doesnt work on tomcat
Update your pom.xml to exclude version of javax.servlet and javax.jstl coming from joda-time-jsptags and it should run fine in tomcat.
Exclude the following from joda-timetags in pom.xml-
javax.servlet:jsp-api
javax.servlet:jstl
Superb code example !!! Really helpful to learn Spring 3.x !!!
@JVerstry thanks and glad it helped :-)
Wonderful and it works !! ... such a blessing to have a plug & run tutorial that actually works :D
Hi,
Thanks for this good tutorial, I tried :
mvn integration:test
But got this :
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.420s
[INFO] Finished at: Mon Jul 08 13:34:35 EDT 2013
[INFO] Final Memory: 5M/15M
[INFO] ------------------------------------------------------------------------
[ERROR] No plugin found for prefix 'integration' in the current project and in t
he plugin groups [org.apache.maven.plugins, org.codehaus.mojo] available from th
e repositories [local (C:\Users\z994306\.m2\repository), central (http://repo.ma
ven.apache.org/maven2)] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e swit
ch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please rea
d the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/NoPluginFoundF
orPrefixException
C:\Users\z994306\Downloads\springMvcExample>
Post a Comment