Does autowiring work for classes instantiated from PerConnectionWebSocketHandler? - java

I can successfully read an autowired instance of HandlerSettings in the main class, so I know I have the application.properties entries correct.
#Component
#ConfigurationProperties(prefix="handler")
public class HandlerSettings {
private int timeout;
public int getTimeout(){
return timeout;
}
public void setTimeout(int timeout){
this.timeout = timeout;
}
}
I am having difficulties trying to autowire fields in the MyHandler class, which is instantiated within a PerConnectionWebSocketHandler.
#SpringBootApplication
#RestController
#EnableWebSocket
#EnableConfigurationProperties(HandlerSettings.class)
public class MyController implements WebSocketConfigurer{
#Bean
public WebSocketHandler myHandler() {
return new PerConnectionWebSocketHandler(MyHandler.class));
}
I want to autowire fields in an abstract base class of MyHandler.
#Component
public abstract class Handler implements WebSocketHandler {
#Autowired
private HandlerSettings handlerSettings;
...
}
MyHandler inherits from Handler:
public class MyHandler extends Handler
The example in the Spring documentation does not address what to do in either the case of inheritance, or the way classes are instantiated with the PerConnectionWebSocketHandler.
I have tried unsuccessfully to autowire fields in Handler. It is possible to autowire if the class is instantiated by PerConnectionWebSocketHandler? If so, is there something special I have to do to autowire the base class, but not the classes that extend it?

As per the Source Codes unless you set the PerConnectionWebSocketHandler.setBeanFactory in will instantiate a Handler without the Injected Beans.
#SpringBootApplication
#RestController
#EnableWebSocket
#EnableConfigurationProperties(HandlerSettings.class)
public class MyController implements WebSocketConfigurer {
#Autowired
private BeanFactory beanFactory;
#Bean
public WebSocketHandler myHandler() {
PerConnectionWebSocketHandler perConnectionWebSocketHandler = new PerConnectionWebSocketHandler(MyHandler.class);
perConnectionWebSocketHandler.setBeanFactory(beanFactory);
return perConnectionWebSocketHandler;
}
}

Related

Using Autowired bean inside Preauthorize expression in Spring

I have the following class for a resource in my Spring Application
#RestController
#RequestMapping("/whatever")
public class SomeResource {
#Autowired
private CoolService coolService;
#RequestMapping(
path = "",
method = RequestMethod.GET)
#PreAuthorize("hasPerm(#coolService.resolve(#attribute))")
public void resource(#PathVariable("attribute") int attribute) {
...
}
And I want to call the bean implementing CoolService that has been autowired by the Spring context, because for CoolService I have two beans that get activated depending on the profile at startup.
public interface CoolService {
resolve(int attribute);
}
#Service
#Profile("super")
public interface SuperCoolService implements CoolService {
public Object resolve(int attribute){...}
}
#Service
#Profile("ultra")
public interface UltraCoolService implements CoolService {
public Object resolve(int attribute){...}
}
However it seems that Spring does not know which bean to use because there is no single bean just named CoolService, and inside the Preauthorize I can't write #superCoolService or #ultraCoolService because it is profile-dependant.
How can I achieve this?
If you want to define 2 bean implement same interface, then you can user annotation #Qualifier.
For example:
#Service
#Qualifier("service1")
public interface SuperCoolService implements CoolService {
public Object resolve(int attribute){...}
}
#Service
#Qualifier("service1")
public interface UltraCoolService implements CoolService {
public Object resolve(int attribute){...}
}

What is the difference between using #Qualifier annotation and directly injecting bean in spring?

Suppose I have a program
#Component
public interface Coach{
public String giveCoaching();
}
#Component
public TennisCoach implements Coach{
#Override
public String giveCoaching(){
return "Teaching forhand";
}
}
I have two Demo classes in which I have injected the bean in different ways. what is the difference in both the injections
public class AppDemo{
#AutoWired
#Qualifier("tennisCoach")
private Coach theCoach;
}
public class AppDemo{
#AutoWired
private TennisCoach tennisCoach;
}
}
When you have more than 1 implementation for you interface, you will get an exception when Autowiring the bean. At that time #Qualifier will be used to choose the required implementation
#Component
public interface Coach{
public String giveCoaching();
}
#Component
public TennisCoach implements Coach{
#Override
public String giveCoaching(){
return "Teaching forhand";
}
}
#Component
public CricketCoach implements Coach{
#Override
public String giveCoaching(){
return "Teaching forbat";
}
}
Now the ambiguity will occur when you autowire the Coach Interface like below
public class AppDemo{
#AutoWired
private Coach theCoach;
}
So you have to qualify the right bean for the CoachInterface like below.
public class AppDemo{
#AutoWired
#Qualifier("tennisCoach")
private Coach theCoach;
}
Alternatively you can use #Primary annotation on top of any one of the implementation so that the Spring Container will by default choose the bean in case of more than 1 implementation for an interface.
But in the code below, you are directly creating the object for the implementation rather than interface.
public class AppDemo{
#AutoWired
private TennisCoach tennisCoach;
}
}
#Qualifier annotation is used when your interface has more than one implementing class, You should opt for the one you want inject as a bean in spring context.

How to #Autowired a concrete implementation of a service?

I have the following situation:
public interface ServiceAura extends Serializable { }
#Service
public class ServiceA implements ServiceAura {
....
}
#Service
public class ServiceB implements ServiceAura {
....
}
Now from the controller I need to call both of them by separate:
#Path("")
#Controller
public class ServiciosAuraPortalRESTfulService {
#Autowired
private ServiceAura srvA;
#Autowired
private ServiceAura srvB;
}
I have read about #Qualified, is this the only way? How can I archive this?
You're right. You can use #Qualifier("ServiceA") to specify which implementation you want to autowire.
#Path("")
#Controller
public class ServiciosAuraPortalRESTfulService {
#Autowired
#Qualifier("ServiceA")
private ServiceAura srvA;
#Autowired
#Qualifier("ServiceB")
private ServiceAura srvB;
}
On the service itself, you can use the annotation #Primary to specify which one is the default implementation that you want.
Alternatively, you can use the application context to retrieve a specific bean. You'll need to autowire the ApplicationContext class and then retrieve it with ServiceAura srvA = context.getBean(ServiceA.class);
There are two ways to do this.
The first way is using #Qualifier annotation as you've stated.
#Path("")
#Controller
public class ServiciosAuraPortalRESTfulService {
#Autowired
#Qualifier("serviceA")
private ServiceAura srvA;
#Autowired
#Qualifier("serviceB")
private ServiceAura srvB;
}
Your services should be defined like this:
#Service
#Qualifier("serviceA")
public class ServiceA implements ServiceAura {
....
}
#Service
#Qualifier("serviceB")
public class ServiceB implements ServiceAura {
....
}
Another way is to create interfaces that extend interface ServiceAura
interface ServiceAInterface extends ServiceAura {
}
class ServiceA implements ServiceAInterface {}
.... // the same for service B
And then in code:
public class ServiciosAuraPortalRESTfulService {
#Autowired
ServiceAInterface serviceA;
}

How to autowire Spring service with Class name?

I have multiple services and I want to autowire this services dynamically with using their class names. I have a method named "runCustomService" and this methods takes service's class names as input parameter (like "Service1" or "Service2"). I want to autowire these services and call its run method. Is there any way to do this?
#Service
public class Service1 extends BaseService{
#Autowired
private AnotherService anotherService;
public void run(){ .... }
}
#Service
public class Service2 extends BaseService{
#Autowired
private AnotherService anotherService;
public void run(){ .... }
}
public void runCustomService(String serviceClassName){
BaseService baseService = //Create baseService object from serviceClassName
baseService.run();
}
You could use qualifiers on your two services and get the correct bean based on the qualifier name from the ApplicationContext.
#Service
#Qualifier("Service1")
public class Service1 extends BaseService{
#Service
#Qualifier("Service2")
public class Service2 extends BaseService{
#Autowired
ApplicationContext applicationContext;
public void runCustomService(String serviceClassName){
BaseService baseService = applicationContext.getBean(serviceClassName);
baseService.run();
}
Get an instance of ApplicationContext and get bean by a class name:
#Autowired
ApplicationContext ctx;
Use the method getBean(...):
BaseService baseService = ctx.getBean(Service1.class.getName());
However, as the other answer says, I recommend you to use #Qualifier(...) to inject a certain named conditionally.

How to extend #Service?

Here is a sample class below:
#Service("testService")
public class TestService {
public String something() {
return "abc";
}
}
I want to extend the class and let the container know that it needs to pick up my extended class from now.
#Service("extendedTestService")
public class ExtendedTestServiceMock extends TestService {
#Override
public String something() {
return "xyz";
}
}
Test class:
public class TestClass extends SpringTest {
#Autowired
#Qualifier("extendedTestService")
private ExtendedTestService testService;
public void testMethod() {
......
}
}
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [TestService] is defined: expected single matching bean but found 2: ExtendedTestServiceMock,testService
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:865) ~[spring-beans-3.2.8.RELEASE.jar:3.2.8.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:770) ~[spring-beans-3.2.8.RELEASE.jar:3.2.8.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:489) ~[spring-beans-3.2.8.RELEASE.jar:3.2.8.RELEASE]
... 91 common frames omitted
How to resolve it?
Try using interfaces.
public interface TestService {
String something();
}
Implementations:
#Service
#Qualifier("testService")
public class TestServiceImpl implements TestService { ... }
#Service
#Qualifier("testServiceMock")
public class TestServiceMockImpl implements TestService { ... }
And the test class:
public class TestClass extends SpringTest {
#Autowired
#Qualifier("extendedTestService")
private TestService testService;
...
}
One solution that would work in your case is the #Primary annotation.
Your TestServiceMockImpl would look like:
#Service("extendedTestService ")
#Primary
public class ExtendedTestServiceMock extends TestService {
#override
public String something() {
return "xyz";
}
}
Check out this for more details on #Primary
I however suggest that you don't follow the above solution (since this will get out of hand very quick if you start using #Primary everywhere), that you instead take a look at Spring Profiles
There are a lot of way you could create your Spring configuration using profiles, but regardless of how you end up configuring the beans, the end result would be a more clean design.
If you have an identifier to help you decide which service to initialize, then you can use ConditionlOnProperty annotation
Ex:
#Service
#ConditionlOnProperty(value = "test.service.extension.enabled")
public class TestService {
}
#Service
#ConditionlOnProperty(value = "test.service.extension.enabled", havingValue = "false")
public class ExtendedTestServiceMock extends TestService {
}
If you want to use the extended test service, you can set the property test.service.extension.enabled=true in your application.properties
It depends on your definition order if your service define on the xml file.
Otherwise, you could use a BeanFactoryPostProcessor to do this, which is only registered in the test scenarios that you want this mocked.
public class SystemTestBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
// put your custom code in here
}
}
Or you can use #DependsOn to make sure the parent bean should be deploy firstly then your extend bean
#Service("testService")
#DependsOn("testService")
public class ExtendedTestService extends TestService {
}
Hope this helps.

Categories

Resources