That’s right. Struts 1. Not hip. Not happenin’. But in the real world you might have bills to pay and a pair-programmer to feed.
The docs are a bit sketchy with Spring 2.5 stuff.
First you set up your service layer in web.xml
1
2
3
4
5
6
7
8
9
| <context-param>
<description>Used by ContextLoaderListener</description>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-service-layer.xml</param-value>
</context-param>
<listener>
<description>spring. looks for /WEB-INF/applicationContext.xml unless you set contextConfigLocation (see above)</description>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> |
Inside my spring-service-layer.xml file I tell spring to look for classes annotated with one of the 2.5 @Component stereotypes – which include the @Service annotation.
1
2
3
4
| <context:annotation-config />
<context:component-scan base-package="com.rockhoppertech.example"
scoped-proxy="targetClass">
</context:component-scan> |
Here’s a simple service:
1
2
3
4
5
6
7
8
9
| @Service("HelloService")
public class DefaultHelloService implements HelloService {
/*
* @see com.rockhoppertech.example.service.HelloService#getMessage()
*/
public String getMessage() {
return "Yo";
}
} |
Spring will create a root web context in application scope accessible with the attribute name org.springframework.web.context.WebApplicationContext.ROOT. My service appears here.
1
2
3
4
5
| org.springframework.web.context.support.XmlWebApplicationContext@74907490: display name [Root WebApplicationContext]
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
HelloService |
The Spring docs say one approach is to configure Spring to manage your Actions as beans, using the ContextLoaderPlugin which will read the appropriately specified spring config files like this:
1
2
3
4
| <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/action-servlet.xml"/>
</plug-in> |
Then in action-servlet.xml for example you configure your actions as beans.
1
2
| <bean name="/hello" class="com.rockhoppertech.example.struts.action.HelloAction">
</bean> |
Then in struts-config.xml you specify a special Spring controller:
1
2
3
4
| <controller>
<set-property property="processorClass"
value="org.springframework.web.struts.DelegatingRequestProcessor"/>
</controller> |
Also in struts-config.xml you have to change your actions definitions to use a Spring delegate. Notice
that the path matches the bean name specified in action-servlet.xml.
1
2
| <action name="HelloForm" path="/hello"
type="org.springframework.web.struts.DelegatingActionProxy"> |
Ok, that works fine. Not too dry though since I have to specify the bean twice.
Let’s try a newer Spring class.
In struts-config.xml I change the controller to this.
1
2
3
| <controller>
<set-property
property="processorClass"value="org.springframework.web.struts.AutowiringRequestProcessor" /></controller> |
Since it’s named AutowiringRequestProcessor I’ve used the @Autowire annotation in my Action to get the service injected (which will NOT work if you annotate the instance variable and not a setter method).
The docs say that you specify just the usual action tag in struts-config.xml.
But how will the action beans then be created?
I simply annotated HelloAction with @Component. The scan for the service found the action too. The thing is, they live in the parent web context not in the child context created by the contextLoader plugin:
(accessible as an application scoped attribute named org.springframework.web.struts.ContextLoaderPlugIn.CONTEXT)
org.springframework.web.context.support.XmlWebApplicationContext@4e8a4e8a: display name [WebApplicationContext for namespace 'action-servlet']
The beans you specify in your plugin config file live here. But since we’re doing the component scan chacha they aren’t living there anymore. Do you think that’s a bad thing?
Here’s my annotated Action:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
| @Component
public class HelloAction extends Action {
private Logger logger = Logger.getLogger(HelloAction.class);
private HelloService helloService;
/**
* @param helloService
* the helloService to set
*/
@Autowired(required = true)
public void setHelloService(HelloService helloService) {
this.helloService = helloService;
if (logger.isDebugEnabled()) {
logger.debug("Service was set");
}
}
@Override
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
ActionMessages errors = new ActionMessages();
ActionForward forward = null;
if (helloService == null) {
errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
"helloservice.null"));
if (logger.isDebugEnabled()) {
logger.debug("Hello service is null");
}
} else {
request.setAttribute("Greeting", helloService.getMessage());
if (logger.isDebugEnabled()) {
logger.debug("Set request attribute Greeting");
}
}
if (!errors.isEmpty()) {
saveErrors(request, errors);
forward = mapping.findForward("failure");
} else {
forward = mapping.findForward("success");
}
return forward;
} |
Here is a mavenized project with all the sources for this post.