I have a requirement to initialize the "flag" field in a java enum by spring:
public enum EntitySequenceType {
TypeOne(-1),
TypeTwo(-1000);
private boolean flag;
EntitySequenceType(long firstId){
System.out.println("enum's constructor: "+this.name()+" firstId="+firstId);
}
public void setFlag(boolean val) {
this.flag = val;
}
public boolean getFlag() {
return this.flag;
}
}
The spring config is:
<bean id="myEnum" class="com.maven.start.maven_spring.EntitySequenceType"
factory-method="valueOf">
<property name="flag" value="true"/>
<constructor-arg>
<value>TypeOne</value>
</constructor-arg>
</bean>
But I have met some problems, so I have the following questions:
1.It seems that I can only write a single value in the <constructor-arg> tag in the config xml, I can't figure out why it is like this.
2.When I debugging the code, I found that when spring is initializing the bean, although I only write one constructor-arg value in the config xml, the constructor is called twice.How could this happen?
3.In the constructor of EntitySequenceType, I found that the "flag"'s value is null, why? There is "afterPropertiesSet()" can be called if the enum implements InitializingBean, but it is not called every time a enum type is constructed, so is there any method to be called after the field is set by spring, but is called every time a enum type is called?
Thanks for your answers!
It seems that I can only write a single value in the
tag in the config xml, I can't figure out why it is like this.
Used with a factory-method, the constructor-arg values are referring to the parameter list of that factory-method. EntitySequenceType.valueOf(String) only takes one argument, a String.
When I debugging the code, I found that when spring is initializing
the bean, although I only write one constructor-arg value in the
config xml, the constructor is called twice.How could this happen?
Enum types, like any other types, have their .class file loaded and initialized when they are first referenced in the code. The enum constants
TypeOne(-1),
TypeTwo(-1000);
are actually static fields in the compiled byte code. As such, they are initialized when the class is initialized. Those are two constructor calls, so that is what you see.
The constructor-arg value has nothing to do with these constructors, it has to do with your factory-method.
In the constructor of EntitySequenceType, I found that the "flag"'s
value is null, why? There is "afterPropertiesSet()" can be called if
the enum implements InitializingBean, but it is not called every time
a enum type is constructed, so is there any method to be called after
the field is set by spring, but is called every time a enum type is
called?
It can't be null, it's a primitive type. Your property is going to be set after the factory-method is called and executed. There is no need to implement InitializingBean.
Don't use an enum for this. Enums are meant to be constant.
The problem is:
The constructor for an enum type must be package-private or private access. It automatically creates the constants that are defined at the beginning of the enum body. You cannot invoke an enum constructor yourself.
Related
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"/>
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)
I am using spring-expression for parsing values in a class(present in jar). After i read this value i set it in the target class [a typical use case of spring-expression]. However, all the field's value from the class in jar can be parsed except, boolean value. In the source class, it is declared like this:
boolean isVerified;
//getter
public isVerified() {
return isVerfied;
}
Spring-expression code to read this value:
Expression sourceExp = parser.parseExpression(<source field string>);
sourceExp.getValue(sourceContext);
and this fails. The message is
Couldn't find property isVerified
My question is it because spring is looking for isIsVerified method rather than isVerified method? If not this what could be the reason for failure?
You don't show your expression but SpEL uses JavaBean semantics when accessing bean properties. It knows nothing about the internals of the referenced bean. When it encounters a property request...
"x.foo"
it tries to find the getter getFoo() (any return type) and if that's not found, it looks for isFoo() if it returns boolean.
I suspect you are trying to use x.isVerified. There is no such getter; you need to use x.verified, or you can invoke the method itself x.isVerified().
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.