Using Spring MVC Controllers in Grails
Thursday, March 27th, 2008Update 03/22/2010: As of version 1.2 Grails has support for Spring MVC controllers; check out the 1.2 release notes
Groovy is slower than Java and sometimes dramatically slower. Realistically, this has little impact on a web application since response time is affected more by the database and network latency, so as long as the slowdown isn’t too dramatic, the benefits of Groovy and Grails far outweigh these concerns. And Grails is still way faster than Rails 🙂
But having said that, I was wondering how to use a regular Java Spring MVC controller and JSP instead of a Grails controller and a GSP (both of which use Groovy). Turns out it’s pretty easy:
-
Register the traditional Spring dispatcher servlet in
web.xml
(you’ll need to have rungrails install-templates
). In this example the name (SpringMVC) isn’t important, use whatever you want, and I’ve chosen to map *.action URLs to this controller and let Grails handle the rest:
<servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>*.action</url-pattern> </servlet-mapping>
- Generate web-app/WEB-INF/SpringMVC-servlet.xml:
<?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:p='http://www.springframework.org/schema/p' xmlns:lang='http://www.springframework.org/schema/lang' xsi:schemaLocation=' http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd'> <bean id='mvcHandlerMapping' class='org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping' p:order='1'> <property name='interceptors'> <list> <ref bean='openSessionInViewInterceptor' /> <ref bean='localeChangeInterceptor' /> </list> </property> </bean> <bean id='mvcViewResolver' class='org.springframework.web.servlet.view.UrlBasedViewResolver' p:viewClass='org.springframework.web.servlet.view.InternalResourceView' p:order='1' p:prefix='/WEB-INF/jsp/' p:suffix='.jsp' /> <bean name='baseSimpleController' abstract='true' p:cacheSeconds='0'/> <bean name='jspController' class='com.foo.spring.controller.JspController' parent='baseSimpleController' abstract='true' /> <!-- actions --> <bean name='/test.action' class='com.foo.spring.controller.TestController' parent='baseSimpleController' p:successView='test' /> <bean name='/other.action' parent='jspController' p:successView='other' /> </beans>
And that’s it. Some notes:
- the handler mapping uses the id
mvcHandlerMapping
since Grails will create one using the standard name ofhandlerMapping
- since handler mappings are auto-discovered by default, you need to set the
order
attribute to something lower than the Grails mapping’s (which uses the default value ofInteger.MAX_VALUE
) so this mapping is accessed first - the
HandlerInterceptor
s that are configured for the Grails mapping (OpenSessionInView, LocaleChange) won’t be automatically available to this mapping, but it’s simple to borrow them since they’re registered as beans; you can also add other custom interceptors to the list - I’ve created an optional abstract parent controller bean (
baseSimpleController
) for simple controllers (single-page, i.e. not form or wizard controllers) - I’ve also created a simple controller that just shows a JSP – this is useful for pages that don’t have any controller logic:
package com.foo.spring.controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController; public class JspController extends AbstractController { private String _successView; @Override protected ModelAndView handleRequestInternal( final HttpServletRequest request, final HttpServletResponse response) { return new ModelAndView(_successView); } public void setSuccessView(final String view) { _successView = view; } }
I’ve mapped two sample URLs – /test.action
, which uses a controller, and /other.action
, which uses JspController
to just show other.jsp
.
Note that it is possible to use JSPs with Grails; Grails looks for a GSP using the specified name, but if it doesn’t find one it looks for a JSP (under /WEB-INF/grails-app/views/
) and uses that if it exists. So another option is to use Grails controllers and JSP.
Big caveat: I haven’t used this in production yet – I’m just prototyping so I’ll have this available in the future just in case.