4

I found similar question here Spring - how to inject a bean into class which is created many times at runtime? and Why is Spring's ApplicationContext.getBean considered bad? but neither really answers my case.

Example code:

public interface AppNameProvider
{
    String getAppName();
}

public class DefaultAppNameProvider implements AppNameProvider
{
    private String appName;

    public String getAppName()
    {
        return appName;
    }

    public setAppName(String appName)
    {
        this.appName = appName;
    }
}

<bean id="appNameProvider" class="some.package.DefaultAppNameProvider">
    <property name="appName" value="MyApplication"/>
</bean> 

public class MyException extends RuntimeException
{
    // Imagine obligatory constructors here...

    public String getAppName()
    {
        // Inject appNameProvider somehow here
        return appNameProvider.getAppName();
    }
}

I have a provider bean declared in xml. In the example the value is simply declared in xml for simplicity. I have a custom exception which needs to receive something from a bean. How to inject such bean into the exception class. I obviously can't declare exception as a Spring bean. The appName is just a simple example, it can be anything else. You might wonder why a hypothetic caller of myException.getAppName() wouldn't just simply call appNameProvider.getAppName()? Because it's not intended so, e.g. there might be different provider in each exception etc.

I would like to know how to inject the bean into such exception. I can add setter and set the provider at the exception throw time. But I have to know which provider to use from outside (in my app code) and I would have to do it redundantly everywhere where I want to throw this exception. Ideally I would like to declare which provider to use for the exception in the xml.

Ultimately the question can be widened so that instead of exception we think of any runtime object which is not a bean itself.

PS I'm not scared of having hardcoded dependencies to Spring in the code. I use Spring and I want to embrace it - not avoid it.

Community
  • 1
  • 1
user1063845
  • 41
  • 1
  • 3
  • Could you post the code which throws a new instance of `MyException`? I wonder if you can't pass a reference to the AppNameProvider through a constructor... – JBert Nov 24 '11 at 13:40

4 Answers4

1
  1. Inject the provider in the class which throws the exception
  2. Provide a constructor/setter by which to set the provider to the exception
  3. throw new MyException(provider)
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • Answer for Bozho and JBert: I can add setter and set the provider at the exception throw time. But I have to know which provider to use from outside (in my app code) and I would have to do it redundantly everywhere where I want to throw this exception. – user1063845 Nov 25 '11 at 14:51
  • @user1063845 The thing is, I want to know in what context you'd throw the exception and why you would actually need the AppnameProvider at the place where you catch it. I'd expect that when you throw an exception, someone did a wrong operation on a class implementing AppnameProvider, meaning you __have__ an instance of the provider. I'm sure that Spring can do some magic tricks, but to met it sounds like you are trying to fix a flaw in your design. The Spring magic you'd need to make this work could make the code incomprehensible for anyone who later works on the code. – JBert Nov 25 '11 at 22:12
0

It's a while since I've done it, but if you are using / can use annotation based config and aspectJ you can annotate a class as @Configurable which will allow you get Spring to inject dependencies each time an instance of the class is created.

http://static.springsource.org/spring/docs/current/spring-framework-reference/html/aop.html#aop-atconfigurable

Russell
  • 12,261
  • 4
  • 52
  • 75
0

You can create a component and inject the property in it. For example you can define DefaultAppNameProvider as a component and thus you can autowire other components in it. You can then provide using a singleton design pattern with a private constructor a static method called getInstance. In the class MyException you can access the DefaultAppNameProvider property using DefaultAppNameProvider.getInstance().getAppName().

Example code for the singleton component.

@Component
public class DefaultAppNameProvider {
    private static DefaultAppNameProvider instance;

    private DefaultAppNameProvider() {
        instance = this;
    }

    public static DefaultAppNameProvider getInstance() {
       return instance;
    }

    private String appName;

    public String getAppName()
    {
        return appName;
    }

    public setAppName(String appName)
    {
        this.appName = appName;
    }
}
aseychell
  • 1,794
  • 17
  • 35
0

I have been looking around in the mean time. I found this. In the end I used the following solution.

According to 1 created an ApplicationContextProvider:

public class ApplicationContextProvider implements ApplicationContextAware
{
    private static ApplicationContext applicationContext;

    public static ApplicationContext getApplicationContext()
    {
        return applicationContext;
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
    {
        ApplicationContextProvider.applicationContext = applicationContext;
    }
}

Then AppNameProviderFactory which maps providers to the keys. Key can be exception name:

public class AppNameProviderFactory
{
    private Map<String,AppNameProvider> map;

    public void setMap(Map<String, AppNameProvider> map)
    {
        this.map = map;
    }

    public AppNameProvider getAppNameProvider(String key)
    {
        return map.get(key);
    }
}

And in xml I define the mappings:

<bean id="appNameProviderFactory" class="some.domain.AppNameProviderFactory">
    <property name="map">
        <map>
            <entry key="MyException" value-ref="appNameProvider"/>
        </map>
    </property>
</bean>

And finally in the exception class:

public class MyException extends RuntimeException
{
    // Imagine obligatory constructors here...

    public String getAppName()
    {
        final ApplicationContext applicationContext = ApplicationContextProvider.getApplicationContext();
        AppNameProviderFactory factory = (AppNameProviderFactory) applicationContext.getBean("appNameProviderFactory");
        return factory.getAppNameProvider("MyException").getAppName();
    }
}

This way I have configuration in xml, decoupled from business code. I can have as many exceptions with different providers as needed.

Thanks all for suggestions. PS Error handling and NPE handling ommited for simplicity.

user1063845
  • 41
  • 1
  • 3