I have:
#Component
class MyDecorator{
private Cache cache;
/*
some wrapped methods like get put remove
*/
}
Is it possible to autowire MyDecorator in different places with different cache?
I can configure XML like this:
<bean id="id1" class="MyDecorator ">
<property name="cache" value="Cache1" />
</bean>
<bean id="id2" class="MyDecorator ">
<property name="cache" value="Cache2" />
</bean>
But is there more elegance way without addition of xml configs, only with annotation?
Correct code should be
#Configuration
public class AppConfig {
#Bean
public MyAdapter adaptedCache2() {
return new MyAdapter (cache1);
}
#Bean
public MyAdapter adaptedCache2() {
return new MyAdapter (cache2);
}}
according to specs will be generated two beans adaptedCache1 and adaptedCache2
and now i can
autowire those beans with qualifiers adaptedCache1 and adaptedCache2
With Java configuration (Spring 3.1) you can write:
#Bean
public MyDecorator decoratedCache1() {
return new MyDecorator(cache1);
}
#Bean
public MyDecorator decoratedCache2() {
return new MyDecorator(cache2);
}
Of course in this case MyDecorator does not need #Component:
#Component
class MyDecorator{
private final Cache cache;
public MyDecorator(Cache cache) {
this.cache = cache;
}
}
Related
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.
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.
TL/DR: The problem boils down to creating a custom Spring scope, injecting a prototype-like scoped bean into a singleton with proxyMode = ScopedProxyMode.TARGET_CLASS but still getting a singleton in the Java config version of the configuration (whereas it works fine with XML).
UPDATE: Problem solved, see answer.
I'm using jBehave to write BDD test scenarios for our Spring application. We recently thought that we need independence in executing test scenarios (meaning that test context has to be reset before each scenario) and found this article on the web that addresses exactly the issue we're dealing with.
The article advises creating a custom Spring Scenario scope, assigning it to the class that represents test context and injecting an AOP proxy instead of the context file.
I've coded everything in accordance with the article and it worked great, but the thing is we need it in terms of Java config, not XML, and when I converted all the changes to Java config, it stopped working - meaning the Map in StoryContext was not reset after each test scenario and contained values from the previous scenario.
My changes were as follows:
marked the ScenarioScope class with the #Component annotation:
#Component
public class ScenarioScope implements Scope {
private final ConcurrentMap<String, Object> cache = new ConcurrentHashMap<>();
#BeforeScenario
public void startScenario() {
cache.clear();
}
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
return cache.putIfAbsent(name, objectFactory.getObject());
}
#Override
public Object remove(String name) {
return cache.remove(name);
}
#Override
public void registerDestructionCallback(String name, Runnable callback) {
}
#Override
public Object resolveContextualObject(String key) {
return null;
}
#Override
public String getConversationId() {
return "scenario scope";
}
}
created a Spring configuration class to add the new scope:
#Configuration
public class SpringConfiguration {
#Bean
public static CustomScopeConfigurer scopeConfigurer() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("scenario", new ScenarioScope());
return configurer;
}
}
annotated the StoryContext class with the #Component and #Scope annotations:
#Component
#Scope(value = "scenario", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class StoryContext {
private Map<String, Object> storyContext = new HashMap<>();
public void put(String key, Object value) {
storyContext.put(key,value);
}
public <T> T get(String key, Class<T> tClass) {
return (T) storyContext.get(key);
}
#PostConstruct
public void clearContext() {
storyContext.clear();
}
}
To my knowledge, the code above is analogous to the XML configuration, which was as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation=" http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<context:component-scan base-package="foo"/>
<bean id="scenarioScope" class="foo.ScenarioScope"/>
<bean class="foo.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="scenario" value-ref="scenarioScope"/>
</map>
</property>
</bean>
<bean id="storyContext" class="foo.StoryContext" scope="scenario">
<aop:scoped-proxy/>
</bean>
</beans>
Can anyone please point me to why the Java config is not working as expected? I've spent some time researching stackoverflow but the majority of similar questions is solved by adding proxyMode = ScopedProxyMode.TARGET_CLASS to the #Scope annotation, which I did.
UPDATE: So I tried to gradually move from XML to Java config by commenting / decommenting corresponding lines in the files and figured out that the problem is in this part of the code:
<bean class="foo.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="scenario" value-ref="scenarioScope"/>
</map>
</property>
</bean>
When I replace it with
#Configuration
public class SpringConfiguration {
#Bean
public static CustomScopeConfigurer scopeConfigurer() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("scenario", new ScenarioScope());
return configurer;
}
}
the StoryContext bean becomes a singleton. I tried doing it another way through registering a custom BeanFactoryPostProcessor and using the registerScope() method as described here, but it didn't work either.
I've managed to solve the problem, and the solution was trivial: the ScenarioScope instance in the SpringConfiguration class has to be managed by the Spring container rather than be created via the new() operator:
#Configuration
public class SpringConfiguration {
#Bean
public static CustomScopeConfigurer scopeConfigurer(ScenarioScope scenarioScope) {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.addScope("scenario", scenarioScope);
return configurer;
}
}
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;
}
How would I specify an anonymous inner bean in a named Spring Service?
#Service("myNamedService")
public class myNamedServiceClass {
private InnerBeanType innerBean;
#Autowired
public void setInnerBean(InnerBeanType innerBean) {
this.innerBean = innerBean;
}
}
I'm basically trying to achieve the equivalent of the following Spring XML wiring:
<bean name="myNamedService" class="somePackage.myNamedServiceClass">
<property name="innerBean">
<bean class="somePackage.InnerBeanType"/>
</property>
</bean>
Equivalent using pure annotations is I think not possible. You can use #Configuration though if the purpose is to not expose innerBean as a visible bean this way:
#Bean
public MyNamedServiceBean myNamedServiceBean(){
MyNamedServiceClass myNamedServiceBean = new MyNamedServiceClass();
myNamedServiceBean.setInnerBean(new InnerBeanType());
return myNamedServiceBean;
}