Using BeanFactoryAware | Forgo dependency injection. |
Using Lookup Method Injection | Dynamically generate a subclass overriding the method, using bytecode generation via the CGLIB library |
Using ServiceLocatorFactoryBean | A custom locator interface. |
In Web Applications | Uses RequestContextListener in addition to ContextLoaderListener |
The Command
The following is the code fo the Command class that will be used by the different Command Managers in the example.
package beans;
public class Command {
private String someProperty;
public String getSomeProperty() {
return someProperty;
}
public void setSomeProperty(String someProperty) {
this.someProperty = someProperty;
}
public String execute() {
return "Command property : " + someProperty;
}
}
Using BeanFactory Aware
In this approach, we delegate the creation of the prototype bean to the Singleton itself. This can be achieved by implementing the BeanFactoryAware interface as shown below.
package beans;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
public class CommandManager implements BeanFactoryAware {
private BeanFactory beanFactory;
public Object process(String prop) {
Command command = createCommand();
command.setSomeProperty(prop);
return command.execute();
}
protected Command createCommand() {
return (Command) this.beanFactory.getBean("command");
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
You can see here that the createCommand method uses the beanFactory.getBean() method to get the Command bean. This dependency on the Spring API is not that desirable.
Lookup Method Injection
Lookup method injection refers to the ability of the container to override methods on container managed beans, to return the result of looking up another named bean in the container.The Spring Framework implements this method injection by dynamically generating a subclass overriding the method, using bytecode generation via the CGLIB library. The command manager of the above implementation is changed in the following to allow lookup method injection.
package beans;
public abstract class CmdMgrNoBFA {
public Object process(String prop) {
Command command = createCommand();
command.setSomeProperty(prop);
return command.execute();
}
protected abstract Command createCommand();
}
You can see here that the Command Manager does not implement the BeanFactoryAware interface. The createCommand was declared as abstract method, but it may also be defined here (the container will override it any way). The signature of the lookup method must have the following form
<public|protected> [abstract] <return-type> theMethodName(no-arguments);Note that beans that have been the target of method injection cannot be serialized.
Using ServiceLocatorFactoryBean
Service locators help avoid the usage of the BeanFactory API using a custom locator interface. Service locators are typically used for prototype beans. Setter or constructor injection of the target bean is preferable, where the target bean is a Singleton. The custom locator interface shown below does not depend on the bean factory API.
package beans;
public interface ServiceFactory {
public Command getCommand();
public Command getCommand(String beanName);
}
The corresponding Command Manager must have a ServiceFactory property that will be injected by the container. This is the code for the CommandManager used here.
package beans;
public class CmdMgrServiceFactory {
public ServiceFactory serviceFactory;
public Object process(String prop) {
Command command = serviceFactory.getCommand("command");
command.setSomeProperty(prop);
return command.execute();
}
public ServiceFactory getServiceFactory() {
return serviceFactory;
}
public void setServiceFactory(ServiceFactory serviceFactory) {
this.serviceFactory = serviceFactory;
}
}
Invoking the no-arg getCommand() method or the single-arg method with null, will return an instance of the Command object by matching it's type if there is only one bean of the type beans.Command. If there are more then beans.Command beans, then a NoSuchBeanDefinitionException is thrown. By default, the single string argument is matched to the bean names defined in the applicationContext.xml file. Alternatively, you can set a mapping of the string argument to bean names using a Properties object for mapping. This Properties object can be injected in using the serviceMappings property. Using with Web applications and rest of the definitions are in the next post.
No comments:
Post a Comment