1

I'm learning spring dependency injection with Struts2, beased on a web project. In my example, I created a zoo having animals. Animal will talk if injection is succeed. E.g. in the console, we will see dog's talk :

Wowowo ฅ^•ﻌ•^ฅ

However, if injection failed, then we'll see :

zooService bean has not been injected.

Here's the architecture of my application :

application architecture

  • com.zoo.controller.ZooController is the controller for receiving web actions.
  • com.zoo.service.ZooService is the interface for animal's talk
  • com.zoo.service.ZooServiceForDog is the implementation for dog's talk

Problem

Up to the step, everything is OK. And the dependency injection is handled by Spring using an XML file called applicationContext.xml. However, I've 2 types of configuration for this file, the first one Config 1 works but the second Config 2 doesn't.

Injection succeed using config 1.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="zooService" class="com.zoo.service.ZooServiceForDog" />

</beans>

Injection failed using config 2.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="zooController" class="com.zoo.controller.ZooController">
    <property name="zooService" ref="zooServiceBean" />
  </bean>
  <bean id="zooServiceBean" class="com.zoo.service.ZooServiceForDog" />

</beans>

Can somebody explain why the Config 2 cannot work ?


Here're other codes that might be helpful to the issue :

Class com.zoo.controller.ZooController:

package com.zoo.controller;

import com.zoo.service.ZooService;
import com.opensymphony.xwork2.ActionSupport;

public class ZooController extends ActionSupport {

    private static final long serialVersionUID = 1L;
    private ZooService zooService;

    public String live () {
        if (zooService != null) {
            zooService.talk();
        } else {
            System.out.println("zooService bean has not been injected.");
        }
        return SUCCESS;
    }

    public ZooService getZooService() {
        return zooService;
    }

    public void setZooService(ZooService zooService) {
        this.zooService = zooService;
    }
}
Roman C
  • 49,761
  • 33
  • 66
  • 176
Mincong Huang
  • 5,284
  • 8
  • 39
  • 62
  • Related: http://stackoverflow.com/a/34063506/1654265. Mincong, since you're learning, IF Spring has been chosen by you (and not imposed by your company's standards), take a look at [**Java EE** standard dependency injection, **CDI**](http://stackoverflow.com/a/35358961/1654265) (in Struts2 achieved with the [Struts2-CDI-plugin](http://stackoverflow.com/a/28808488/1654265)), it helps a lot – Andrea Ligios Mar 07 '16 at 09:46
  • If you have found a solution to your problem then answer yourself and accept your answer. – Aleksandr M Mar 07 '16 at 10:58
  • @AleksandrM, ma answer is inspired by the idea of Roman. And he spent many times to explain his response. So I prefer to keep his answer :) And I'll look at your proposition – Mincong Huang Mar 07 '16 at 12:37

1 Answers1

0

It cannot work because the scope of the zooController is singleton. You should make the scope prototype.

<bean id="zooController" class="com.zoo.controller.ZooController" scope="prototype" >
   <property name="zooService" ref="zooServiceBean" />
</bean>

The dependency management is defined by the container:

If your actions managed by Struts container, then Struts is creating them in the default scope. If your actions is managed by Spring container then you need to define the scope of the action beans, because Spring by default uses singleton scope and if you don't want to share your action beans between user's requests you should define the corresponding scope. You can use prototype scope, which means a new instance is returned by the Spring each time Struts is being built an action instance.

The Struts integrates to Spring via plugin. Make sure it has

<constant name="struts.objectFactory" value="spring" />

then you can delegate actions to Spring

References:

EDIT:

In your first config you declared a bean zooService that will be injected by Struts using spring object factory.

In your second config you declared two beans zooController and zooServiceBean, but you changed the name of the second bean. Then you tried to build the action bean using spring object factory like in the first case. And because there's no bean with name zooService the autowiring has been failed. Because by default Struts is configured to autowire beans from the application context by name.

Then you changed struts.xml and used a bean reference in the action class attribute. It means that Struts will use app context to get a bean from Spring. And because it has an explicit dependency on the second bean, it would be wired before the bean is returned.

Roman C
  • 49,761
  • 33
  • 66
  • 176
  • I fellow your proposition, the scoped has been changed and constant has been added. Unfortunately, it didn't work. But when I renamed the `zooServiceBean` to `zooService`, config 2 worked without additional changes. Any idea ? Is it related to the wiring mode in spring-struts plugin ? – Mincong Huang Mar 06 '16 at 17:41
  • Struts wires beans by name. – Roman C Mar 06 '16 at 18:50
  • If `zooServiceBean` is changed to `zooService`, it means the beans are still managed by struts. Actually, config 2 aims to delegate actions to spring. And the solution is: in `struts.xml`, rename the action node from `com.zoo.controller.ZooController` to `zooController` as specified in `applicationContext.xml`. So that struts can find the bean created by spring container. If you're agreed with me, could you please update your answer so that i can accept it ? – Mincong Huang Mar 07 '16 at 00:34