Question

My class contains several methods those return some Java objects, i.e. InputStream and OutputStream:

public class Provider {
    public InputStream getInputStream() { ... }
    public OutputStream getOutputStream() { ... }
}

From the other hand I have a third-party class whose constructor can accept InputStream and OutputStream:

public class Consumer {
    public Consumer(InputStream is, OutputStream os) { ... }
}

The issue is: how to inject dependency on Provider's streams into Consumer bean?

Here is my XML file for Spring:

<?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-2.0.xsd">
     <bean id="provider" class="org.example.Provider" />
     <bean id="consumer" class="org.example.Consumer">
         <constructor-arg value="#{provider.inputStream}" />
         <constructor-arg value="#{provider.outputStream}" />
     </bean>
 </beans>

Unforunatelly, the solution above leads to the following error:

Apr 01, 2014 10:54:59 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.FileSystemXmlApplicationContext@5f18cd5: display name [org.springframework.context.support.FileSystemXmlApplicationContext@5f18cd5]; startup
date [Tue Apr 01 10:54:59 MSK 2014]; root of context hierarchy
Apr 01, 2014 10:54:59 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from file [C:\Test\context.xml]
Apr 01, 2014 10:54:59 AM org.springframework.context.support.AbstractApplicationContext obtainFreshBeanFactory
INFO: Bean factory for application context [org.springframework.context.support.FileSystemXmlApplicationContext@5f18cd5]: org.springframework.beans.factory.support.DefaultListableBeanFactory@2d30be8c
Apr 01, 2014 10:54:59 AM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2d30be8c: defining beans [provider,consumer]; root of factory hierarchy
Apr 01, 2014 10:54:59 AM org.springframework.beans.factory.support.DefaultSingletonBeanRegistry destroySingletons
INFO: Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2d30be8c: defining beans [provider,consumer]; root of factory hierarchy
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'consumer' defined in file [C:\Test\context.xml]: Unsatisfied dependency expressed through constructor argument with index 0 of type [java.io.InputStream]: Could not convert constructor argument value of type [java.lang.String] to required type [java.io.InputStream]: Failed to convert value of type [java.lang.String] to required type [java.io.InputStream]; nested exception is java.lang.IllegalArgumentException: Could not retrieve InputStream for file [C:\Test\#{provider.getInputStream()}]: #{provider.getInputStream()} (File specified cannot be found)
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:565)
        at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:193)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:925)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:835)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:440)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:429)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:728)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:380)
        at org.springframework.context.support.FileSystemXmlApplicationContext.<init>(FileSystemXmlApplicationContext.java:140)
        at org.springframework.context.support.FileSystemXmlApplicationContext.<init>(FileSystemXmlApplicationContext.java:84)
        at org.example.Launcher.main(Launcher.java:9)

This error log looks very weird and I'm really confused what's going on...

UPD: Full stacktrace have been appended to the post.

Was it helpful?

Solution

Your config will cause Spring to want to call:

 new com.example.Consumer("#{provider.inputStream}","#{provider.outputStream}");

... that is, Consumer(String,String). But that constructor doesn't exist, so it fails.

Your error message Could not convert constructor argument value of type [java.lang.String] to required type [java.io.InputStream] says that. "The constructor takes InputStream but you gave me String".

You need to ask Spring to create your Provider, then call that object's methods to create the InputStream and OutputStream has Spring beans. Then use those in your Consumer:

<bean id="provider"
     class="com.example.Provider" />

<bean id="inputStream"
   factory-bean="provider"
   factory-method="getInputStream"/>

<bean id="outputStream"
   factory-bean="provider"
   factory-method="getOutputStream"/>

<bean id="consumer" class="com.example.Consumer">
     <constructor-arg ref="inputStream" />
     <constructor-arg ref="outputStream" />
</bean>

You can also do this inline, e.g.

<bean id="consumer" class="com.example.Consumer">
     <constructor-arg>
         <bean factory-bean="provider" factory-method="getInputStream"/>
     </constructor-arg>
     <constructor-arg>
         <bean factory-bean="provider" factory-method="getOutputStream"/>
     </constructor-arg>
</bean>

Based on the Spring Expression Language docs (I haven't tested), the following may also work:

 <bean id="consumer" class="com.example.Consumer">
     <constructor-arg value="#{provider.getInputStream()}" />
     <constructor-arg value="#{provider.getOutputStream()}" />
 </bean>

... possibly helping it along with:

<constructor-arg type="java.io.InputStream" value="#{provider.getInputStream()}" />

OTHER TIPS

Instead of providing the consumer with two streams, you could instead inject the actual provider bean.

The consumer class should then look like this:

public class Consumer {
    public Consumer(Provider provider) { ... }
}

and the config xml should look like this:

<?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-2.0.xsd">
     <bean id="provider" class="org.example.Provider" />
     <bean id="consumer" class="com.example.Consumer">
         <constructor-arg ref="provider" />
     </bean>
 </beans>

Ok, thanks everyone! Summary, there were two possible ways to overcome that challenge:

  1. Use Provider as a bean factory; or
  2. Upgrade Spring version. Spring 4.0.2 manages with SPeLs pretty fine!
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top