Induction vs. Spring MVC - 5 serious issues that impact your project
- Induction vs. Spring MVC - 5 serious issues that impact your project
Perhaps the most important motivation to create Induction was the observation that the major Java MVC frameworks failed in addressing a number of issues fundamental to the maintainability of complex web applications. This article compares Induction with Spring MVC to illustrate these issues, and how they are addressed in Induction.
The issues discussed here apply to every major MVC framework, so why then discuss them in the context of Spring MVC? Well, a comparison done with a specific framework can be more precise and has a better defined audience. Spring MVC is a popular MVC framework so naturally the question has been raised by developers I have met as to what the compelling reasons are in favor of Induction vs. Spring MVC. To be sure the author has a lot of goodwill and respect for the folks behind Spring, and maintains that modules of the Spring framework such as the core IoC container are a great application-tier choice for Induction powered web applications.
This article is based on the features in Induction 1.2.0b and Spring MVC 2.5. Now let's take a look at five issues that come up in web application using Spring MVC.
The Unsolved Issues and the Solution
#1 Too much time is wasted redeploying and restarting the web container
Most frameworks including Spring MVC require that application be built, deployed and the web container restarted for every change the developer makes. The author has always seen this as a needless suffering that too many Java developers have accepted as the status quo, while our colleagues using more dynamic languages such as Groovy, Python, Ruby, PHP may be quietly wondering how we get any work done. The loss in productivity is a serious one.
From its initial design Induction sought to address this unproductive deploy/restart cycle. At the heart of Induction is a reloading classloader that allows most changes to your controllers, views and models to be "hot" deployed. This means that, for example, after changing the source for a controller just recompile the controller in your IDE and reload your web page. Yes, its that simple.
#2 Zero configuration controllers
Induction in contrast to Spring MVC requires zero configuration per controller. So no configuration for the controller in an XML file, annotation or anywhere else! All Induction controllers are multi-action and their constructors and methods are "auto-wired" (in Spring parlance) by default.
#3 Rule-based configuration of URL mappings
Here I am referring to how URLs are mapped to controllers (and views), for example the configration that says map /login.action to the class myapp.web.LoginController. I am indeed very surprised (honestly a little appalled) that the status quo even in Spring MVC is to expect these mappings to be defined as part of the configuration for each controller. Here are the problems with this approach:
- Enumerating these mappings whether in an XML or using annotations is tedious work (especially when it can be avoided!)
- There is not a way to automate the enforcement of an application-wide convention (or pattern) for these mappings
- There are much better ways to do this
Induction has the concept of a controller resolver that is a developer pluggable component responsible for resolving an HTTP request to a fully qualified controller class name and method. Induction ships with a very powerful built-in controller resolver called the ShortURLResolver.
The design of the ShortURLResolver was partly inspired by power of the mod_rewrite engine in the Apache HTTP server. Induction allows the mapping from URLs to controller class names to be done using a scheme similar to the rewrite rules in Apache. With a single rule (specified using two regular expressions) you are able to map a URL pattern to set of controllers. Shown below is the <controller-mapping> section of the Induction configuration file from the sample demoapp that ships with Induction:
<controller-mapping> <url-to-class-map> <url-pattern>/(\w+)(?:\.(\w+))?\.action</url-pattern> <class-pattern>(?:.*\.)?(\w*)Controller</class-pattern> <class-packages>demoapp</class-packages> </url-to-class-map> <default-handler-method>handler</default-handler-method> <ignore-handler-method-case>true</ignore-handler-method-case> </controller-mapping>
The above rule will map a URL like http://somehost.com/counter.action to the controller class demoapp.counter_app.CounterController class, and default handler method in that class. This same rule will also map a URL like http://somehost.com/counter.incrementCounter.action to the controller class demoapp.counter_app.CounterController class, and the specific method incrementCounter in that class.
The <controller-mapping> section can have an unlimited number of <url-to-class-map> sections, each <url-to-class-map> is checked in the order it is configured, and the request is dispatched to the first match. The example above has only one <url-to-class-map> section. There is detailed discussion of how the above rule works in the Using Resolvers Tutorial.
#4 Keep controllers free of URLs
Web applications frequently use redirects in controllers when returning a new view to the user after a successful or failed operation. A redirect to a page prevents the user from inadvertently resubmitting POST entered on the previous page.
Most, if not all, MVC frameworks expect that you to specify the redirect in terms of a plain string that codifies a URL. Ok, so what's the big deal? Well, URLs are mapped to controllers/views somewhere (as discussed in #3 above), now if this mapping is changed your redirect will break and you will not even know it!
In a web application the target of almost every redirect is to a another controller or view. So therefore would it not make sense that the redirect be specified using the type of the controller or view class? As in, for example, return new Redirect( MyOrderController.class ), instead of using a string to specify the URL that maps to that controller? The key idea here is that the framework translates the controller type to a fully qualified URL using a resolver like the controller resolver discussed in #3 above, but working in the reverse driection to go from a type to a URL. Induction allows exactly this scheme.
The key here is that:
- Your code defines all redirects in terms of your controller/view types so your application is fully independent of the structure of your URLs
- The configuration to map your controllers/views to URLs is outside your app
- The configuration to map your URLs to controllers/views is outside your app
The Spring MVC documentation raises the above concern about hard-coding URLs in a controller. But as described there the best solution proposed is to use logical view names, which unfortunately are still strings (which cannot be reliably tracked by an IDE). Also the mapping of the logical view name to a URL in Spring MVC seems surprisingly simplistic in contrast to Induction which gives the developer the full power of a redirect resolver.
#5 Views are important and should be represented by types not strings
View objects are an important part of an MVC application, still like most frameworks Spring MVC uses strings to reference views. So what's the issue with this? Well, if you wanted to know which controllers use a specific view the Java dependency analysis tools of your IDE won't help. If you wanted to rename/refactor a view you are on your own again.
In Induction, each view is represented by a Java class. So if you need to know which controllers use a view its simple to use your IDE to find the precise answer. Also if you rename a view using the refactoring tools of your IDE, the controllers will automatically be kept in sync. In Induction, for views rendered using a templating engine, the class representing a view serves as a container for the data available to template.
Views are discussed in more detail in the Using Views Tutorial.
MVC frameworks continue to play an important role in the Java world. The hope is that developers will embrace the simplicity and power of Induction that has been honed by a decade of experience observing the pitfalls encountered in large web applications projects.
It is also hoped that raising these issues will motivate other MVC frameworks to address these issues in future versions.
Adinath Raveendra Raj
July 28, 2009