I have a bit of code that is working :
#Component
public class MessageUtil {
#Autowired
#Qualifier("processMessages")
private ReloadableConfig config;
public String createMessage() {
return config.getPropertyStr("message.simple.signature");
}
}
The bean processMessages is defined here :
<bean id="processMessages" class="com.company.framework.resources.ReloadableConfig">
<property name="basename" value="classpath:com/company/aaa/bbb/domain/service/processMessages"/>
<property name="defaultEncoding" value="UTF-8"/>
<property name="cacheSeconds" value="60"/>
</bean>
Then I created some new classes :
public abstract class MessageBuilder {
#Autowired
#Qualifier("processMessages")
protected ReloadableConfig config;
public abstract String createMessage();
}
#Component
public class SimpleMessageBuilder extends MessageBuilder {
private String template;
private void setTemplate() {
template = config.getPropertyStr("message.simple.signature");
}
#Override
public String createMessage() {
setTemplate();
return template;
}
}
I now have a NullPointerException because in setTemplate(), config is null.
What's the problem in the second code ?
#Autowired doesn't work neither on field neither on constructors of abstract classes. It works on setter of abstract class but be sure to make it final because if overwritten by concrete class behavior is unstable. An abstract class isn't component-scanned since it can't be instantiated without a concrete subclass.
Related
I found many solutions to this issue, and choose the below one.
But it still gets NullpointerException, what's wrong?
A Class
#Component
public class A {
private static Foo foo;
#Autowired
public void setFoo(Foo foo) {
A.foo = foo;
}
public static someFunction() {
foo.doSomething();
}
}
B Class
#Service
public class B {
public void someFunction() {
A.someFunction();
}
}
You cannot auto-wire static properties in Spring, Static fields are instantiated during class load as they are the properties of the class while auto wired attributes work after spring initializes the beans.
Although you may use MethodInvokingFactoryBeanin Spring to achieve what you wanted.
some example would be in XML as below
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="foo.bar.Class.setTheProperty"/>
<property name="arguments">
<list>
<ref bean="theProperty"/>
</list>
</property>
</bean>
Edit :- without XML
inside your #Configuration class
do
#Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setStaticMethod("MyClass.staticMethod");
return methodInvokingFactoryBean;
}
let me know in case you need more help.
I have 2 beans of the same type:
#Component("beanA")
public class BeanA implements BaseBean {}
#Component("beanB")
public class BeanB implements BaseBean {}
This type is used in my service:
#Service
public class MyService {
#Autowired
private BaseBean baseBean;
}
Now I want to use both possible MyService beans in another service
#Service
public class AnotherService {
#Autowired
private MyService myServiceWithBeanA;
#Autowired
private MyService myServiceWithBeanB;
}
How can I achieve that? Maybe I should take another approach?
I know how to do it in XML-based beans configuration. How can I do it using annotations?
<bean id="AnotherService" class="AnotherService">
<property name="myServiceWithBeanA" ref="myServiceWithBeanA" />
<property name="myServiceWithBeanB" ref="myServiceWithBeanB" />
</bean>
<bean id="myServiceWithBeanA" class="MyService">
<property name="baseBean" ref="beanA" />
</bean>
<bean id="myServiceWithBeanB" class="MyService">
<property name="baseBean" ref="beanB" />
</bean>
<bean id="beanA" class="BeanA" />
<bean id="beanB" class="BeanB" />
The problem is that MyService is annotated with #Service. This means that it is a singleton - only one instance will be created.
In order to create multiple instances, you need to expose two #Beans via configuration.
#Configuration
public class MyServiceConfig {
#Bean
public MyService serviceA(#Qualifier("beanA") beanA) {
return new MyService(beanA);
}
#Bean
public MyService serviceB(#Qualifier("beanB") beanB) {
return new MyService(beanB);
}
}
MyService would become
public class MyService {
private BaseBean baseBean;
public MyService(BaseBean baseBean) {
this.baseBean = baseBean;
}
}
You can then pass all of these to the other service with qualifiers
#Service
public class AnotherService {
#Autowired
#Qualifier("serviceA")
private MyService myServiceWithBeanA;
#Autowired
#Qualifier("serviceB")
private MyService myServiceWithBeanB;
}
Using the #Qualifier annotation you can specify which bean you want to autowire.
#Service
public class AnotherService {
#Autowired
#Qualifier("beanA")
private MyService myServiceWithBeanA;
#Autowired
#Qualifier("beanB")
private MyService myServiceWithBeanB;
}
Hope this helps.
In your xml bean definition add the these tags <qualifier value="name"/>.
<bean id="myServiceWithBeanA" class="MyService">
<qualifier value="A"/>
<property name="baseBean" ref="beanA" />
</bean>
<bean id="myServiceWithBeanB" class="MyService">
<qualifier value="B"/>
<property name="baseBean" ref="beanB" />
</bean>
And then you can get them by using Qualifier annotation like this:
#Autowired
#Qualifier("A")
private MyService myServiceWithBeanA;
#Autowired
#Qualifier("B")
private MyService myServiceWithBeanB;
You also can do more stuff than that.
Have a look at the documentation on the following link:
Qualifier documentaiton
Not everything can be done with annotations. In cases where you need to create multiple instance of a bean but with different arguments you have to fallback to #Configuration and define those beans as you would in the XML config.
#Configuration
public class AppConfig {
#Bean
public MyService myServiceWithBeanA(BeanA beanA) {
return new MyService(beanA);
}
#Bean
public MyService myserviceWithBeanB(BeanB beanB) {
return new MyService(beanB);
}
}
Now just tell AnotherService to expect MyService twice with matching bean names as name of the bean defining method is its default qualifier.
#Service
public class AnotherService {
#Autowired
private MyService myServiceWithBeanA;
#Autowired
private MyService myServiceWithBeanB;
}
consider the following code:
public abstract class MachineInPitImpl extends AbstractPersistentObject implements MachineInPit {
protected PersonReference currentOperatorRef;
public void setCurrentOperatorRef(PersonReference currentOperatorRef) {
this.currentOperatorRef = currentOperatorRef;
this.currentOperatorObj = null;
}
public PersonReference getCurrentOperatorRef() {
return currentOperatorRef;
}
The above class is not wired with spring context, I need to extend this class and grab the values in the get method of this class in my new class.
I have wrote a class as this:
public class MachineOPJMXBeanImpl extends MachineInPitImpl {
public MachineOPJMXBeanImpl(){
}
#Override
public PersonReference getCurrentOperatorRef() {
return super.getCurrentOperatorRef();
}
}
But the value in this class get method is null.Why am I getting null value?
Here is the applicationcontext.xml file:
<bean id="machineOPJMXBean"
class="com.mincom.works.cc.personnel.node.MachineOPJMXBeanImpl" parent="machineInPitImpl">
<property name="currentOperatorRef" ref="currentOperatorRef"/>
</bean>
<bean id="machineInPitImpl" class="minestar.pitlink.domain.pitmodel.MachineInPitImpl" abstract="true" scope="singleton" lazy-init="true">
<property name="currentOperatorRef" ref="currentOperatorRef"/>
</bean>
<bean id="currentOperatorRef" class="minestar.machinetracking.domain.PersonReference"/>
Hopefully someone might have the answer for this problem.
I have a autowire problem occuring when I run my tests but not else.
The exception is pretty clear as it says 'myField' is missing. The field is however inherited from a base class. I cannot find the answer to my problem anywhere so I will try my luck here.
The exception
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'myService': Injection of autowired dependencies failed;
nested exception is java.lang.NoSuchFieldError: myField
My base class
public abstract class CommonStuff {
protected String myField; // the one thats gone missing
public abstract void setMyField(String myField);
}
My service
#Service("myService")
public class MyService extends CommonStuff {
#Value("${myProperty.myField}")
public void setMyField(String myField) {
this.myField = myField;
}
...
}
My controller
#Controller
public class MyController {
#Autowired
private MyService myService;
public void setMyService(MyService myService) {
this.myService = myService;
}
...
}
My application-context.xml
Nothing seems to be left out.
<context:component-scan base-package="com.myapp" />
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:my.properties</value>
<value>classpath:myother.properties</value>
</list>
</property>
</bean>
my.properties
myProperty.myField=some value
My test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:application-context.xml" })
public class MyControllerTest {
#Autowired
MyController myController;
#Mock
MyService myService;
#Test
public void myServiceTest() {
// do stuff with myService
}
...
}
The problem
As I mentioned the code runs fine but a spring has trouble autowiring whenever I try to run the test.
The problem is not the test but the wiring when starting the test. Somehow spring cannot find myField from the abstract CommonStuff class so it says that MyService do not have the field.
If needed I can post the full stacktrace but I think the essense of the exception is already here.
I gave up and moved the myField back into the service and skipped the setMyField.
This is how it is now.
Base class
public abstract class CommonStuff {
public abstract String getMyField();
public void doCommonStuff() {
// use getMyField()
}
}
My service
#Service("myService")
public class MyService extends CommonStuff {
#Value("${myProperty.myField}")
private String myField;
#Override
public String getMyField() {
return myField;
}
...
}
This solves what I wanted since I now have access to myField from CommonStuff.
I have a problem to select the correct spring bean that should get injected.
I need a way to tell the spring container what bean to inject depeding on the call to a previous class. I do all the spring bean wiring in xml.
My question: is this possible and if it is any reference on an implementation?
I have created some sample code to illustrate what i´m trying to accomplish. Feel free to change it so that it will work to get the correct ReportHeader bean injected depending on the selected reportType during runtime.
public enum ReportType{
Credit,
Annul
}
public class ReportService {
private ReportHeaderService reportHeaderService;
private ReportType reportType;
public ReportService (){}
public setReportType(ReportType reportType){
this.reportType = reportType;
}
public void setReportHeaderService(ReportHeaderService reportHeaderService){
this.reportHeaderService = reportHeaderService;
}
private void generateHeader(){
//i would like to call my service like this and have the correct bean injected to ReportHeader.
reportHeaderService.generateHeader(reportType)
}
}
public class ReportHeaderService {
private ReportHeader reportHeader;
//this will call the injected bean that needs to be selected accoring to the ReportType
public void generateHeader(ReportType type){
reportHeader.createHeader();
}
}
public interface ReportHeader{
public void createHeader();
}
public class CreditReportHeader implements ReportHeader{
public void createHeader(){
..dostuff();
}
}
public class AnnulReportHeader implements ReportHeader{
public void createHeader(){
..dostuff();
}
}
Consider injecting a Map<ReportType, ReportHeader> to ReportHeaderService, so that generateHeader works as:
public class ReportHeaderService {
private Map<ReportType, ReportHeader> reportHeaderMap;
public void generateHeader(ReportType type){
ReportHeader reportHeader = reportHeaderMap.get(type);
if (reportHeader != null) {
reportHeader.createHeader();
}
}
}
You can define a ReportHeaderFactory to get the ReportHeader according to ReportType:
public class ReportHeaderFactory {
private CreditReportHeader creditReportHeader;
private AnnulReportHeader annulReportHeader;
public ReportHeader getReportHeader(ReportType reportType) {
switch (reportType) {
case Credit:
return creditReportHeader;
case Annul:
return annulReportHeader;
default:
throw new IllegalArgumentException("No Such Header");
}
}
}
Re-define the ReportHeaderService with an instance of ReportHeaderFactory:
public class ReportHeaderService {
//private ReportHeader reportHeader;
private ReportHeaderFactory headerFactory;
//this will call the injected bean that needs to be selected accoring to the ReportType
public void generateHeader(ReportType type){
//reportHeader.createHeader();
headerFactory.getReportHeader(type);
}
}
As you are doing all the spring bean wiring in xml, you just need to make below entries in the config file:
<bean id="ReportHeaderService" class="x.y.ReportHeaderService">
<property name="headerFactory" ref="headerFactory" />
</bean>
<bean id="headerFactory" class="x.y.ReportHeaderFactory">
<property name="creditReportHeader" ref="creditReportHeader" />
<property name="annulReportHeader" ref="annulReportHeader" />
</bean>
<bean id="creditReportHeader" class="x.y.CreditReportHeaderImpl" />
<bean id="annulReportHeader" class="x.y.AnnulReportHeaderImpl" />