42

A request before you mark it as duplicate. I have gone through the forum and couldn't find the solution for the problem anywhere.

I am writing a code using Spring 3.2 and everything is purely annotation based. The code receives XML files which are derived form different XSD files.

So we can say, there are five different XSD ( A1, A2, A3, A4, A5) and my code receives XML of any type, and I have the logic to identify the type of the XML upon arrival.

Now, I am trying to un-marshal these using Spring OXM. But because there are multiple XSDs involved, we cannot actually using one Un-marshaller. So we need around five of them.

In the Configuration class, I added five beans like below:

@Bean(name="A1Unmarshaller")
public Jaxb2Marshaller A1Unmarshaller(){
    Jaxb2Marshaller unMarshaller = new Jaxb2Marshaller();
    unMarshaller.setContextPath("package name for the classes generate by XSD A1");
}

@Bean(name="A2Unmarshaller")
public Jaxb2Marshaller A2Unmarshaller(){
    Jaxb2Marshaller unMarshaller = new Jaxb2Marshaller();
    unMarshaller.setContextPath("package name for the classes generate by XSD A2");
}

@Bean(name="A3Unmarshaller")
public Jaxb2Marshaller A3Unmarshaller(){
    Jaxb2Marshaller unMarshaller = new Jaxb2Marshaller();
    unMarshaller.setContextPath("package name for the classes generate by XSD A3");
}

@Bean(name="A4Unmarshaller")
public Jaxb2Marshaller A4Unmarshaller(){
    Jaxb2Marshaller unMarshaller = new Jaxb2Marshaller();
    unMarshaller.setContextPath("package name for the classes generate by XSD A4");
}

@Bean(name="A5Unmarshaller")
public Jaxb2Marshaller A5Unmarshaller(){
    Jaxb2Marshaller unMarshaller = new Jaxb2Marshaller();
    unMarshaller.setContextPath("package name for the classes generate by XSD A5");
}

Now I have five different classes C1, C2, C3, C4 and C5 and I am trying to inject one unmarshaller bean into one class. That means A1Unmarshaller is autowired to C1 and so on.

When the Spring context is built, it throws an error saying it expected one bean of type Jaxb2Marshaller and got five.

Note It worked fine when done using XML configuration, so I am not sure if I am missing something. Please help.

EDIT The code for one of the classes C1 is below:

@Component
public class C1{

@Autowired
private Jaxb2Marshaller A1Unmarshaller;
    A1 o = null

public boolean handles(String event, int eventId) {
    if (null != event&& eventId == 5) {
                A1 =  A1Unmarshaller.unMarshal(event);
        return true;
    }
    return false;
}

}

Eric Palakovich Carr
  • 22,701
  • 8
  • 49
  • 54
dharam
  • 7,882
  • 15
  • 65
  • 93
  • Could you please post the exception and the code of the bean where you inject the `Jaxb2Marshaller`. And maybe the relevant parts of old XML configuration "that worked fine". – Ralph Sep 10 '13 at 06:43
  • 2
    Are you adding the appropriate `@Qualifier`s to `Cn`? – chrylis -cautiouslyoptimistic- Sep 10 '13 at 07:00
  • Chrylis ... I am not putting any @Qualifier. could you please elaborate? – dharam Sep 10 '13 at 07:04
  • @Ralph.. I have added the code you asked for. – dharam Sep 10 '13 at 07:51
  • "because there are multiple XSDs involved, we cannot actually using one Un-marshaller" - have you actually tried this? It should work fine if you call `setContextPath("package.a1:package.a2:...")` (unless different schemas define the same type or element in the same namespace with a different meaning). – Ian Roberts Sep 10 '13 at 08:29
  • FYI, nice background reading: https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-autowire – Jacob van Lingen May 30 '22 at 13:47

3 Answers3

92

You should qualify your autowired variable to say which one should be injected

@Autowired
@Qualifier("A1Unmarshaller")
private Jaxb2Marshaller A1Unmarshaller;

The default autowiring is by type, not by name, so when there is more than one bean of the same type, you have to use the @Qualifier annotation.

Bruce Lowe
  • 6,063
  • 1
  • 36
  • 47
  • I get `Qualifier not applicable in constructor` error. So I would have to use the field injection (which is generally not recommended)? – parsecer Jun 05 '23 at 17:20
12

The Jaxb2Marshaller is perfectly capable to work with multiple different contexts/xsd. Simply specify multiple context paths by using the setContextPaths methods.

@Bean(name="A1Unmarshaller")
public Jaxb2Marshaller A1Unmarshaller(){
    Jaxb2Marshaller unMarshaller = new Jaxb2Marshaller();
    unMarshaller.setContextPaths(
        "package name for the classes generate by XSD A1",
        "package name for the classes generate by XSD A2",
        "package name for the classes generate by XSD A3",
        "package name for the classes generate by XSD A4",
        "package name for the classes generate by XSD A5" );
    return unMarshaller;
}

That way you only need a single marshaller/unmarshaller.

Links

  1. Jaxb2Marshaller javadoc
  2. setContextPaths javadoc
M. Deinum
  • 115,695
  • 22
  • 220
  • 224
4

Injection using @Resource annotation is what you're looking for. You can use

@AutoWired
@Qualifier("A1Unmarshaller")
private Jaxb2Marshaller A1Unmarshaller;

But that is not the only way.

@Resource("A1Unmrshaller")

Does the job too. I suggest you use the later one! See why

Community
  • 1
  • 1
Amanuel Nega
  • 1,899
  • 1
  • 24
  • 42