1

My application can run on different environments. I need configure MY data model per environment. The data model is build using Spring beans.

I use Spring 3.0.5, so I cannot conditionally load resources. I have this:

<bean id="Template1" class="...
..............
</bean>

<bean id="Template2" class="...
..............
</bean>

<bean id="Template3" class="...
..............
</bean>
................

<bean id="Factory" ...>
<propety name="type"><value>${app.type}</value></property>
<property>
 <map>
   <entry key="Temlate1" value-ref="Template1">
   <entry key="Temlate2" value-ref="Template1">
   <entry key="Temlate3" value-ref="Template1">

..................

Real bean I create by factory:

<bean id="real" factory="Factory" factory-method="getInstance"
 <constructor-arg>Factory</.....
 .............
 </bean>

Java code:

class Factory {
private Map<String, Object> templateBeans;

 Object getInstance(String name) {
  return templateBeans.get(name);
 ...........

Is it possible in some way to declare abstract template beans? Because I have very big problem with memory. Does another way exist to instantiate different beans conditionally in Spring before version 3.1? It would be good to use only EL because I don't have access to the Java code of the beans as they are from a third-party library.

Michael Petrotta
  • 59,888
  • 27
  • 145
  • 179
user710818
  • 23,228
  • 58
  • 149
  • 207
  • If I understand correctly, you have Spring beans that are instantiated but aren't used, and you think that this causes a memory problem. Is that right? Have you clearly diagnosed that those unused singleton template beans are the ones which cause your memory problems? Or is it a shot in the dark? – JB Nizet Oct 28 '11 at 22:15
  • The problem that these template beans contain references to another they to another, in sum near 1000 additional objects. – user710818 Oct 28 '11 at 22:18
  • 1
    If each of those objects consume 10 KB of memory, that still makes only 10 MB. Make sure that they do cause a real problem before trying to fix it. – JB Nizet Oct 28 '11 at 22:28
  • But on shared host near 50 applications(now). And also it slows application – user710818 Oct 29 '11 at 08:20
  • I would like to see the code which uses your Factory instance. I would say you shouldn't be needing such an object on the first place. – Daniel Dinnyes Nov 07 '11 at 02:07

6 Answers6

5

I haven't tried this but I fairly sure you could use bean aliases for this.

Firstly declare all your template beans to lazy so they aren't instantiated on startup.

<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>

Then use a bean alias with a variable to point to the real one:

<alias name="real" alias="${beanForEnvironment}"/>

See my answer here for how to nicely load properties per environment:

property-placeholder location from another property

Community
  • 1
  • 1
Pablojim
  • 8,542
  • 8
  • 45
  • 69
1

I've encountered this problem before and to get around it, I use the Spring import tag. For example:

<import resource="file:/location/to/your/config/my_beans.xml"/>

That allows you to externalize a Spring XML config from your application war/jar. So in your situation, you will have to deploy a different external Spring XML config to each of your environments but that also allows you to instantiate the exact beans you want.

Chris J
  • 9,164
  • 7
  • 40
  • 39
0

A clean solution, if possible, is to not use the Factory, but use different config files for different environments, all defining the same 'real' bean, but with different implementations. At runtime, you then just import the correct version, e.g. template.${app.type}.xml.

Another, uglier solution for this would be to make your template beans lazy-init beans, and make sure they get created dynamically. You can't inject them in the map in the factory, because that would instantiate them anyway. You could store a map of beanNames instead, and make your Factory ApplicationContextAware. Then in the getInstance() method return applicationContext.getBean(beanNames.get(...));

A more elaborate solution would be to mix XML config with Spring JavaConfig, which allows you all sorts of logic in your bean definitions.

GeertPt
  • 16,398
  • 2
  • 37
  • 61
  • In version 3.0.5 this is not possible. Possible from 3.1 – user710818 Nov 07 '11 at 05:04
  • You are right, for the first solution, you'd need to have the variable ${app.type} replaced at build type, e.g. by a maven profile, but that's not a solution for you, I guess. My second solution will work, but @pablojim's answer is even more elegant. – GeertPt Nov 08 '11 at 12:24
0

If you are able to upgrade to Spring 3.1, it gives you profies

In case upgrading is not an option, take a look at using system variables ( e.g. ${ENV_SYSTEM:dev} ) to switch between configurations ( properties ).

Another good SO's one is Common strategies when defining Spring beans for different environments

e.g. have you development beans in a service-development.xml, import it as:

<import resource="service-${profile}.xml"/>

and start it with -Dprofile=development. Or when you roll into QA: -Dprofile=qa

Community
  • 1
  • 1
tolitius
  • 22,149
  • 6
  • 70
  • 81
  • I cannot upgrade to 3.1. Obviously in 3.1 there is no problem about. – user710818 Nov 08 '11 at 04:21
  • so why can't you use system variables: , and start it with "-Dprofile=production", which will only bring the beans from "that" environment? [it works since 2.5 if not earlier] – tolitius Nov 08 '11 at 04:26
0

Use lazy init lazy-init="true" But maybe the beans get instanced once referenced in the Map, so use a Map of String with the name of the instances so the factory ask for them to the app context by code. context.getBean(MyInterface.class, templates.get(name));

aalku
  • 2,860
  • 2
  • 23
  • 44
0

As tolitius suggest runtime arguments work just fine. You can pass a -Dyourvar=yourvalue to your java runtime and you can use ${yourvar} in your spring imports. If it is not set then you will receive a resource not found error from spring.

If its a stand alone program: java YourClass -Dyourvar=yourvalue When you are using an application server then you can set the runtime arguments as well. Search your servers documentation on how to increase the servers memory. The place were you can set the -Xmx setting will usually the place where you can set your -Dyourvar constant as well.

Udo Held
  • 12,314
  • 11
  • 67
  • 93