Issue Details (XML | Word | Printable)

Key: SPR-667
Type: New Feature New Feature
Status: Resolved Resolved
Resolution: Fixed
Priority: Minor Minor
Assignee: Juergen Hoeller
Reporter: James Estes
Votes: 1
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
Spring Framework

Autowiring Struts Request Processor

Created: 31/Jan/05 08:08 AM   Updated: 26/Mar/06 11:54 AM   Resolved: 26/Mar/06 11:54 AM
Component/s: SpringWEB
Affects Version/s: 1.1.4
Fix Version/s: 2.0 M4

Time Tracking:
Not Specified

File Attachments: 1. Zip Archive request_processors_src.zip (4 kB)
2. Zip Archive request_processors_src_1.1.4.zip (4 kB)



 Description  « Hide

I have used the DelegatingTilesRequestProcessor a lot on my team. I quite regularly get developers getting NPEs when trying to access their service object...invariably it is because they forgot to put their bean definition for their action in the context. I figured it would be easy to create a RequestProcessor that would autowire the actions (similar to the base unit tests) and negate the need for the actions in the xml file all together. So, I created an 'AutowiringRequestProcessor' and 'AutowiringTilesRequesProcessor' that will load an action and autowire it by name. From the javadoc for them:

A custom Struts RequestProcessor that will load and autowire actions using autowire-by-name against beans defined in the ContextLoaderPlugIn's WebApplicationContext. The actions are also cached by class name so that Action instances will be shared across action mappings. Given the stateless nature of the Actions, this is typically the desired behavior.

With this processor and the ContextLoaderPlugin in place, all the developer needs to do to express a dependency is add a setter for that dependency on the Action. For example, if there is a bean in the context whose id is 'fooService' and an action requires that service, it simply needs to have a 'setFooService' method. The RequestProcessor will inject the dependency when the Action is loaded.

When autowire-by-name is not appropriate for a few of the actions, then those actions can be explicitly wired up by placing a bean in the context for the action. The Action's 'name' attribute is the same as the action mapping (the way it is done in the DelegatingTilesRequestProcessor). These actions will still be cached by classname, so if 2 action mapping point to the same Action class, the RequestProcessor will return the same instance.



James Estes added a comment - 31/Jan/05 08:10 AM

Source for the 2 Request Processors.


James Estes added a comment - 01/Feb/05 01:57 PM

Fixed code to reflect new heirarchy of the XmlWebApplicationContext (extends from AbstractRefreshableApplicationContext instead of just AbstractXmlApplicationContext).


Dan Washusen added a comment - 02/Feb/05 05:09 PM

That would be very useful.


Matthew Sgarlata added a comment - 04/Feb/05 08:14 AM

I like this idea! In addition to removing the double-configuration of Struts actions, this should cut down the startup time of my app by quite a bit, since it will only need to instantiate 1 ForwardAction instead of 100.


Yann Cébron added a comment - 08/Mar/05 09:23 AM

Thanks for this contribution, I was thinking about implementing something like this myself before. Works like a charm, please consider including this in Spring1.2


Alef Arendsen added a comment - 09/Mar/05 03:46 PM

I used this approach as well once but instead I created a common base class that did the autowiring for me.

About this solution: the current impl. is inheriting from DelegatingRequestProcessor. Shouldn't it just extend the normal RequestProcessor? The delegating RP looks up a Spring bean in the application context, which is not what you need, is it?

I think the best approach would be to configure your actions in Struts and when they are processed by the RP, autowire them. This means you don't have to configure them in Spring anymore.

Also, is the autowire by name the default you wan't to use? Please advise...


James Estes added a comment - 09/Mar/05 09:08 PM

In the implementation provided I was trying to be flexible. I extend from the DelegatingRequestProcessor to handle situations when you want more control over the wiring. So the impl lets the superclass attempt to find the bean in the container first...if found it uses it. If not found, it will instantiate and autowire it by name. I thought it was kind of a best of both worlds: autowire by name for most actions...then for actions that need more care, wire them directly. Honestly in the projects I've used it on, we never had a need to directly wire the action...but it was just TOO easy to allow the flexibility.

I did autowire by name because that is what fit best with the projects I needed it for. There were times when we had multiple implementations of a service interface that needed to be autowired, in these cases autowire by name suited best. The naming convention was already such that it required no code changes.

I'm happy to see some movement on this. Its been a really handy piece of code for me.


Yann Cébron added a comment - 10/Mar/05 02:09 AM

+1 for autoWireByName as default, this is IMHO the best fit for most situations.

How about a configuration option for autoWireType, so it can be reconfigured to e.g. autoWireByType via the struts-config.xml?


Juergen Hoeller added a comment - 26/Mar/06 11:54 AM

I've finally added an AutowiringRequestProcessor and corresponding AutowiringTilesRequestProcessor. Those essentially delegate straight to the ApplicationContext's AutowireCapableBeanFactory to autowire the bean properties of every created Action instance.

The default autowire mode is by type, since we do use this as a default in other places as well. However, I do see the point that autowiring by name is often preferable (and I generally agree with that). The autowire mode can be switched through the "spring.autowire" init-param of the Struts ActionServlet (for example with value "byName"). Accordingly, dependency checking can be activated through the "spring.dependencyCheck" init-param (for example with value "true").

Juergen