New Spring ‘alias’ tag helps achieve lightweight component oriented assembly of applications
The new alias tag in Spring’s XML bean definition format makes using Spring in a (lightweight) component oriented fashion significantly easier.
Spring has always had the ability to assign multiple IDs to components (via combined usage of the id and name attributes) which made it possible to link up components somewhat. Consider a library called ComponentA. It defines within it an XML application context fragment called componentA-context.xml. This is the bean definitions for the component. Now some of the component’s beans in there need to work with a DataSource, so they use a reference to a datasource bean:
However, the component’s XML file fragment doesn’t actually define this datasource bean. It’s assumed that some application is going to be assembling an entire context by using multiple XML file fragments like this together, and the datasource will be defined elsewhere. So we might have another component, ComponentB, which in its definition file called componentB-context.xml, refers to the datasource under another name.
Then finally we have the application’s main context definition file, called myApp-context.xml. Inside this are a bunch of bean definitions, including the actual definition for the datasource.
Any bean definitions in componentB’s definition XML fragment can refer to componentB-dataSource, and any definitions in the main application XML fragment can refer to myApp-dataSource, and this will be resolved to the componentA-dataSource bean definition coming from ComponentA’s XML fragment.
Of course, you still have to worry yourself about name clashes. This is why I call this a lightweight component oriented approach. It’s quite possible for different XML file fragments which are assembled together for the final context, to accidentally use the same name for something. This is why it makes sense when using this approach to use some sort of prefix on bean names as a sort of namespacing scheme.


Modified

Rick Cui says:
Added on April 9th, 2005 at 3:26 pmOh, my! I’ve been waiting for this for a long long time. Thanks a zillion! When are you going to put in? I just went to the gmane site and followed your link to here. I am having 3 sets of what you mentioned now and I need this desperately.
.
I have another question regarding the configuration in the component oriented context. Suppose I have a component A, with a spring xml file inside, A.xml. Now here is the issue, when I use it somewhere else, say component B with B.xml, and I want to overwrite a bean or two inside A.xml. Let’s say I want to overwrite a bean with id=”a” in A.xml.
Right now I am trying to use parent-child contexts.
I can have a new “a” bean in B.xml and set A as B’s parent when I load the application contexts. The real problem is that the bean references are not correct. For example, if in A.xml, a reference aa, when I get a from the AC, the reference of a is correct; but when I get something from B and that something references a, then it still references the old a, not the new a I set in B.xml.
Another approach would be the order of loading A and B, but I haven’t tried it.
The third way I could think of is the PropertyOverrideConfigurer, but since it’s for properties files, it’s a little bit off.
If you could throw in some thoughts, I really appreciate it.
Thanks,
Rick
Colin Sampaleanu (blog author) says:
Added on April 9th, 2005 at 10:25 pmThe alias tag support code is alrady in CVS. The first release to contain that code will be Spring 1.2RC2, probably in about a week or so.
W/regards to overriding beans, using parent and child contexts is not too workable in anything but fairly simple cases, since of course a bean in a parent will never know about newer beans in the child, as you discovered…
Order of loading _is_ a workable solution for some cases, since everything is in the same context. The last definition wins.
The new alias tag can also be used to load one bean definiton vs. another, from one component vs. another. Say component A defines componentA-dataSource, and marks it as lazy-init. Then component B defines componentB-dataSource, and marks it as lazy. As is, neither one will actually ever be created, unless asked for explicitly. So in the main application definition XML file (which will be combined with the two fragments from the two components), you just define an alias which the app will use, and point it at the dataSource from one component vs. the other, then all the beans in that definition file fragment use the alias. Of course, this only works if no bean in each component needs that component’s dataSource, i.e. it’s only going to be used from beans in the main app context xml fragment. Really a datasource is a bad example here, but it shows how two components can both export the same logical bean, and as long as it’s marked as lazy-init=true (or it’s not a singleton), it will never be created until a third component refers to it, and the use of an alias makes switching the actual source very easy.
Rick Cui says:
Added on April 10th, 2005 at 1:56 pmSomehow the xml files are gone. Let me try it one more time with code tag.
Great, we’ll wait for release 1.2.
Thanks for the comment on the overriding, I’ll try the ordering. Basically, I am having an existing component for entitlements(authorization), which is used by a lot of applications. When applications use it, they could call different DataSource or utilize other different resources. So I want to have default settings and allow applications to override them. I have several other shared components too, I just started with this one, try to refactor them into
Spring context.
In you last comment, I had a similar case about lazy-init=true in a different scenario. I found a workaround to make it working. If you have time, maybe you could share some thought(It’s OK if you don’t have time, since I know you folks are quite busy with the next release). We have a HttpMessage class to do the GET/POST(this is pre Apache’s HttpClient, so we don’t use that), and we use it with proxy settings and client/server side digital certificates(so we have HttpProxy and HttpCertificate classes). I want to use Spring to config HttpMessage with HttpProxy and HttpCertificate, namely HttpProxy and HttpCertificate get the UrlConnection from HttpMessage and set the proper settings. Ideally, HttpProxy and HttpCertificate should be singleton since they just have a bunch of unchanged settings. If HttpMessage is singleton too, then I can make HttpProxy and HttpCertificate lazy-init=true, then when I refer HttpMessage, I could use a BeanPostProcessor to trigger HttpProxy and HttpCertificate. Here are my code, I just replace them with simpler class.
HttpMessage is replaced by this simple class:
[code]
public class MyTargetExecutor
{
private String field1 = null;
private String field2 = “”;
// These two are really setters, but they could be something else.
public void tryme1(String f1)
{
field1 = f1;
}
public void tryme2(String f2)
{
field2 = f2;
}
// we want to verify whether these two fields are modified.
public String getme()
{
return field1 + field2;
}
}
[/code]
The HttpProxy and HttpCertificate are replaced by two simple classes:
[code]
public class MySetter1
{
// we may get target fields through getters and set something to them,
// e.g., get UrlConnection, find whether http or https is used, so we
// could set proper proxy.
public void setField1(MyTargetExecutor target)
{
target.tryme1(”Hello”);
}
}
[/code]
and
[code]
public class MySetter2
{
public void setField2(MyTargetExecutor target)
{
target.tryme2(”, World”);
}
}
[/code]
And the xml file is:
[code]
setField1
setField2
[/code]
Here, I use MethodInvokingFactoryBean to invoke the MySetter*’s methods. I figured this kind of injection is not method injection, nor AOP, rather a kind of “behavior” injection. So I named the fields behaviorTarget and behaviorTargets. The post processor is like this:
[code]
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactory;
public class BehaviorInjectionPostProcessor implements BeanPostProcessor, BeanFactoryAware
{
private Object behaviorTarget;
private Object[] behaviorTargets;
private BeanFactory factory;
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
{
System.out.println(”getCalled++++++++++++++++++++++++++++++++++++++++++++++++”);
if (behaviorTarget == null)
{
if (behaviorTargets != null)
{
for (int i = 0, j = behaviorTargets.length; i “> in the singletons and have to
have some temp holder for the bean without toughing the actual bean until HttpMessage bean is refered. So I create a new class:
[code]
/**
* This class serves as a temporary holder for the behaviors. The reason is when
* we want to inject singleton behaviors to non-singleton beans, we can not reference
* non-singleton beans in the singleton behavior settings(otherwise, a new bean
* is created, instead of the one that we want to inject into). So we need a temp
* holder to hold on, then when we use the FactoryBean to create non-singleton target
* beans, we resolve the reference back to the singleton behaviors.
*
* Of course, this class has no behavior, merely a holder. So it’s not a first-class
* object, can we avoid this class?
*/
public class BehaviorHolder
{
// Behavior object, method, and arguments
private Object behaviorObject;
private String behavior;
private String[] behaviorArgs;
// specifying this field will trigger static method(behavior field) call on the behaviorObject.
private Class behaviorClass;
// or you may specify both class and method in one shot.
private String staticBehavior;
public Object getBehaviorObject() { return behaviorObject; }
public void setBehaviorObject(Object behaviorObject) { this.behaviorObject = behaviorObject; }
public Class getBehaviorClass() { return behaviorClass; }
public void setBehaviorClass(Class behaviorClass) { this.behaviorClass = behaviorClass; }
public String getBehavior() { return behavior; }
public void setBehavior(String behavior) { this.behavior = behavior; }
public String getStaticBehavior() { return staticBehavior; }
public void setStaticBehavior(String staticBehavior) { this.staticBehavior = staticBehavior; }
public String[] getBehaviorArgs() { return behaviorArgs; }
public void setBehaviorArgs(String[] behaviorArgs) { this.behaviorArgs = behaviorArgs; }
}
[/code]
I use this class to hold the entire behaviors, including the “String” reference to the target, until the injected bean is refered. Then I use a factory bean to hook everything together, the new Factory bean is:
[code]
import org.springframework.beans.*;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.config.*;
public class BehaviorInjectionFactoryBean implements FactoryBean, InitializingBean
{
private Object targetObject;
private BehaviorHolder behavior;
private BehaviorHolder[] behaviors;
private boolean singleton = false;
private Object singletonObject;
private final BeanWrapper bw = new BeanWrapperImpl();
public Object getObject() throws Exception
{
if (this.singleton)
{
return this.singletonObject;
}
else
{
return targetObject;
}
}
public Class getObjectType()
{
return targetObject.getClass();
}
public void afterPropertiesSet() throws Exception
{
// initialization
bw.setWrappedInstance(targetObject);
if (behavior != null)
{
singleBehave(behavior);
}
else if (behaviors != null)
{
for (int i=0, j=behaviors.length; i 0)
{
mifb.setArguments(convertArguments(behavior.getBehaviorArgs()));
mifb.afterPropertiesSet();
mifb.invoke();
}
}
}
/**
* convert the arguments of String array to Object array, the base object is
* the targetObject, which is specified by “this”. “null” is null. For anything
* else, it will treat as targetObject’s property. Nested property accepted.
* @param stringArgs String[]
* @return Object[]
*/
private Object[] convertArguments(String[] stringArgs)
{
if (stringArgs == null) return null;
if (stringArgs.length
setField1
this
setField2
this
[/code]
This is just a simple test. If all works well, I am going to refactor our components. If you Spring pros could share some thoughts on these, it would be really great.
Thanks a lot.
Rick
Rick Cui says:
Added on April 10th, 2005 at 1:57 pmNo, I can’t get the code tag right. Please let me know how to wrap them.
Sorry for the mess.
Rick
Colin Sampaleanu (blog author) says:
Added on April 10th, 2005 at 2:18 pmBest thing to do is pose the question on forum.springframework.org. That’s the best place to pose support questions in any case, and everybody else can see any answer there…