Is it possible to chain methods in factory-method in spring to create beans. For example, I have the following API:
SomeObject.builder().build();
Is there some way I can create this bean in spring XML config directly without creating 2 beans? For example,
<bean id="fooBar" class="com.foo.bar.SomeObject" factory-method="builder().build"/>
Note: The SomeObject.builder() call returns a SomeObjectBuilder object(private static class within SomeObject).
You can't do that. You just specify a single method (even without the brackets). But in SomeObject class you can create a static method that does that for you. For example:
static SomeObject newFactoryMethod(){
return builder().build();
}
And add it to the XML:
<bean id="fooBar" class="com.foo.bar.SomeObject" factory-method="newFactoryMethod"/>
Related
I followed the following example of dependency injection: http://www.tutorialspoint.com/spring/spring_autowired_annotation.htm
For example the TextEditor class (from the above link):
public class TextEditor {
private SpellChecker spellChecker;
#Autowired
public void setSpellChecker( SpellChecker spellChecker ){
this.spellChecker = spellChecker;
}
public SpellChecker getSpellChecker( ) {
return spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
How can these dependencies/classes be instantiated, while they don't have any constructor?
Is Java simply making an object of that type, that is empty? Like an empty parameter constructor without any code?
Thanks for making this more clear!
Unless specified otherwise, every Java class has the default constructor. So here, you have a default public TextEditor() constructor, even though you haven't coded for it. (You could code it if you needed to change its visibility from public, declare a thrown exception, etc.)
So yes, Spring calls this default constructor - then calls the setSpellChecker method (as annotated, and through reflection) to populate it.
If no constructor is defined, a class can be instantiated via the no-argument default constructor.
So, the framework calls that constructor (supposedly using reflection) and then uses the set method to set the one field of the freshly created class.
The example above is using Spring annotations and Spring context file and those are the main and most important parts of the project, considering the DI.
So in the context file you have following line:
<!-- Definition for spellChecker bean -->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
this defines a class with reference spellChecker that mapps to a class com.tutorialspoint.SpellChecker and once the compiler find such property in a method that is marked as #Autowired on instantiation of the object it injects/sets the relevant version of the required dependency.
In cases where a property doesn't match a reference tag in the applicationContext.xml file Spring is trying to map the type e.g. property with name mySpecialSpellChecker which has type of com.tutorialspoint.SpellChecker still will be mapped to bean with id="spellChecker" if there are more than one of same type Spring won't instantiate your object and you might get compile time error as Spring can't know which version of the two (or more) is the correct one so this requires developer input.
This is the order of execution:
instantiate textEditor, this has default constructor that is not visible in the code public TextEditor ()
the new instance is set in a pool of available objects with reference textEditor
instantiate spellChecker and add to the pool of available object with relevant reference/label
all #Autowired properties/methods are set/called with relevant objects in this case Spring calls: setSpellChecker(spellChecker)
Is there anyway so that I can provide parameter to API( not to the member of class) using Spring?
I know I can pass result of one API call to member of class
<bean id="registryService" class="foo.MyRegistry">
...properties set etc...
</bean>
<bean id="MyClient" class="foo.MyClient">
<property name="endPoint" value="#{registryService.getEndPoint('bar')}"/>
</bean>
But, I want to pass the value to API( Basically I am trying to add ActionListener on JButton from spring)
Not really a spring expert but...
In Spring 3.
#Value("#{properties.getAppropriateActionListener()}")
public void setActionListener(ActionListener listener) {
myJButton.setActionListener(listener);
}
Also, I think Spring expects a setEndpoint() and getEndPoint() methods to be able to resolve the property which is named "endPoint". Declaring a property like that effectively passes the value to the setEndPoint() method. So passing a value to API (which I assume is invoking a method call) is actually pretty straightforward.
I have a web application that executes on tomcat 6.
I have a MysqlDb class that uses a BasicDataSource from a spring JDBC.
so far I've used the following bean configuration in web.xml:
<bean id="MysqlDb" class="com.xpogames.gamesisland.mysql.MysqlDb">
<property name="idDataSource" ref="idDataSource"/>
</bean>
and I had the following setter function:
public void setidDataSource(BasicDataSource ds) {
this._dataSource=(DataSource)ds;
this._simpleJdbcTemplate = new SimpleJdbcTemplate(_dataSource);
this._jdbcTemplate = new JdbcTemplate(_dataSource);
}
I want to convert my class to use static functions, so I created an empty private constructor so the class won't explicitly instantiated by callers.
besides that I changed the setidDataSource function to a static function, but when I try to do that I get the following error:
Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'idDataSource' of bean class [com.xpogames.gamesisland.mysql.MysqlDb]: Bean property 'idDataSource' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
is there a way to resolve this issue in web.xml or do I need to manually
fetch the ServletContext
ServletContext servletContext = this.getServletContext();
this._context = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
and fetch the bean from there and just remove the lines i printed here from web.xml ?
For one, you've declared the setter setidDataSource. It should be setIdDataSource. The first letter of a property must be a capital letter after the word set.
Also, a setter method must not be static but an instance method.
Spring-beans are by default singletons, you do not need to implement your class as a singleton as long as you use the bean from the context.
The simplest answer to your question is getting the bean from the context after setting the datasource in the context, but I thought you want to stay away from the context.
Static setters for class are of course possible (they would just set static property for all instances), but in IoC pattern (which is used in spring), bean is instance, and term "property of bean" always means "property of instance of class" - consider it as limitation of given IoC implementation.
I understand know that I had a bad implementation idea. I still need a constructor, so building a class with static functions and static init is a bad idea, and trying to execute a static setter from a bean is not logical and impossible.
Instead I changed the class to be a singleton class, so I will be able to use it anywhere in my application and it will be constructed only once.
thanks for all the information.
update
I still don't know if that's a good method, but at least it works.
in my red-web.xml (consider it as spring applicationContext.xml), I have the following:
<bean id="MysqlDb" class="com.xpogames.gamesisland.mysql.MysqlDb" init-method="getInstance">
<property name="idDataSource" ref="idDataSource"/>
</bean>
Here it creates a MysqlDb bean and configure it to use the getInstance() init method if MysqlDb Class. i made sure to have a setidDataSource() function in mysqlDb class for the datasource to be properly set.
<bean id="web.handler" class="com.xpogames.gamesisland.Application">
<property name="MysqlDb" ref="MysqlDb"/>
</bean>
Here, I create the main bean of my application and I made sure to have the function setMysqlDb for the MysqlDb class to be set from the bean configuration.
So far mysqlDb acts as a singelton class because it's constructor is protected and it creates the instance only once:
public static MysqlDb getInstance() {
if (instance == null) {
instance = new MysqlDb();
}
return instance;
}
The problem that I encountered was that in other parts of my application whenever I used getInstance(), the MysqlDb class would come up and all the variables that where set with setidDataSource where null.
so resolve that issue I created another function called setInstance in mysqlDb:
public static void setInstance(MysqlDb db) {
instance=db;
}
this is my main setMysqlDb function in my main application:
public void setMysqlDb(MysqlDb db) {
this._mysqlDb=db;
/* it seems that without forcing a setInstance on the class, whenever other classes
* would try to getInstance(), variables that are supposed to be configured by the bean
* would be empty. this resolves that issue
*/
MysqlDb.setInstance(db);
}
so this configuration works. it's obviously not the recommended or best solution! but it seems that I need to read and learn further before I come up with a better solution.
I have a static class ResourceFetcher with a static method fetchResource(String reference). I want to inject the resource returned by it into another class JobRunner. Can anyone specify the cleanest way of doing this?
I do not want to pass ResourceFetcher into JobRunner. In fact, I have an enum with set of keys, and I need to pass a map of key-value pairs into JobRunner with values obtained by invoking fetchResource.
Onething I want to clarify is that ResourceFetcher class' fetchResource returns an object of type String
Thanks in advance.
<bean id="resource" class="com.x.y.ResourceFetcher" factory-method="fetchResource">
<constructor-arg value="someReference"/>
</bean>
You can then inject resource into your JobRunner bean.
If the fetchResource method is static on ResourceFetcher, why can't JobRunner simply refer to it? I don't see the need to inject ResourceFetcher.
I have the following class structure
public class Outer{
private Mapper a;
....
private class MapperA implements Mapper {
}
private class MapperB implements Mapper {
}
}
In my Spring config file I would like to create a an Outer bean, and assign one of MapperA or MapperB as a property. Is this possible?
<bean id="outer" class="mypackage.Outer">
<property name="a" ?????='????' />
</bean>
Edit: Some more info, based on the feedback from answers:
I got lazy with my above example. I do have a public setter/getter for the Mapper instance variable.
The reason all of the Mapper classes are inner classes is because there could potentially be many of them, and they will only ever be used in this class. I just don't want a ton of cruft classes in my project. Maybe a factory method is a better idea.
Spring can instantiate private inner classes. The actual problem with your config is that they are also non-static, so you need a <constructor-arg .../>:
<bean id="outer" class="mypackage.Outer">
<property name = "a">
<bean class = "mypackage.Outer.MapperA">
<constructor-arg ref = "outer" />
</bean>
</property>
</bean>
Normally you'd need a setter for the Mapper within Outer, and an instance of the required Mapper. But as these are:
private
inner
classes, that becomes a bit tricky (as you've identified). If you make them public, I'm sure you could creae an instance using Outer$MapperA etc. But that seems a little nasty. So:
do they need to be inner and private ?
perhaps Outer can take a String, and determine from that whether to instantiate MapperA or MapperB. i.e. there's some factory capability here.
The simplest thing to do is to really determine if they need to be inner/private. If so, then they really shouldn't be referenced within the config, which should be talking about publicly accessible classes.
As far as I know, it's impossible until you make MapperA and MapperB usual public classes.
But if you do want to keep them as inner private classes then you can "inject" them manually.
You'll need to create method with #PostInit annotation and initialize your a field there (a = new MapperA () for example, or something more complex). With this approach you should also check that initialization callbacks are switched-on in your spring configuration.