Dependency Injection with interface in Spring - java

I've a MainHandler class :
#Component
class MainHandler {
//inject this
private Handler handler;
#Autowired
public MainHandler(Handler handler){
this.handler = handler;
}
public void action(String message){
//watch photo
if (message.equals("photo")){
handler.handle();
}
if(message.equals("audio")){
//play music
handler.handle();
}
if(message.equals("video")){
//play video
handler.handle();
}
}
And following other handlers with interface.
Can I inject dependencies with Spring Boot by only interface type handler?
#Component
public interface Handler {
void handle();
}
#Component
class PhotoHandler implements Handler {
public void handle(){
System.out.println("Featuring photo...");
}
}
#Component
class VideoHandler implements Handler {
public void handle(){
System.out.println("Playing video...");
}
}
#Component
class AudioHandler implements Handler {
public void handle(){
System.out.println("Playing music...");
}
}
Or I want to try something like this below. Is it possible ?
class MainHandler {
private VideoHandler videoHandler;
private AudioHandler audioHandler;
private PhotoHandler photoHandler;
#Autowired
public MainHandler(VideoHandler videoHandler,
AudioHandler audioHandler,
PhotoHandler photoHandler) {
this.videoHandler = videoHandler;
this.audioHandler = audioHandler;
this.photoHandler = photoHandler;
}
public void action(String message){
//watch photo
if (message.equals("photo")){
photoHandler.handle();
}
if(message.equals("audio")){
//play music
audioHandler.handle();
}
if(message.equals("video")){
//play video
videoHandler.handle();
}
}
}
So, type of handler depends on user's message. I don't know how Spring can choose which handler gonna be used in this context. Any solution?

There can be multiple solution to this case.
Option #1
You can tweak a design of your handler a bit.
For instance you can introduce a method
boolean canHandle(String message);
so each handler can answer whether passed message can be handled or not.
Then you can inject a list of all handlers into your MainHandler.
private List<Handler> handlers;
Now having that list you can call each handler by message:
public void action(String message) {
handlers.stream()
.filter(h -> h.canHandle(message))
.forEach(handler -> handler.handle());
}
Full example:
#SpringBootApplication
public class SO62370917 {
public static void main(String[] args) {
SpringApplication.run(SO62370917.class, args);
}
#Component
static class MainHandler {
private final List<Handler> handlers;
MainHandler(List<Handler> handlers) {
this.handlers = handlers;
}
public void action(String message) {
handlers.stream()
.filter(h -> h.canHandle(message))
.forEach(Handler::handle);
}
}
#Bean
CommandLineRunner cmd(MainHandler mainHandler) {
return args -> {
mainHandler.action("video");
mainHandler.action("audio");
mainHandler.action("photo");
};
}
interface Handler {
void handle();
boolean canHandle(String message);
}
#Component
class PhotoHandler implements Handler {
public void handle(){
System.out.println("Featuring photo...");
}
#Override
public boolean canHandle(String message) {
return "photo".equals(message);
}
}
#Component
class VideoHandler implements Handler {
public void handle(){
System.out.println("Playing video...");
}
#Override
public boolean canHandle(String message) {
return "video".equals(message);
}
}
#Component
class AudioHandler implements Handler {
public void handle(){
System.out.println("Playing music...");
}
#Override
public boolean canHandle(String message) {
return "audio".equals(message);
}
}
}
Option #2
Use qualifiers.
You can name your handlers however you like and then inject a Map<String, Handler> into your mainHandler. The key would be a bean name and the value - the actual handler. Spring will automatically take care of this.
#SpringBootApplication
public class SO62370917 {
public static void main(String[] args) {
SpringApplication.run(SO62370917.class, args);
}
#Component
static class MainHandler {
private final Map<String, Handler> handlers;
MainHandler(Map<String, Handler> handlers) {
this.handlers = handlers;
}
public void action(String message) {
if (handlers.containsKey(message)) {
handlers.get(message).handle();
}
}
}
#Bean
CommandLineRunner cmd(MainHandler mainHandler) {
return args -> {
mainHandler.action("video");
mainHandler.action("audio");
mainHandler.action("photo");
};
}
interface Handler {
void handle();
}
#Component("photo")
class PhotoHandler implements Handler {
public void handle() {
System.out.println("Featuring photo...");
}
}
#Component("video")
class VideoHandler implements Handler {
public void handle() {
System.out.println("Playing video...");
}
}
#Component("audio")
class AudioHandler implements Handler {
public void handle() {
System.out.println("Playing music...");
}
}
}
The output:
2020-06-14 13:06:47.140 INFO 29447 --- [ main] com.example.demo.SO62370917 : Started SO62370917 in 1.356 seconds (JVM running for 1.795)
Playing video...
Playing music...
Featuring photo...

There are two simple ways in which you can approach :
Recommended : You can use #Qualifier to inject the desired particular bean.
For example
#Component
class MainHandler {
#Autowired
#Qualifier("videoHandler") // example
private Handler handler;
public void action(){
handler.message(); // this will print playing video...
}
}
You can inject the ApplicationContext.
For example :
#Component
class MainHandler {
#Autowired
private ApplicationContext context;
public void action(String message){
//watch photo
if (message.equals("photo")){
((PhotoHandler) context.getBean(PhotoHandler.class)).handle();
}
if(message.equals("audio")){
//play music
((AudioHandler) context.getBean(AudioHandler.class)).handle();
}
if(message.equals("video")){
//play video
((VideoHandler) context.getBean(VideoHandler.class)).handle();
}
}
}

Related

Issue in making Consumer Listener aware of seeking from beginning

I am not able to understand design of SeekAware interfaces.
I am trying to implement dynamic listeners to the new topic created from spring-kafka rest api.As of now only consumes consumer records from the time the listener started on topic but also wanted to modify offsets on start of this listener so that if want to seekToBeginning, my listener whenever invoked will read from start of topic.
the bits and pieces of important code are as follows
#Component
public class CustomKafkaListenerRegistrar implements InitializingBean {
#Autowired
private KafkaListenerEndpointRegistry kafkaListenerEndpointRegistry;
#Autowired
private KafkaListenerContainerFactory kafkaListenerContainerFactory;
public void registerCustomKafkaListener(String name, CustomKafkaListenerProperty customKafkaListenerProperty,
boolean startImmediately) {
String listenerClass = String.join(".", CustomKafkaListenerRegistrar.class.getPackage().getName(),
customKafkaListenerProperty.getListenerClass());
CustomMessageListener customMessageListener =
(CustomMessageListener) beanFactory.getBean(Class.forName(listenerClass));
kafkaListenerEndpointRegistry.registerListenerContainer(
customMessageListener.createKafkaListenerEndpoint(name, customKafkaListenerProperty.getTopic()),
kafkaListenerContainerFactory, startImmediately);
}
public abstract class CustomMessageListener {
private static int NUMBER_OF_LISTENERS = 0;
#Autowired
private KafkaProperties kafkaProperties;
public abstract KafkaListenerEndpoint createKafkaListenerEndpoint(String name, String topic);
protected MethodKafkaListenerEndpoint<String, String> createDefaultMethodKafkaListenerEndpoint(String name,
String topic) {
MethodKafkaListenerEndpoint<String, String> kafkaListenerEndpoint = new MethodKafkaListenerEndpoint<>();
kafkaListenerEndpoint.setId(getConsumerId(name));
kafkaListenerEndpoint.setGroupId(kafkaProperties.getConsumer().getGroupId());
kafkaListenerEndpoint.setAutoStartup(true);
kafkaListenerEndpoint.setTopics(topic);
kafkaListenerEndpoint.setMessageHandlerMethodFactory(new DefaultMessageHandlerMethodFactory());
return kafkaListenerEndpoint;
}
private String getConsumerId(String name) {
if (isBlank(name)) {
return CustomMessageListener.class.getCanonicalName() + "#" + NUMBER_OF_LISTENERS++;
} else {
return name;
}
}
private boolean isBlank(String string) {
return Optional.ofNullable(string)
.map(String::isBlank)
.orElse(true);
}
}
#Component
public class MyCustomMessageListener extends CustomMessageListener {
#Override
#SneakyThrows
public KafkaListenerEndpoint createKafkaListenerEndpoint(String name, String topic) {
MethodKafkaListenerEndpoint<String, String> kafkaListenerEndpoint =
createDefaultMethodKafkaListenerEndpoint(name, topic);
kafkaListenerEndpoint.setBean(new MyMessageListener());
kafkaListenerEndpoint.setMethod(MyMessageListener.class.getMethod("onMessage", ConsumerRecord.class));
return kafkaListenerEndpoint;
}
#Slf4j
private static class MyMessageListener implements MessageListener<String, String> {
#Override
public void onMessage(ConsumerRecord<String, String> record) {
log.info("My message listener got a new record: " + record);
log.info("message is: "+record.toString());
CompletableFuture.runAsync(this::sleep)
.join();
log.info("My message listener done processing record: " + record);
}
#SneakyThrows
private void sleep() {
Thread.sleep(5000);
}
}
}
As far as I understood, MyCustomMessageListener will implement the interface ConsumerSeekAware which in turn will provide method seekToBeginning will get invoked on creation and trigering of listener.
Kindly help.
Note: I am using MessageListener<String, String> having method onMessage which as per my knowledge is equivalent to working of #KafkaListener.Kindly correct me if I am wrong.
If you implement MessageListener there is no need for all the KafkaListenerEndpoint stuff; just create a listener container and add your listener to it.
#SpringBootApplication
public class So72546425Application {
public static void main(String[] args) {
SpringApplication.run(So72546425Application.class, args);
}
#Bean
ApplicationRunner runner1(KafkaTemplate<String, String> template) {
return args -> {
template.send("so72546425", "foo");
};
}
#Bean
ApplicationRunner runner2(ContainerCreator creator) {
return args -> {
System.out.println("Hit enter to create a container");
System.in.read();
MessageListenerContainer container = creator.createContainerForTopic("so72546425");
container.start();
};
}
}
#Component
class ContainerCreator {
private final ConcurrentKafkaListenerContainerFactory<String, String> factory;
ContainerCreator(ConcurrentKafkaListenerContainerFactory<String, String> factory) {
this.factory = factory;
}
ConcurrentMessageListenerContainer<String,String> createContainerForTopic(String topic) {
ConcurrentMessageListenerContainer<String, String> container = factory.createContainer(topic);
container.getContainerProperties().setMessageListener(new MyListener());
container.getContainerProperties().setGroupId("group.for." + topic);
return container;
}
}
class MyListener extends AbstractConsumerSeekAware implements MessageListener<String, String> {
#Override
public void onMessage(ConsumerRecord<String, String> data) {
System.out.println(data);
}
#Override
public void onPartitionsAssigned(Map<TopicPartition, Long> assignments, ConsumerSeekCallback callback) {
System.out.println("Seeking to beginning");
callback.seekToBeginning(assignments.keySet());
}
}

How to extract the common condition check in a class

If I have a class, where all methods should evaluate only if a boolean attribute is true, what is the best way to define it in terms of best practices?
Is there a way to replace the recurring isEnabled check in all the methods by a single check? The current form works, I'm looking for a way to improve it to not have isEnabled checked in all the methods.
#Singleton
public class SomeClass implements SomeInterface {
EventPublisher eventPubilsher;
boolean isEnabled;
#Inject
public SomeClass(final Decider decider, EventPublisher, eventPublisher) {
this.isEnabled = decider.isSomethingEnabled();
this.eventPublisher = eventPublisher;
}
#Override
public void method1() {
if(isEnabled){
eventPublisher.publishSomething1();
}
}
#Override
public void method2() {
if(isEnabled){
eventPublisher.publishSomething2();
}
}
#Override
public void method3() {
if(isEnabled){
eventPublisher.publishSomething3();
}
}
}
You can have implementation for enabled stuff, and one for disabled stuff. The disabled implementation won't do anything:
public class DisabledSomeClass implements SomeInterface {
#Override
public void method1() {
}
#Override
public void method2() {
}
#Override
public void method3() {
}
}
Enabled one will publish stuff:
public class EnabledSomeClass implements SomeInterface {
EventPublisher eventPublisher;
public EnabledSomeClass(EventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
#Override
public void method1() {
eventPublisher.publishSomething1();
}
#Override
public void method2() {
eventPublisher.publishSomething2();
}
#Override
public void method3() {
eventPublisher.publishSomething3();
}
}
Then extract the logic, if functionality is enabled in new factory class - SomeInterfaceFactory (probably not the best name, but you get the idea), it will return the correct implementation, depending on whether something is enabled.
public class SomeInterfaceFactory {
public static SomeInterface getInstance(Decider decider, EventPublisher eventPublisher) {
if (decider.isSomethingEnabled()) {
return new EnabledSomeClass(eventPublisher);
} else {
return new DisabledSomeClass();
}
}
}
This should work
#Singleton
public class SomeClass implements SomeInterface {
EventPublisher eventPubilsher;
boolean isEnabled;
#Inject
public SomeClass(final Decider decider, EventPublisher, eventPublisher) {
this.isEnabled = decider.isSomethingEnabled();
this.eventPublisher = eventPublisher;
}
#Override
public void method1() {
publish(ep->ep.publishSomething1());
}
#Override
public void method2() {
publish(ep->ep.publishSomething2());
}
#Override
public void method3() {
publish(ep->ep.publishSomething3());
}
private void publish(Consumer<EventPublisher> callback){
if(isEnabled) callback.accept(eventPublished);
}
}
A Runnable interface can receive a lambda without args and run a lambda function with run() method. So, you can create a method inside your Decider class and receive a Runnable as a parameter, like:
class Decider{
private boolean enabled;
//...
public void execute(Runnable runnable){
if(enabled) runnable.run();
}
}
And receive your Decider and do:
//...
EventPublisher eventPubilsher;
Decider decider;
#Inject
public SomeClass(final Decider decider, EventPublisher, eventPublisher) {
this.decider = decider;
this.eventPublisher = eventPublisher;
}
#Override
public void method1() {
Decider.execute(() -> eventPublisher.publishSomething1());
}
//...
You're locking the evaluation of the condition to the constructor. If you want any flexibility, you should pull it out.
public class SomeClass implements SomeInterface {
boolean isEnabled;
public SomeClass(EventPublisher, eventPublisher) {
//
}
public void addDecide(boolean value){
isEnabled = value;
}
boolean getEnabled(){
return isEnabled;
}
#Override
public void method3() {
if(getEnabled()){
eventPublisher.publishSomething3();
}
}
}
You can even go crazy and add a supplier:
public void addDecide(Supplier<Boolean> supplier){
supplier = supplier;
}
boolean getEnabled(){
return supplier.get();
}

Dagger2 Named(#Named) Injection with polymorphism or different object

Hi i am trying to study named injection in Dagger2
Here are my Java classes but none of them seems to be working.
What i want is that based on #Named annotation i wish to get different objects.
public interface Server {
public void start();
public void stop();
public String request(String request);
}
public abstract class AbstractServer implements Server {
private boolean started;
#Override
public void start() {
started = true;
}
#Override
public void stop() {
if (!started) {
throw new IllegalStateException("Server was not started");
}
}
}
public class AudioServer extends AbstractServer{
#Override
public String request(String request) {
return "Response from Audio server: " + request;
}
}
public class VideoServer extends AbstractServer {
#Override
public String request(String request) {
return "Response from Video server: " + request;
}
}
#Module
public class ServerModule {
public ServerModule() {
}
#Provides
#Named("audio")
#Singleton
AudioServer provideAudioServer() {
return new AudioServer();
}
#Provides
#Named("video")
#Singleton
VideoServer provideVideoServer() {
return new VideoServer();
}
}
Please not ServerComponent.java is not compiling
#Singleton
#Component(modules = {ServerModule.class})
public interface ServerComponent {
AudioServer provideAudioServer();
VideoServer provideVideoServer();
void inject(TestInject inject);
}
public class TestInject {
private static final Logger logger = Logger.getLogger(TestInject.class.getSimpleName());
#Inject
#Named("audio")
Server audioServer;
public TestInject() {
// ServerComponent component = DaggerServerComponent.builder()
// .build();
// component.inject(this);
}
public void test() {
String serverResponse = null;
if (audioServer != null) {
serverResponse = audioServer.request("game.mp3");
logger.warning(serverResponse);
} else {
serverResponse = "Failure";
logger.info(serverResponse);
}
}
public static void main(String[] args) {
TestInject inject = new TestInject();
inject.test();
}
}
EDITED Please see answer in TestInject.java and ServerComponent.java
public interface Server {
public void start();
public void stop();
public String request(String request);
}
public abstract class AbstractServer implements Server {
private boolean started;
#Override
public void start() {
started = true;
}
#Override
public void stop() {
if (!started) {
throw new IllegalStateException("Server was not started");
}
}
}
public class AudioServer extends AbstractServer{
#Override
public String request(String request) {
return "Response from Audio server: " + request;
}
}
public class VideoServer extends AbstractServer {
#Override
public String request(String request) {
return "Response from Video server: " + request;
}
}
#Module
public class ServerModule {
public ServerModule() {
}
#Provides
#Named("audio")
#Singleton
AudioServer provideAudioServer() {
return new AudioServer();
}
#Provides
#Named("video")
#Singleton
VideoServer provideVideoServer() {
return new VideoServer();
}
}
Please not ServerComponent.java is not compiling
#Singleton
#Component(modules = {ServerModule.class})
public interface ServerComponent {
#Named("audio")
Server provideAudioServer();
#Named("video")
Server provideVideoServer();
void inject(TestInject inject);
}
public class TestInject {
private static final Logger logger = Logger.getLogger(TestInject.class.getSimpleName());
#Inject
#Named("audio")
Server audioServer;
#Inject
#Named("video")
Server videoServer;
public TestInject() {
ServerComponent component = DaggerServerComponent.builder()
.build();
component.inject(this);
}
public void testAudioServer() {
String serverResponse = null;
if (audioServer != null) {
serverResponse = audioServer.request("game.mp3");
logger.warning(serverResponse);
} else {
serverResponse = "audio server Failure";
logger.info(serverResponse);
}
}
public void testVideoServer() {
String serverResponse = null;
if (videoServer != null) {
serverResponse = videoServer.request("movie.mp4");
logger.warning(serverResponse);
} else {
serverResponse = "Video server Failure";
logger.info(serverResponse);
}
}
public static void main(String[] args) {
TestInject inject = new TestInject();
inject.testAudioServer();
inject.testVideoServer();
}
}
Your main problem seems to be related to the fact that you expect in the class TestInject a Server named audio while your provider returns AudioServer so dagger cannot satisfy your dependency.
Indeed don't forget that the annotation #Named is used to distinguish 2 objects of the same type, in other words you can annotate with #Named("audio") different providers as long as they don't return the same type. The object produced will then be identified by its type and its name.
So for example here is one way to fix you problem:
The class TestInject:
public class TestInject {
...
public TestInject() {
// Needed to inject your dependencies
ServerComponent component = DaggerServerComponent.builder()
.build();
component.inject(this);
}
...
}
The class ServerComponent
#Singleton
#Component(modules = ServerModule.class)
public interface ServerComponent {
void inject(TestInject inject);
}
The class ServerModule
#Module
public class ServerModule {
#Provides
#Named("audio")
#Singleton
public Server provideAudioServer() {
return new AudioServer();
}
#Provides
#Named("video")
#Singleton
public Server provideVideoServer() {
return new VideoServer();
}
}
Even with your question update, your module should be what I propose abose otherwise it won't compile for the same reason described previously.

Spring: how to get bean from application context which implements generic interface?

I have interface:
public interface CommandHandler<T extends Command> {
void handle(T command);
}
There are commands which implement Command marker interface
public class CreateCategoryCommand implements Command {
}
public class CreateCategoryCommand implements Command {
}
For each command I have apropriate CommandHandler implementations:
#Component
public class CreateProductCommandHandler implements CommandHandler<CreateProductCommand> {
#Override
public void handle(CreateProductCommand command) {
System.out.println("Command handled");
}
}
#Component
public class CreateCategoryCommandHandler implements CommandHandler<CreateCategoryCommand> {
#Override
public void handle(CreateCategoryCommand command) {
}
}
Question:
I have command bus
#Component
public class SimpleCommandBus implements CommandBus {
#Autowired
private ApplicationContext context;
#Override
public void send(Command command) {
// OF COURSE, THIS NOT COMPILED, BUT I NEED SOMETHING LIKE THIS
CommandHandler commandHandler = context.getBean(CommandHandler<command.getClass()>)
}
}
How to get bean from application context which implements generic interface with particular type?
Way I solved it:
#Component
public class SimpleCommandBus {
private final Logger logger;
private final Set<CommandHandler<?>> handlers;
private final Map<Class<?>, CommandHandler<?>> commandHandlersCache = new WeakHashMap<>();
public SimpleCommandBus(Logger logger, Set<CommandHandler<?>> handlers) {
this.logger = logger;
this.handlers = handlers;
}
public void send(Command command) {
CommandHandler<Command> commandHandler = getCommandHandler(command);
if (commandHandler != null)
commandHandler.handle(command);
else
logger.error("Can't handle command " + command);
}
#SuppressWarnings("unchecked")
private <C extends Command> CommandHandler<C> getCommandHandler(C command) {
Class<?> commandType = command.getClass();
if (commandHandlersCache.containsKey(commandType))
return (CommandHandler<C>) commandHandlersCache.get(commandType);
for (CommandHandler<?> haandler : handlers) {
Class<?> supportedCommandType = resolveTypeArgument(haandler.getClass(), CommandHandler.class);
if (commandType.isAssignableFrom(supportedCommandType)) {
commandHandlersCache.put(commandType, haandler);
return (CommandHandler<C>) haandler;
}
}
commandHandlersCache.put(commandType, null);
return null;
}
}

Spring #Async method inside a Service

I have this service bean with a sync method calling the internal async method:
#Service
public class MyService {
public worker() {
asyncJob();
}
#Async
void asyncJob() {
...
}
}
The trouble is that the asyncJob is not really called in async way.
I found that this doesn't work because an internal call skips the AOP proxy.
So I try to self-refer the bean:
#Service
public class MyService {
MyService mySelf;
#Autowired
ApplicationContext cnt;
#PostConstruct
public void init() {
mySelf=(MyService)cnt.getBean("myService");
}
public void worker() {
mySelf.asyncJob();
}
#Async
void asyncJob() {
...
}
}
It fails. Again no async call.
So I tried to divide it in two beans:
#Service
public class MyService {
#Autowired
MyAsyncService myAsyncService;
public void worker() {
myAsyncService.asyncJob();
}
}
#Service
public class MyAsyncService {
#Async
void asyncJob() {
...
}
}
Fails again.
The only working way is to call it from a Controller Bean:
#Controller
public class MyController {
#Autowired
MyAsyncService myAsyncService;
#RequestMapping("/test")
public void worker() {
myAsyncService.asyncJob();
}
}
#Service
public class MyAsyncService {
#Async
public void asyncJob() {
...
}
}
But in this case it is a service job. Why I cannot call it from a service?
Found a really nice way to solve this (with java8) in the case where you have a lot of various things you want to both sync and async. Instead of creating a separate XXXAsync service for each 'synchronous' service, create a generic async service wrapper:
#Service
public class AsyncService {
#Async
public void run(final Runnable runnable) {
runnable.run();
}
}
and then use it as such:
#Service
public class MyService {
#Autowired
private AsyncService asyncService;
public void refreshAsync() {
asyncService.run(this::refresh);
}
public void refresh() {
// my business logic
}
public void refreshWithParamsAsync(String param1, Integer param2) {
asyncService.run(() -> this.refreshWithParams(param1, param2));
}
public void refreshWithParams(String param1, Integer param2) {
// my business logic with parameters
}
}
I solved the third method (divide it in two beans) changing the async method's access modifier to public:
#Service
public class MyService {
#Autowired
MyAsyncService myAsyncService;
public void worker() {
myAsyncService.asyncJob();
}
}
#Service
public class MyAsyncService {
#Async
public void asyncJob() { // switched to public
...
}
}
In my case, it was easier to remove the #Async annotation and use the taskExecutor directly to submit my task:
Before
#Async("taskExecutor")
private Future<U> executerEnAsync(
final T pInput) {
final U resultat = this.appelerBS(pInput);
return new AsyncResult<U>(resultat);
}
After
#Autowired
private AsyncTaskExecutor taskExecutor;
private Future<U> executerEnAsync(
final T pInput) {
final Future<U> future = taskExecutor.submit(new Callable<U>() {
#Override
public U call() {
final U resultat = appelerBS(pInput);
return resultat;
}
});
return future;
}

Categories