I have a work class :
public class WorkClass implements ApplicationContextAware {
... // has access to ApplicationContext
}
Have some useful interface :
public interface UsefulInterface {
void doUseful();
}
Have some impl class that can do much more:
public class CanDoAlmostEverything implements UsefulInterface {
...
}
I want to provide UsefulInterface implementation (via CanDoAlmostEverything) to WorkClass using Spring, but NOT to access any other CanDoAlmostEverything methods exept "doUseful"
In other words I want to declare my bean[s] like :
<bean id="workerA" interface="UsefulInterface" class="CanDoAlmostEverything"/>
<bean id="workerB" interface="UsefulInterface" class="AnotherUsefulImpl"/>
WorkClass will know about interface impl only during runtime and code must look like:
String todayWorker = getWorkerNameFromDataBase();
UsefulInterface worker = appCtx.getBean(todayWorker, UsefulInterface.class);
worker.doUseful();
Is it possible? And how it must look like?
I don't recommend you to use getBean this way. In the Spring documentation, it is written that it could be bad for performance.
http://docs.spring.io/spring/docs/1.0.2/api/org/springframework/beans/factory/BeanFactory.html#getBean%28java.lang.String%29
Return an instance (possibly shared or independent) of the given bean
name. Provides a measure of type safety by throwing an exception if
the bean is not of the required type.
Note that callers should retain references to returned objects. There
is no guarantee that this method will be implemented to be efficient.
For example, it may be synchronized, or may need to run an RDBMS
query.
Will ask the parent factory if the bean cannot be found in this
factory instance.
It really depends of what do you want to do. Did you tought that you Workclass could be a bean ?
public class WorkClass implements ApplicationContextAware {
private UsefulInterface workerA;
private UsefulInterface workerB;
public void setWorkerA(UsefulInterface workerA) {
this.workerA = workerA;
}
public void setWorkerB(UsefulInterface workerB) {
this.workerB = workerB;
}
public void work() {
UsefulInterface workerToUse;
if(condition) {
workerToUse = workerA;
} else {
workerToUse = workerB;
}
// treatment
}
}
Here the configuration file :
<bean id="workerA" interface="UsefulInterface" class="CanDoAlmostEverything"/>
<bean id="workerB" interface="UsefulInterface" class="AnotherUsefulImpl"/>
<bean id="mainWorker" class="package.of.WorkClass">
<property name="workerA" ref="workerA" />
<property name="workerB" ref="workerB" />
</bean>
Your main class will have to call getBean, but only one time to get the instance of WorkClass.
Related
I am new to Spring, and I would like to write a beanGenerator for a template bean. I would like to use this generator to overcome thread-safe concerns. Can anyone help me to add / modify the code to make this work? It's kind of hard to describe my real issue, so I abstract the issue in the following code:
abstract class BeanDefinition {
abstract public void preprocess();
}
// now we have 1st user specific bean :
class UserSpecifiedBeanDefinition extends BeanDefinition{
#override
public void preprocess() {
// do something
}
}
// we could have more user-specific beans that extend BeanDefinition
....
// Following generator class is used to generate beans
public class BeanGenerator {
private BeanDefinition beanDefinition;
public BeanGenerator(BeanDefinition beanDefinition) {
this.beanDefinition = beanDefinition;
}
public generate() {
BeanDefinition newBean = // create new bean based on beanDefinition? how can I make this work??
newBean.preprocess();
return newBean;
}
}
// In spring.xml, I would like to use them like:
<bean id="generator1" class="com.xxx.xxx.BeanGenerator">
<constructor-arg name="beanDefinition" ref="userSpecifiedBeanDefinition"/>
</bean>
I suppose you have a constructor without parameters. Use reflection to instantiate class
Class c = BeanGenerator.class.getClassLoader().loadClass(beanDefinition.getBeanClassName());
Constructor con = c.getConstructor();
Object instance = con.newInstance();
If you have constructor with parameters the logic should be changed to select correct constructor and pass the parameters to the newInstance() call
I would like to pass constructor argument when binding an interface with Impl in Guice.
There are a couple of solutions, but I don't have code access to these interfaces/Impl's.
They're developed by other team's and I've included them in My project.
#Named/#Assisted - Both needs a change in the source code (Constructor) of the Impl to include these annotations. (I don't have access to this code)
Implement Provider - which returns instance of that Impl by loading the required arguments. This Worked.
But the problem is I've 200+ such existing DI's and I'll have to write 200+ Providers.
Currently we're using Spring DI and are in the process of moving to Guice.
So I need to define something like
<bean name="clientWrapper" class="com.wrapper.client.ClientWrapper">
<constructor-arg index="0" value="${server.ip}" />
<constructor-arg index="1" value="${server.port}" />
</bean>
in Guice. But Pass those Constructor Args.
bind(IClientWrapper.class).to(ClientWrapper.class);
How to achieve this without using Providers?
Your best option I think is a mix of Provides methods and toConstructor binding.
Use the #Provides method binding when you have an object who has dependencies that can not be worked out by type alone.
public class ProvidesModule extends AbstractModule {
#Provides
IClientWrapper clientWrapper(#Named("server.ip") String ip,
#Named("server.port") int port) {
return new ClientWrapper(ip, port);
}
}
In overall code size this is not significantly more than Spring and is type safe.
When the constructor only has dependencies that can be worked out by type alone then use toConstructor binds
protected void configure() {
try {
bind(TransactionLog.class).toConstructor(
DatabaseTransactionLog.class.getConstructor(DatabaseConnection.class));
} catch (NoSuchMethodException e) {
addError(e);
}
}
One last option:
Our Legacy thingie:
interface LegacyThing {
}
class LegacyThingImp implements LegacyThing {
public LegacyThingImp(String test) {
System.out.println(test);
}
}
Is my magic provider on GitHub. This takes an implementation class and the list of dependencies (as Keys) and then finds the right constructor by Magic (or reflection).
public class TestMagic {
public static void main(final String... arg) {
Guice.createInjector(
new AbstractModule() {
#Override
protected void configure() {
bind(String.class).annotatedWith(Names.named("testString")).toInstance("Guice!");
bind(LegacyThing.class).toProvider(new MagicLegacyProvider<>(LegacyThingImp.class, Key.get(String.class, Names.named("testString"))));
}
}).getInstance(LegacyThing.class);
}
}
Spring reports false positive circular dependency error when the dependency order looks like below
FactoryBean depends on List (example AnimalFeeder)
AnimalFeeder depends on List of Strings.
Interesting things are
Issue was not observed when the spring instantiation order changes Ie) FactoryBean comes before AnimalFeeder. But the issue was seen when AnimalFeeder comes before FactoryBean.
This happens only when FactoryBean is involved. issue was not observed when simple bean class is used instead of FactoryBean.
here is the source code
public interface Feeder {
void feed();
}
public class AnimalFeederImpl implements Feeder {
private List<String> feedingTypes;
public AnimalFeederImpl(List<String> feedingTypes) {
this.feedingTypes = feedingTypes;
}
#Override
public void feed() {
//feed here
}
}
public class FeederManager {
private final List<Feeder> feeders;
public FeederManager(List<Feeder> feeders) {
this.feeders = feeders;
}
//This method will trigger the feeding for every 4 hours
public void triggerFeeding() {
}
}
public class FeederFactory implements FactoryBean, InitializingBean {
private List<Feeder> feeders;
private FeederManager feederManager;
public FeederFactory(List<Feeder> feeders) {
this.feeders = feeders;
}
#Override
public void afterPropertiesSet() throws Exception {
feederManager = new FeederManager(feeders);
}
public static void main(String args[]){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
context.setAllowBeanDefinitionOverriding(false);
context.setConfigLocation("test-application-context.xml");
context.refresh();
}
}
here is the context file
<bean id="water" class="java.lang.String">
<constructor-arg value="water"/>
</bean>
<bean id="animalFeeder" class="org.test.spring.autowire.impl.AnimalFeederImpl" autowire="constructor"/>
<bean id="animalFeeder" class="org.test.spring.autowire.impl.AnimalFeederImpl" autowire="constructor"/>
<bean id="feederMgr" class="org.test.spring.autowire.impl.FeederFactory" autowire="constructor"/>
Thanks!
The issue (which also causes a compilation error in your seemingly incomplete example) is that your FeederFactory implements the type FactoryBean.
public class FeederFactory implements FactoryBean, InitializingBean {
When Spring tries to instantiate the AnimalFeederImpl bean using its constructor
public AnimalFeederImpl(List<String> feedingTypes) {
this.feedingTypes = feedingTypes;
}
it needs to first construct the List<String> argument. To do that, it needs to scan the context for beans of type String. It knows for sure that water is a bean of type String because it's declared that way in the XML configuration. It knows for sure that AnimalFeederImpl isn't a bean of type String because its class type is also declared.
For FeederFactory, however, it's a little different. By declaring the class as a subclass of FactoryBean, you're telling Spring that this bean can create bean(s) of another type. To find out what type that is, Spring needs to instantiate the FeederFactory type and use its getObjectType method.
But its instantiation requires Spring to autowire
public FeederFactory(List<Feeder> feeders) {
which requires it to instantiate AnimalFeederImpl which is already in construction and the whole thing fails with a circular dependency.
I am using Java7 and Spring3. I have below classes.
Request.java
public interface Request {
public void doProcess();
}
RequestImpl.java
#Transactional
public class RequestImpl implements Request{
private String name;
private String age;
//setters and getters
public void doProcess(){
//use name and age and call third party class which will save data into database
}
}
SpringConfig.xml
<bean id="request" class="pkg.RequestImpl.java" />
Now clients will use RequestImpl as below.
RequestImplreq = (RequestImpl)applicationContext.getBean("request");
req.setName("someName");
req.setAge("20");
req.doProcess();
Now my question is do i need to declare above RequestImpl.java scope as prototype or singleton?
Thanks!
IMHO you are not working well: processes and data to process should be separated (Can DTOs be spring managed beans?) so doProcess() should be defined as doProcess(name,age) or shaded behind a factory or something similar.
Probably the best option is to define
public interface Request {
public void doProcess(String name,String age);
}
#Transactional
public class RequestImpl implements Request{
public void doProcess(String name,String age){
// do what you want
}
}
your SpringConfig.xml stay the same and call code will change to:
Request req= applicationContext.getBean(Request.class);
req.doProcess("someName", "20");
Beside all, perform a ApplicationContext.getBean() and cast result to an implementation is (usually) bad pratice because Spring can proxy returned object and cast to implementation will fail with a ClassCastException
#user3269829 : By default the scope would be singleton now it is totally depend upon your requirement, if you want a bean object for every request then you can go for "prototype" and if you want to share single bean object among the multiple request then you can go for "singleton"
It depends on how your third party class is implemented. If you want to ensure a single instance of your class you can use factory-method of spring beans and ensure single instance.
Check "3.3.2.2 Instantiation with a static factory method" part of Spring Documentation
It should look like this in bean definition:
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
and singleton creator:
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private DefaultServiceLocator() {}
public ClientService createClientServiceInstance() {
return clientService;
}
}
I have a singleton bean definition like this:
<bean id="exampleBean" class="com.examples.ExampleBean">
<property name="exampleBean2">
<bean class="com.examples.ExampleBean2" />
</property>
</bean>
where ExampleBean could be:
public class ExampleBean {
private ExampleBean2 exampleBean2;
public ExampleBean() { }
public ExampleBean2 getExampleBean2() { return exampleBean2; }
public void setExampleBean2(ExampleBean2 exampleBean2) { this.exampleBean2 = exampleBean2; }
}
The problem is that, in certain conditions, the com.examples.ExampleBean2 class might not exist at runtime witch will cause an error when the IoC tries to instantiate exampleBean.
What I need is to ignore this error from IoC and allow the exampleBean to be created but leaving the exampleBean2 property null.
So the question is: is this possible in any way?
Thanks for all your help.
If you use autowire, what you wish to achieve is possible.
<bean class="com.examples.ExampleBean" autowire="byType" />
<bean class="com.examples.ExampleBean2" />
Or via annotations
#Autowired(required=false)
ExampleBean2 exampleBean2;
Is it an option to declare an init-method on your ExampleBean, and in this init-method check if the class ExampleBean2 exists, and if so setting it?
<bean id="exampleBean" class="com.examples.ExampleBean" init-method="init"/>
Maybe a better way of doing things here would be to use some form of the NullPattern, where you always provide an implementation of ExampleBean2, even if it only is its 'null' value.
If I got it right ExampleBean2 isn't loaded when Spring tries to instantiate the beans. Is this correct? In that case I don't thing you could do much with Spring's built in capabilities.
Perhaps you could create a container class that will always be present. This class will check if ExampleBean2 is loaded and if yes, it will instantiate an instance of it. The container class will have an Object property that could either be null or the instance of the ExampleBean2.
maybe lazy-init will do it, but i think spring will at least check if the bean implementation class is available at creation of the application context
Perhaps this will work:
public class NullFactoryBean implements FactoryBean {
#Override
public Object getObject() throws Exception {
return null;
}
#Override
public Class<?> getObjectType() {
return Object.class;
}
#Override
public boolean isSingleton() {
return false;
}
}
And then...
public class ClassNotFoundPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);
String beanClassName = beanDefinition.getBeanClassName();
try {
Class.forName(beanClassName);
} catch (ClassNotFoundException e) {
beanDefinition.setBeanClassName(NullFactoryBean.class.getName());
}
}
}
}
And then...
<beans>
<bean id="exampleBean" class="com.examples.ExampleBean">
<property name="exampleBean2">
<bean class="com.examples.ExampleBean2" />
</property>
</bean>
<bean class="ClassNotFoundPostProcessor" />
</beans>
EDIT: Sorry about that, it appears that this did not catch the inner beans. I missed this detail when I tested it. It only catches top-level beans. By the way, com.examples.ExampleBean probably won't load anyway because it itself depends directly upon the ExampleBean2 class, which the virtual machine won't find, causing an Error