I have an interface(X) with a method which prints out a statement and the interface has 2 implementations of it X1 and X2 and there is this class Y which has 2 objects of X autowired by type for X1 and X2 with the interface. i.e, like private X x; and private X x2; and when I call x.statement() it prints the default profile statement but when x2.statement() is called it still prints the x.statement() print statement instead of x2.statement().
BTW I am using Spring boot.
public interface HelloWorldService {
public String getGreeting();
}
#Component
#Profile({ "default", "english" })
public class HelloWorldServiceEnglishImpl implements HelloWorldService {
#Override
public String getGreeting() {
return "Hello World";
}
}
#Component
#Profile("spanish")
public class HelloWorldServiceSpanishImpl implements HelloWorldService {
#Override
public String getGreeting() {
return "Hola Mundo";
}
}
-
#Controller
public class GreetingController {
#Autowired
private HelloWorldService helloWorldService;
#Autowired
private HelloWorldService helloWorldServiceSpanish;
public void setHelloWorldServiceSpanish(HelloWorldServiceSpanishImpl helloWorldServiceSpanish) {
this.helloWorldServiceSpanish = helloWorldServiceSpanish;
}
public void setHelloWorldService(HelloWorldService helloWorldService) {
this.helloWorldService = helloWorldService;
}
public String sayHello() {
String greeting = helloWorldService.getGreeting();
System.out.println(helloWorldServiceSpanish.getGreeting());
System.out.println(greeting);
return greeting;
}
}
Firstly, what makes you think one of the Injected beans will be the 'English' bean and one will be the 'Spanish' bean. You inject two instances of the bean defined by the active profile: the 'English' bean. There is no Bean HelloWorldServiceKannadaImpl active for this profile. So both instances are instances of HelloWorldServiceEnglishImpl.
//this code is not called. The instance variable is auto-wired by field
//and the auto-wired bean is the only one available: the English one.
public void setHelloWorldServiceSpanish(HelloWorldServiceSpanishImpl helloWorldServiceSpanish) {
this.helloWorldServiceSpanish = helloWorldServiceSpanish;
}
How it should be:
#Controller
public class GreetingController {
//will be English or Spanish depending on Active profile.
#Autowired
private HelloWorldService helloWorldService;
public void sayHello() {
String greeting = helloWorldService.getGreeting();
System.out.println(greeting);
}
}
In your original code either remove the #Profile from the two beans, or change to spring.profiles.active=english,spanish and it might work as you expect. Although having both kinds of defeats the whole purpose which is to have dynamically injected bean based on the runtime environment.
At a time, only one Profile will be ACTIVE depending upon the set value, so you either can be able to print English or Spanish, but NOT both as per your config.
You can look at here for more details.
If you want to be able to inject the two beans of the same type at the same time you shouldn't bind them to profiles: you can assign them different names.
#Component("english")
public class HelloWorldServiceEnglishImpl implements HelloWorldService {
#Override
public String getGreeting() {
return "Hello World";
}
}
#Component("spanish")
public class HelloWorldServiceSpanishImpl implements HelloWorldService {
#Override
public String getGreeting() {
return "Hola Mundo";
}
}
then you can inject them with the #Named (java api) or #Qualifier (spring api) (if I remember it correctly) annotation
#Named("english") // #Qualifier("english")
#Autowired
private HelloWorldService helloWorldService;
#Named("spanish") // #Qualifier("spanish")
#Autowired
private HelloWorldService helloWorldServiceSpanish;
Related
I'm implementing a websocket service where incoming messages are passed to controllers and the controllers can then broadcast response messages to another websocket session(s).
When they broadcast the message back, there is either 1 of 2 issues. Either MySocketHandler is a different instance than the one that handled afterConnectionEstablished (using Autowired annotation on MySocketHandler in MessageRouter seems to create a new instance) or I get NoUniqueBeanDefinitionException (if I use ApplicationContext to specifically get the bean by class type).
An instance of my application should only have 1 MySocketHandler, so I annotated MySocketHandler with #Scope(ConfigurableBeanFactory.SCOPE_SINGLETON).
I suspect this has something to do with asynchronous event publishing and listening. I've refactored this code a few times to try to implement this the "Spring" way but there's some fundamental error each time.
I want to know how I can enforce the Spring container to create and reuse only 1 instance of MySocketHandler.
Here is my a minimalized version of MySocketHandler.java to exemplify the problem:
#Component
#Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class MySocketHandler extends BinaryWebSocketHandler {
#Autowired private ApplicationContext applicationContext
#Autowired private MessageRouter messageRouter;
private final HashMap<String, WebSocketSession> sessions = new HashMap<>();
#EventListener
public void onOutgoingBinaryMessageEvent(OutgoingBinaryMessageEvent event) {
// ERROR: NoUniqueBeanDefinitionException
applicationContext.getBean(MySocketHandler.class).broadcast(event.getBytes(), event.getConnectionIds());
}
#Override
public void afterConnectionEstablished(WebSocketSession session) {
sessions.put(session.getId(), session);
}
#Override
public void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
eventPublisher.publishEvent(new IncomingBinaryMessageEvent(
this,
message.getPayload().array(),
session.getId()));
}
private void broadcast(byte[] bytes, Set<String> playerIds) {
BinaryMessage binaryMessage = new BinaryMessage(bytes);
// this.sessions is null because its a different instance of MySocketHandler than the one that actually managing the connections
for (WebSocketSession session : sessions.values()) {
try {
webSocketSession.sendMessage(binaryMessage);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
And an example of the MessageRouter.java:
#Component
public class MessageRouter {
#Autowired private ApplicationEventPublisher eventPublisher;
public void send(Message message) {
eventPublisher.publishEvent(message);
}
#EventListener
private void routeMessageToController(SomeMessageEvent any, String connectionId) {
.....
// Parse message and route it to a controller class.
.....
}
}
}
Application entry point:
public class MyApplication implements WebSocketConfigurer {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(getSocketHandler(), "/").setAllowedOriginPatterns("*");
}
#Bean
public MySocketHandler getSocketHandler() {
return new MySocketHandler();
}
}
An instance of my application should only have 1 MySocketHandler, so I
annotated MySocketHandler with
#Scope(ConfigurableBeanFactory.SCOPE_SINGLETON).
First of all , singleton is applied in the bean level but not the type level. It can't ensure your application will only has the single bean of a particular type. You can still define multiple singleton bean for the same type.
In most general cases , a bean can be defined by the following ways:
Annotating the class with #Component (or its specialisation version such as #Repository , #Service , #Controller , #Configuration etc.)
Using #Bean method in the #Configuration class
Now you are doing :
#Component
#Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class MySocketHandler extends BinaryWebSocketHandler
}
#SpringBootApplication
public class MyApplication implements WebSocketConfigurer {
#Bean
public MySocketHandler getSocketHandler() {
return new MySocketHandler();
}
}
Note: #SpringBootApplication is a composed annotation which contain #Configuration
which means you are now defining two MySocketHandler beans . One with the name mySocketHandler (defined via #Component) and the other has the name getSocketHandler (defined via #Bean)
So to ensure there is only one MySocketHandler bean , either remove #Component from MySocketHandler or remove this #Bean method.
Having the following class structure:
public abstract class A {
String someProperty = "property"
public abstract void doSomething();
}
#Service
public class Aa extends A {
#Override
public abstract void doSomething() {
System.out.println("I did");
}
}
#Service
public class Ab extends A {
#Override
public abstract void doSomething() {
System.out.println("I did something else");
}
}
I need a way to tell Spring which A concrete class to Autowire in my Foo service, based on a property in a properties file.
#Service
public class Foo {
#Autowire
private A assignMeAConcreteClass;
}
And in my properties file I have this:
should-Aa-be-used: {true, false}
Remove the #Service annotation, instead write a #Bean-annotated method in a configuration class that reads the properties, and returns the appropriate A instance.
Not a new way but in your case I think that a possible suitable way would be to use
FactoryBean in the class that wants to inject the bean conditionally.
The idea is simple : you implement FactoryBean by parameterizing it with the interface of the bean that you want to inject and override getObject() to inject the wished implementation :
public class FactoryBeanA implements FactoryBean<A> {
#Autowired
private ApplicationContext applicationContext;
#Value("${should-Aa-be-used}")
private boolean shouldBeUsed;
#Override
public A getObject() {
if (shouldBeUsed) {
return applicationContext.getBean(Aa.class));
return applicationContext.getBean(Ab.class));
}
}
But FactoryBean instances are not classic beans. You have to configure it specifically.
You could configure it in a Spring Java configuration in this way :
#Configuration
public class FactoryBeanAConfiguration{
#Bean(name = "factoryBeanA")
public FactoryBeanA factoryBeanA() {
return new FactoryBeanA();
}
#Bean
public beanA() throws Exception {
return factoryBeanA().getObject();
}
}
I am using SpringBoot in my application and am currently using applicationContext.getBean(beanName,beanClass) to get my bean before performing operations on it. I saw in a couple of questions that it is discouraged to use getBean(). Since I am very new to Spring I don't know all the best practices and am conflicted. The solutions posed in the above linked question probably won't work in my use case. How should I approach this?
#RestController
#RequestMapping("/api")
public class APIHandler {
#Value("${fromConfig}")
String fromConfig;
private ApplicationContext applicationContext;
public Bot(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#PostMapping(value = "")
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
SomeInterface someInterface = applicationContext.getBean(fromConfig, SomeInterface.class);
someInterface.doSomething();
}
}
I have an interface called SomeInterface defined like:
public interface SomeInterface {
void doSomething();
}
And I have 2 classes which implements this interface called UseClass1 and UseClass2. My config file stores a string with the bean name of a class which I need to know in run-time and call the appropriate implementation of the method.
Any directions would be appreciated.
Since Spring 4.3 you can autowire all implementations into a Map consisting of pairs beanName <=> beanInstance:
public class APIHandler {
#Autowired
private Map<String, SomeInterface> impls;
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
String beanName = "..."; // resolve from your requestBody
SomeInterface someInterface = impls.get(beanName);
someInterface.doSomething();
}
}
assuming you have two implementations like following
// qualifier can be omitted, then it will be "UseClass1" by default
#Service("beanName1")
public class UseClass1 implements SomeInterface { }
// qualifier can be omitted, then it will be "UseClass2" by default
#Service("beanName2")
public class UseClass2 implements SomeInterface { }
This is only code works for me to get beans dynamically from ApplicationContext
#Service
public class AuthenticationService {
#Autowired
private ApplicationContext сontext;
public boolean authenticate(...) {
boolean useDb = ...; //got from db
IAuthentication auth = context.getBean(useDb ? DbAuthentication.class : LdapAuthentication.class);
return auth.authenticate(...);
}
}
You can define your spring bean component with
#Profile("dev") , #Profile("test")
and inject as mention comment, then switch profile with
-Dspring.profiles.active=test jvm argument
The real question is not how to solve this, but why would you inject something different based on a configuration value?
If the answer is testing, then perhaps it's better to use #Profiles as #murat suggested.
Why are different implementations of an interface there on your classpath?
Can't you package your application in a way that only one is there for one use case? (see ContextConfiguration)
I think you should probably use a configuration class to produce your bean based on the fromConfig string value:
Your controller:
#RestController
#RequestMapping("/api")
public class APIHandler {
#Autowired
SomeInterface someInterface;
#PostMapping(value = "")
public ResponseEntity post(#RequestBody HandlingClass requestBody) {
someInterface.doSomething();
}
}
The bean producer:
#Configuration
public class SomeInterfaceProducer {
#Value("${fromConfig}")
String fromConfig;
#Bean
public SomeInterface produce() {
if (fromConfig.equals("aValueForUseClass1") {
return new UseClass1();
} else {
return new UseClass2();
}
//...
}
}
or if you have DI in UseClass1 and/or UseClass2:
#Configuration
public class SomeInterfaceProducer {
#Value("${fromConfig}")
String fromConfig;
#Bean
public SomeInterface produce(#Autowired YourComponent yourComponent) {
SomeInterface someInterface;
if (fromConfig.equals("aValueForUseClass1") {
someInterface = new UseClass1();
someInterface.setYourComponent(yourComponent);
// or directly with the constructor if you have one with yourComponent as parameter.
} else {
someInterface = new UseClass2();
someInterface.setYourComponent(yourComponent);
}
//...
}
}
Is possible to specify that all setter should be autowired with one annotation?
This is my class:
#Component
public class MyClass {
private static Bean1 bean1;
//...
private static BeanN beanN;
public static Bean1 getBean1() {
return bean1;
}
#Autowired
public void setBean1(Bean1 bean1) {
MyClass.bean1 = bean1;
}
//...
public static BeanN getBeanN() {
return beanN;
}
#Autowired
public void setBeanN(BeanN beanN) {
MyClass.beanN = beanN;
}
}
No. There is no such built-in annotation. Also, Spring doesn't care that your method is to be interpreted as a bean mutator (a setter). Any method can be annotated with #Autowired and Spring will try to invoke it with the appropriate arguments.
Since the whole point of Spring is dependency injection, there's no reason for you to have static fields. Just inject the bean where you need it.
I am trying to add a simple String to my Spring Application Context, and then autowire this to a different existing bean (A) within the application context. I know this is not the usual way to go, yet I need to add many beans programmatically, which would otherwise make my xml configuration huge.
public class MyPostProcessor implements BeanFactoryPostProcessor, Ordered {
#Override
public int getOrder() {
return 0;
}
#Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerSingleton("myString", "this is the String");
A a = beanFactory.getBean(A.class);
beanFactory.autowireBean(a);
}
}
public class A {
#Autowired
public transient String message;
}
When running this, the property message of the instance of A is null. What am I missing?
EDIT: this is my application context:
#Configuration
class TestConfig {
#Bean
public A a() {
return new A();
}
#Bean
public MyPostProcessor postProcessor() {
return new MyPostProcessor();
}
}
And this is my test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.class)
public class MyTest {
#Autowired
private transient A a;
#Test
public void test() throws Exception {
System.err.println("Running");
System.err.println("This is the autowired String: " + a.message);
Thread.sleep(1000);
}
}
Thanks
You should not instantiate beans from BeanFactoryPostprocessors.
From BeanFactoryPostProcessor JavaDoc:
A BeanFactoryPostProcessor may interact with and modify bean
definitions, but never bean instances. Doing so may cause premature
bean instantiation, violating the container and causing unintended
side-effects.
In your case, the A bean is instantiated before BeanPostProcessors and therefore not autowired.
Remove the lines:
A a = beanFactory.getBean(A.class);
beanFactory.autowireBean(a);
And will work.
Try using the #Qualifier to specific which bean you want to Auto wire.
public class A {
#Autowired
#Qualifier("myString")
public transient String message;
}