A Grails Plugin for Spring MVC Controllers
I wrote earlier about using Spring MVC controllers
in a Grails app. I was looking at that again since I thought it might be useful as a plugin. Unfortunately although it did work at the time, I ended up having to do a lot more work to get it going. I guess some things changed in intermediate Grails releases, and I didn’t save the code. But anyway, it’s working now 🙂
Installation
To use it in your app, install like any plugin:
grails install-plugin springmvc
The install script will create a web-app/WEB-INF/jsp
folder if it doesn’t exist, and copy the sample error.jsp
there if it doesn’t exist. It also creates an empty web-app/WEB-INF/SpringMVC-servlet.xml
– the file has to exist but all beans are defined by the plugin or in the app’s resources.groovy
and/or resources.xml
.
Configuration
There are a few configuration options available – all are optional. They’re defined in a springmvc
block in Config.groovy
:
Name | Default Value | Description |
---|---|---|
urlSuffix | ‘action’, i.e. *.action |
the URL pattern for Spring MVC controller URLs |
exceptionMappings | none | Map with exception class names as keys and JSP names as values |
defaultErrorView | ‘error’, i.e. web-app/WEB-INF/jsp/error.jsp |
the default error page if there’s no corresponding mapping for the exception class |
interceptors | none | bean names of HandlerInterceptor s to apply to Spring MVC URLs |
So a configuration block that defines a single interceptor, uses *.action for URLs, and defines a default error JSP and two exeption-specific exception mappings (note that the values for urlSuffix and defaultErrorView are redundant since they’re the default values) would be:
springmvc { interceptors = ['loggingInterceptor'] exceptionMappings = ['java.io.IOException': 'ioexception'] exceptionMappings = ['com.myapp.MyException': 'myex'] defaultErrorView = 'error' urlSuffix = 'action' }
Application Beans
Unlike in Grails, there’s no automatic URL mapping. To connect a controller to a URL you define a Spring bean (in resources.groovy or resources.xml) whose name is the url and the bean class is the controller, e.g.:
'/test.action'(com.burtbeckwith.mvctest.controller.TestController) { cacheSeconds = 0 bookService = ref('bookService') }
This will map http://localhost:8080/yourapp/test.action to TestController. Setting cacheSeconds to 0 instructs Spring to send headers to disable caching. And ‘bookService’ is a dependency injection for BookService to access Book domain instances. The controller returns ‘books’ as it’s view name – this is prefixed by ‘/WEB-INF/jsp/’ and suffixed by ‘.jsp’ to define the JSP that will render the response (i.e. ‘/WEB-INF/jsp/books.jsp’).
You can also define HandlerInterceptor
s in resources.groovy. They should extend HandlerInterceptorAdapter
or implement HandlerInterceptor
directly. Add their bean names to the ‘interceptors’ list and Spring MVC requests will be intercepted. Note that these interceptors are in addition to the standard Hibernate OSIV interceptor and a locale change interceptor.
For example:
loggingInterceptor(com.burtbeckwith.mvctest.interceptor.LoggingInterceptor)
Sample App
You can download a sample app here. It’s pretty simple – it has a single domain class, and a single Grails controller and a Spring MVC controller (to test that both work in the same app). There’s a Grails service to access domain instances that implements a Java interface so it’s callable by the MVC controller. There’s also a sample interceptor (it just logs requests) and a sample JSP 2.0 tag file, date.tag.
The app creates six books in Bootstrap – you can see the data by going to http://localhost:8080/mvctest/test.action. Go to http://localhost:8080/mvctest/regular
to access a regular Grails controller.
Hi burt,
Congrats once again for another useful contribution. It would be awesome if you could document your contributes at http://grails.org/Plugins. Even if it is just copying and pasting the contents you have here in the blog, at least people know where to look for the docs.
Keep it up!
[…] feature is described by VaanNila and for it’s use in Grails I found this older post by Burt Beckwith […]