3

I am a Spring newbie. I found that sometime we need to construct an object using runtime data, but fixed value is used in constructor inject of Spring. I know I could create a setter method and change the value with it, but I don't think this an elegant solution. Could any one tell me how to do it?

eric2323223
  • 3,518
  • 8
  • 40
  • 55

2 Answers2

3

Using SpEL => via Another Bean

<bean id="bank" class="UsBank">
   <property name="moneyLeft" value="100"/> <!-- initial value -->
</bean>

<bean id="bet" class="UsDollars" scope="prototype">
   <constructor-arg value="#{ bank.moneyLeft }"/>
</bean>

Let's say the bank bean is injected somewhere, so you have an access to it:

bank.setMoneyLeft( 100 )
Bet currentBet = appContext.getBean( "bet" )

Using SpEL => via Expression

If this argument can to be computed with an arbitrary expression:

<bean id="bet" class="UsDollars" scope="prototype">
   <constructor-arg value="#{ T(java.lang.Math).random() * 100.0 }"/>
</bean>

Using SpEL => via System Properties

If your use case allows to use system properties:

<bean id="bet" class="UsDollars" scope="prototype">
   <constructor-arg value="#{ systemProperties['moneyLeft'] }"/>
</bean>

to get the bean:

System.setProperty( "moneyLeft", "5000" )
Bet currentBet = appContext.getBean( "bet" )

You can read read more about SpEL.

One thing to note about making a bet bean prototype => if it is injected into another bean, it (another bean) also needs to be a prototype, or there is some AOP magic that can be used, but it may not be all that important in your case.

If the above bean does not have to be a prototype (which would mean you would only need a single instance of this bean, but not at the application context creation time), you can remove scope="prototype" and add a lazy="true". That would tell Spring to only attempt to create this bean when it is first referenced.

tolitius
  • 22,149
  • 6
  • 70
  • 81
1

Generally for this my preference is to make a factory like so:

@Service
public class RuntimeObjectFactoryImpl implements RuntimeObjectFactory {
    private final CustomObjectTypeA oA;
    private final CustomObjectTypeB oB;

    @Autowired
    public RuntimeObjectFactoryImpl(final CustomObjectTypeA oA, final CustomObjectTypeB oB) {
        this.oA = oA;
        this.oB = oB;
    }

    @Override
    public RuntimeObject get(CustomObjectTypeC oC) {
        return new RuntimeObject(oA, oB, oC);
    }
}

This can be configured in XML aswell (which I would generally recommend over annotations for wiring business services, but it's a more presentable answer here to use annotations). So then at runtime:

RuntimeObject o = runtimeObjectFactory.get(new CustomObjectTypeC(...));
mogronalol
  • 2,946
  • 8
  • 38
  • 56