Set a public variable of bean in spring without using a setter - java

I read on a few websites that with Spring its possible to do setter based Dependency Injection without having to create a setter for the injected variable it. Would nicely tidy up the code. I read this on another site and also here on stackoverflow.
I've tried it but in my case it does not work. I'm using 3.2.0.RELEASE. I'm getting the following error.
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'DI_without_setter' defined in class path resource [SpringBeans.xml]: Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'url' of bean class [net.comsys.springpropstest.DiWithoutSetter]: Bean property 'url' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
my Java test code
package net.xxx.springpropstest;
public class DiWithoutSetter {
private String url;
}
I've just added it to my main code. Not displayed here. I don't even use DiWithoutSetter in the main code.
SpringBeans.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="DI_without_setter" class="net.xxx.springpropstest.DiWithoutSetter">
<property name="url" value="jan" />
</bean>
If someone could shed some light on his issue that would be appreciated. Is it possible to set the value of a (public) variable in a Spring bean without using a setter method?

You can't do that (you need a setter), and that tutorial appears to be wrong (note in the example source zip, the classes have getters and setters).
Either add a setter or #Inject the field implicitly using autowiring.

You would need to annotate the field with #Autowired and enable component scanning for this injection to work without a setter method.

Related

Spring: apply #Autowired to property to eliminate setter

My understanding about the #Autowired annotation applying on a property of a bean is that by doing that we can eliminate the setter method. This seems valid when we choose to annotation based configuration: we just create a bean annotated with #Component and annotate #Autowired to its property of interest.
However I failed to do the same when I tested the idea using xml based configuration.
Here is what I put in the bean class:
#Autowired
private String message2;
public String getMessage2() {
return message2;
}
In the xml file:
<bean id="testBean" class="TestBean">
<property name="message2" value="Hello world!"/>
</bean>
the IDE complained "can't resolve the property" and failed to compile. Maybe using #Autowired with xml configuration is an odd marriage that is disallowed?
Anyone care to help me on this might-be-silly question?
If you don't want the setter, then get rid of the <property> element in your TestBean bean definition. That property requires that a setter be available to set the property. Because it's missing, if you actually tried to load the XML configuration, with a ClassPathXmlApplicationContext for example, you'd get this error
Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property message2 of bean class [org.example.TestBean]: Bean property message2 is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
After removing <property>, declare an appropriate bean to inject
<bean id="message2" class="java.lang.String">
<constructor-arg value="Helllo world!"/>
</bean>
You'll also need
<context:annotation-config />
to register the AutowiredAnnotationBeanPostProcessor that handles processing of #Autowired.
Note that declaring a String bean is awkward. You'd be better served by using the <property> or some property resolution mechanism that pulls the value from a config file and assigns it through a #Value annotated field.
#Value("${config.message2}")
private String message2;

Spring bean get initialized twice by xml and component-scan

I have created few singleton bean in spring. I added log inside constructor. I could see log is getting printed twice.
#Component
public class User{
public User() {
System.out.println(" Bean got initialized"+this);
}
private Integer userid;
private String username;
//getter and setter
}
I have defined bean in XML and configured auto-scan.
<context:component-scan base-package="com.demo" />
<bean id="user" class="com.demo.User"/>
Since I have initialized bean in context xml and package scan, bean is getting initialized twice. After removing #Component from class, I am seeing only once log.
Please confirm my understanding is correct or not?
That's correct; the component scan will pick it up, and then you explicitly create another copy.
#Component annotation was introduced in spring 2.5 in order to get rid of XML bean definition by using classpath scanning. if the class is annotated with #Component, it will auto detected in the classpath scanning and an instance will be created(instantiated). Since #Component does the job for creating the bean, you do not have to declare the bean in XML. So remove the XML bean declaration. so it will not create duplicate objects.
Note: if you do the annotation based bean declaration with #Component, you do not need to use XML based declaration as you have done. If you are doing the XML based bean declaration, you do not need to do the annotation based bean declaration. So make sure that you declare only one of these options. not with both.

How setter works inside Spring Framework?

I'm new in spring Framework. And actually i was doing an experiment with spring actually.
Look at this HelloWorld.java:
public class HelloWorld {
private String messageee;
public void setMessage(String messageee){
this.messageee=messageee;
}
public void show(){
System.out.println("message: "+messageee);
}
}
You see in this program, I've one variable which is outside declared as private named as messageee and next variable which is parametrized with setter named as messageee. You see both have same name.
Okay.. Now look at this bean file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="helloWorld" class="com.springframework.HelloWorld">
<property name="message" value="Hello.. This is Spring Framework example."></property>
</bean>
</beans>
Here you see inside bean tag. I've declared the property name as message. I don't understand, when i give the name as messageee it gives an error as:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'helloWorld' defined in class path resource [beans.xml]: Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'messageee' of bean class [com.springframework.HelloWorld]: Bean property 'messageee' is not writable or has an invalid setter method. Did you mean 'message'?
But when i give the name as message. It runs successfully. But i don't have any message's method or any kind of variables with this similar name. So, How setter works actually? Will you please elaborate?
Help would be appreciated!
You're confusing fields (or instance variables) with properties.
property is a term coming from the Java Beans specification. A property foo of a bean is data that can be accessed using a getter method called getFoo() (or isFoo() for a boolean) and/or set using a setter method called setFoo().
What these methods do internally, whether they get/set a variable or not, whether the variable is also named foo or anything else, is completely irrelevant. What matters is the name of the getter/setter.
So, when you define your bean and tell Spring to set the property named message, Spring will look for a method called setMessage(). It doesn't care about the private fields of your bean class.
The Spring IoC container also supports setter injection, which is the preferred method of dependency injection in Spring. Setter injection uses the set* methods in a class file to garner property names that are configurable in the spring XML config.
From a configuration standpoint, setter injection is easier to read because the property name being set is assigned as an attribute to the bean, along with the value being injected.
To determine the property names, Spring follows the JavaBeans Specification.
First of all, you are mixing up fields with properties - also your property name in the applicationContext.xml is wrong (it should be messageee)
You need to use #Autowired annotation with either:
1) fields i.e. messageee
or
2) setter i.e. setMessage()
If you are thinking "what is that!!???" Read about Spring's basic features with beans and how Spring is capable of taking POJOs (Plain Old Java Objects) to and configure them using the IoC framework. Read about #Autowired here - How does autowiring work in Spring?
Then you should be fine with this:
<bean id="helloWorld" class="com.springframework.HelloWorld">
<property name="message" value="Hello.. This is Spring Framework example."></property>
</bean>
BTW...good approach for looking into Spring by using the very basic Java stuff....good luck!

How do you inject a proxy into a service?

There is some information in the tomcat engine that we want to access run time, so we have the following in our app context (got this from this blog post):
<bean id="tomcatEngineProxy" class="org.springframework.jmx.access.MBeanProxyFactoryBean">
<property name="objectName" value="Catalina:type=Engine" />
<property name="proxyInterface" value="org.apache.catalina.Engine" />
<property name="useStrictCasing" value="false" />
</bean>
In a controller, we then autowired it in like this:
#Autowired
private MBeanProxyFactoryBean tomcatEngineProxy = null;
We cannot wire in org.apache.catalina.Engine like in the blog post, because that class is not available to us at build time. It's only available at run time with all the different tomcat versions running on the different machines.
We were able to get the information we needed from this #Autowire using reflection. Now, we want to move this functionality into a service. I added this to our app context:
<bean id="myService" class="com.foo.bar.MyServiceImpl">
<constructor-arg ref="tomcatEngineProxy" />
</bean>
And the class looks like this:
public class MyServiceImpl implements MyService
{
public MyServiceImpl(MBeanProxyFactoryBean tomcatEngineProxy) throws Exception
{
//stuff with the proxy
}
.....
}
When I do this, I get the following error:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myService' defined in ServletContext resource [/WEB-INF/spring/root-context.xml]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.springframework.jmx.access.MBeanProxyFactoryBean]: Could not convert constructor argument value of type [$Proxy44] to required type [org.springframework.jmx.access.MBeanProxyFactoryBean]: Failed to convert value of type '$Proxy44 implementing org.apache.catalina.Engine,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised' to required type 'org.springframework.jmx.access.MBeanProxyFactoryBean'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [$Proxy44 implementing org.apache.catalina.Engine,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [org.springframework.jmx.access.MBeanProxyFactoryBean]: no matching editors or conversion strategy found
Knowing basically nothing about how proxies work and how to use them, I'm not sure how to go about making this work. Is there some declaration I can use for my constructor arg that will match up? What is different between the #Autowire in the controller that does work and the constructor arg that doesn't work?
It's because your factory bean is exposing the result as the engine interface:
<property name="proxyInterface" value="org.apache.catalina.Engine" />
So if you try to wire in the "tomcatEngineProxy" bean itself, it's only compatible assignment is to "org.apache.catalina.Engine", since the created proxy implements only that interface.
try referencing the factory bean directly instead (notice the ampersand which is the syntax for finding the actual factory bean which created the object instead of the object itself):
<constructor-arg ref="&tomcatEngineProxy" />
How to inject FactoryBean instead of object it produces?
I believe that #Autowired works since the binding is done according to the type of the Bean (i.e. MBeanProxyFactoryBean), however the binding to the constructor argument is done by name and the name you provided tomcatEngineProxy does not match the type you expect since the type of a bean created using a FactoryBean is different than that of the FactoryBean.
It looks like the simplest solution will be to change MyServiceImpl to be:
public class MyServiceImpl implements MyService
{
#Autowired
private MBeanProxyFactoryBean tomcatEngineProxy;
public MyServiceImpl() throws Exception
{
//stuff with the proxy
}
.....
}
This should do the trick.

Does Spring require all beans to have a default constructor?

I don't want to create a default constructor for my auditRecord class.
But Spring seems to insist on it:
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'auditRecord' defined in ServletContext resource
[/WEB-INF/applicationContext.xml]:
Instantiation of bean failed;
nested exception is org.springframework.beans.BeanInstantiationException:
Could not instantiate bean class [com.bartholem.AuditRecord]:
No default constructor found;
nested exception is
java.security.PrivilegedActionException:
java.lang.NoSuchMethodException:
com.bartholem.AuditRecord
Is this really necessary?
No, you are not required to use default (no arg) constructors.
How did you define your bean? It sounds like you may have told Spring to instantiate your bean something like one of these:
<bean id="AuditRecord" class="com.bartholem.AuditRecord"/>
<bean id="AnotherAuditRecord" class="com.bartholem.AuditRecord">
<property name="someProperty" val="someVal"/>
</bean>
Where you did not provide a constructor argument. The previous will use default (or no arg) constructors. If you want to use a constructor that takes in arguments, you need to specify them with the constructor-arg element like so:
<bean id="AnotherAuditRecord" class="com.bartholem.AuditRecord">
<constructor-arg val="someVal"/>
</bean>
If you want to reference another bean in your application context, you can do it using the ref attribute of the constructor-arg element rather than the val attribute.
<bean id="AnotherAuditRecord" class="com.bartholem.AuditRecord">
<constructor-arg ref="AnotherBean"/>
</bean>
<bean id="AnotherBean" class="some.other.Class" />
nicholas' answer is right on the money for XML configuration. I'd just like to point out that when using annotations to configure your beans, it's not only simpler to do constructor injection, it's a much more natural way to do it:
class Foo {
private SomeDependency someDependency;
private OtherDependency otherDependency;
#Autowired
public Foo(SomeDependency someDependency, OtherDependency otherDependency) {
this.someDependency = someDependency;
this.otherDependency = otherDependency;
}
}
You might be able to do constructor based injection, i.e. something like this (taken from documentation found here)
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean>
but I'm not sure it will work.
If you are defining a JavaBean, you need to follow the convention and put a public no-arg constructor on it.
Almost all Java developers know that compiler adds a default constructor or better known as a no-argument constructor in every Java class, but many of them forget that it only does when you don't provide any other constructor. This means it becomes the developers' responsibility to add a no-argument constructor if he is adding explicit constructor. Now, Why it's important to provide default constructor in Java, What happens if your class doesn't have a no-argument constructor? Well, this is how it's asked in many Java interviews, most commonly as part of Spring and Hibernate interviews.
You should always define a no argument constructor in your Java class, even if you are writing an explicitly constructor, until you are absolutely sure that, it's won't be instantiated using reflection and instantiating it with no argument constructor is a bug, like in your example of Spring Bean.

Categories

Resources