I am trying to use Spring boot and akka. I have two processes and communicate with akka cluster. Only process A uses spring boot.
#Autowired
private ActorSystem springActorSystem;
#Autowired
private SpringExtension springExtension;
private ActorRef caActor;
caActor = springActorSystem.actorOf(springExtension.props("clientAgentActor"), "ca");
If I create the actor on process A, of course, using springExtension, all injections are working. However, the caActor is a cluster actor. If process B send a message to process A, the ClientAgentActor invoked somewhere, all injections are failed.
How to solve it?
#Component
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ClientAgentActor extends AbstractActor {
private static final Logger logger = LogManager.getLogger(ClientAgentActor.class);
#Autowired
ClientAgentService caService;
#Autowired
LineService lineService;
#Override
public Receive createReceive() {
//TODO
return receiveBuilder().match(String.class, msg -> logger.debug(msg)).build();
}
Thought the same for almost whole day. And I think there is no way to integrate Spring into Akka cluster with full DI for cross-cluster calls without changing the core of Akka Cluster.
When you do call without cluster inside one JVM you use Akka wrapper instead of pure Akka.
But when you do call in cluster this call is recieved on other node by pure Akka infrastructure without Spring wrapper, so this infrastructure doesn't know about Spring actors proxies that's why you see no injections.
So if you need Spring in Akka Cluster you need to wrap the core of this library with Spring infrastructure. Except it would be not easy to implement it would be also hard to follow the Akka rules and conventions in application architecture. For example, it would be too easy to inject transitive dependency which has blocking calls or multithreading code.
If you need to use some Spring functionality I think the best way to do this is to fully separate Akka infrastructure from Spring's one. And after application initialization set global static field with created ApplicationContext and make applicationContext.getBean(...) calls where they are needed. Of course you can do comfortable method for this. Or for example class with public static fields with needed beans which are set once after Spring initialization completes.
Related
In my Spring Boot application, I generally prefer to use #Import to individually activate my service beans instead of using #ComponentScan. I have an interface that's annotated with Spring Integration's #MessagingGateway and is intended to be used as an MQ entry point:
#MessagingGateway(defaultRequestChannel = CHANNEL_DISPATCH_QUEUE)
interface DispatchQueue {
public static final String CHANNEL_DISPATCH_QUEUE = "dispatchQueue"
void enqueue(Dispatch dispatch)
}
I tried to enable this in our usual style by adding #Import(DispatchQueue) to a configuration class, but I get an exception saying Specified class is an interface.
I'm aware that I could use #IntegrationComponentScan to turn on component scanning, but is there a mechanism equivalent to int:gateway that would allow me to tell Spring "activate this specific gateway interface" from Java configuration?
Note: An indirect solution using an IntegrationFlow will not work in my case because gateways declared in IntegrationFlows aren't visible to the Spring context before the flow is started; I need the typed bean in the context.
The current solution is like this:
#Bean
AnnotationGatewayProxyFactoryBean myGateway() {
return new AnnotationGatewayProxyFactoryBean(MyGateway.class);
}
See its javadocs:
* A {#link GatewayProxyFactoryBean} extension for Java configuration.
* The service interface may be marked with the {#link MessagingGateway} annotation.
* Otherwise, the default state is applied.
For the whole story there is also a GH issue to consider #Import support for interfaces: https://github.com/spring-projects/spring-integration/issues/3923 .
But I doubt it can done since an #Import is a part of Spring Framework which has no knowledge about Spring Integration gateway proxy infrastructure.
In my project, I have a lot of spring managed components that does about the same thing. I want to create a common Util class that does all the common operations for all my components. Since this Util class needs to access environment variables and beans it's instantiated like this:
// Util class:
public class FooUtil {
public FooUtil(Environment env) {
env.getProperty("FOO_TOPIC", "foo")
}
}
// Example configuration for one of my components:
#Configuration
public class ComponentConfig {
#Bean
FooUtil fooUtil(Environment env) {
return new FooUtil(env);
}
}
This allows FooUtil to access all environment variables and beans without itself being a component.
Now, this Util class also need to listen to kafka topics. Each component currently has a listener set up like this:
#KafkaListener(topics = "${FOO"_TOPIC:foo2}", containerFactory = "kafkaListenerContainerFactory")
private void fooListener(ConsumerRecord<String, Foo> rec) {
// Stuff...
}
I want to move this kafka listener into FooUtil. How can I do this? To be clear, I want FooUtil to start listening as soon as it's instantiated and initialized by a component.
Since the FooUtil is not managed by Spring you're unable to use the #KafkaListener annotation. If FooUtil was a bean managed by Spring it would be picked up by Spring and the listener annotation will cause Spring to connect the listener. All of this is done by Spring in KafkaListenerAnnotationBeanPostProcessor I believe.
Does the FooUtil have to be an unmanaged bean? I might be missing some details but from the question I can't see why it shouldn't be possible. If you need different instances for every bean using it you can use #Scope("prototype") on the FooUtil.
Turns out, you can make a kafka listener without using the #KafkaListener annotation (thanks Gary Russell). Just follow the instructions here (douevencode.com) for instructions of how to do it.
I have a question about #Service in spring, but i didn't find any response about it.
Situation :
I have a web application with #RestController using spring
Now for my service layer, I saw on some project two way of processing
#Service on service class and #Autowired on controller class (Create a bean that is a singleton excepted if we change the scope)
Create a object like a service
MyService service = new MySerivce()
So my questions are :
Create a object each time for each call of controller will not be a issue for memory ? If i create a load test (with Apache Jmeter) and send 1000 requests, it will create 1000 object my service so could be a problem no ?
Create a singleton with #Service will not be a issue for memory but, how spring will handle 1000 requests on 1 seconds for example. Will he push requests on a sort of queue and execute one at a time ?
What is the best practice for Service declaration and why ?
Thank in advance for any response
The whole point of dependency injection (using annotations such as #Autowired and #Service, #Component etc.) is to let Spring manage instances of service classes for you, instead of manually creating an instance with new MyService() each time you need it.
Letting Spring manage service class instances (and other Spring beans) has a number of advantages. For example, it makes it a lot easier to replace a service with a different implementation; you only need to change the Spring configuration for that. Also, it makes it easy to inject a mock version of a service for unit testing. Replacing real services with mocks would be really hard if the class you are trying to test is directly instantiating a specific implementation of the service class using new MyService().
how spring will handle 1000 requests on 1 seconds for example. Will he push requests on a sort of queue and execute one at a time ?
No. Calling a method on a service is just like any other method call. There is no invisible queue and there is also no reason why that would be necessary, as long as the methods in the service are thread-safe.
What is the best practice for Service declaration and why ?
When you use Spring, use Spring's dependency injection and never instantiate service classes using new in your code.
Spring Controller uses IOC mechanism like where Singleton objects are created, like in example you described #Service,
Application Server manages requests client, It uses thread pooling to handle request and generated or use same thread for request or response,
Spring applications are it self uses container mechanism where objects are created using #Service and #Autowired annotations.
When ever we are using #Service, this means we are telling spring
to create a object of that class and keep in Spring container, By
default its Singleton. Wherever we are using #Autowired then spring
handover that object to the Service/caller.
Whenever we are calling MyService service = new MySerivce()
java
creates new object of MyService every time. If that service called
1000 times then MyService object will get created 1000 times, and Spring
has no control on this.
Best Practice is :
Use Spring #Service annotation to create object and use annotation #Autowired to get that class object.
Handling 1000 Requests
Spring will not create 1000 new service object, it will use same service object (which is autowired) if object scope is Singleton which is default scope
but incase of Prototype, it is same as creating a object with new keyword.
SO in this case it will create 1000 objects.
For handling huge requests we need to make thread safe, pooling separately, in this case Spring will use its container pooling which is not very efficient.
If I use spring configuration to construct an object, is it possible for me to instantiate that object for clients without requiring them to import my spring configuration?
If this can't be done, does one client using spring always necessitate any of its clients use spring?
If this can be done, what is the correct way to do it? Something like this...
Library code:
public MyFactory() {
#Autowired
InitializedObject obj
public getInstance() {
return obj;
}
}
Client code:
import com.code.package.something.myfactory.MyFactory;
...
InitializedObject obj = MyFactory.getInstance();
One of the option is to avoid Spring annotations altogether, working only with spring configuration files to have constructor or setter based injection. That way your classes become independent of Spring code and your client can use your code as long as he provide the dependencies correctly using factory or whatever.
Autowiring dependency is done by the Spring container. If there's none, then it won't get autowired. You need to either ensure the client has a Spring container configured properly to autowire your dependency, or setup one yourself (eg: using ClassPathXmlApplicationContext)
I have an elaborate Spring bean setup for integration tests. Now I'm looking into writing a Robot library to expose my test data creation / behavior execution / assertion methods to Robot tests.
However what I understand from the Robot Framework user guide is that Robot can only instantiate library classes by calling a constructor. This is a bummer because I'd rather have my instances managed by Spring.
Ideally, I'd want to be able to give Robot the path to the application context and the bean name for the library. Failing that, I'd want Robot to be able to invoke a static factory method rather than a constructor, so I'm not forced to create a new instance.
One workaround I thought of is to create the Spring context in a static initializer and wire my dependencies by fetching beans from that context.
My original class looks like:
public class MyAwesomeTests {
#Autowired
private ThisHelper thisHelper;
#Autowired
private ThatHelper thatHelper;
// implementations of test steps and such
}
So I would change the above #Autowired fields to be protected, and create a subclass that statically initializes the Spring context and defines a Robot-friendly constructor:
public class RobotFriendlyTests extends MyAwesomeTests {
private static final ApplicationContext CONTEXT = new ClassPathXmlApplicationContext(...);
public RobotFriendlyTests() {
this.thisHelper = (ThisHelper) CONTEXT.getBean("thisHelper");
this.thatHelper = (ThatHelper) CONTEXT.getBean("thatHelper");
}
}
This should work, but it feels somewhat clunky. Is there a better way I should consider? Better yet, is there a Robot extension that already does this for me?
Have you thought about using Spring #Configurable, then even instances created by a normal new will become spring managed beans.
#See Spring Reference Chapter 7.8.1 Using AspectJ to dependency inject domain objects with Spring
There's a Robot Framework extension that supports using Spring to wire test libraries, take a look at: http://code.google.com/p/robotframework-javalibcore/wiki/SpringLibrary
I am not entirely sure whether it supports your case since I am not familiar at all with Spring.