Spring cloud streams could not autowire Source.class - spring

I am learning Spring Cloud Streams from scratch.
I tried to create a Source application like this:
import org.springframework.cloud.stream.messaging.Source; //etc
#RestController
#SpringBootApplication
#CrossOrigin
#EnableBinding(Source.class)
public class StreamsProducerApplication {
#Autowired
Source source;
#GetMapping(value="/send/{message}")
public void sendMessage(#PathVariable String message){
if(message != null){
source.output().send(MessageBuilder.withPayload(message).build());}
}
public static void main(String[] args) {
SpringApplication.run(StreamsProducerApplication.class, args);
}
}
However, I get error hint from Intellij IDEA at "Source source;" saying "Could not autowire. No beans of 'Source' type found.
I can understand that Source is a interface from where I import, but the spring official website says "Spring Cloud Stream creates an implementation of the interface for you. You can use this in the application by autowiring it" https://docs.spring.io/spring-cloud-stream/docs/current/reference/htmlsingle/
So how did I do this wrong? Thank you.

It is just the Intellij IDEA doesn't know that #EnableBinding(Source.class) is going to be a bean at runtime. There is just on such a bean definition, so tooling fails to determine what to inject in that #Autowired.
Otherwise your code is fully good and you just need to run it and play with whatever you expect from that code.

This is just an IDE false alert.
You can suppress this error in IDE by adding
#SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")

Related

IntelliJ cannot autowire the parameter in #ServiceActivator method if I use #EnableAutoConfiguration

With auto configuration enabled, my Spring Integration service activator gives me an error in IntelliJ: "Could not autowire. No beans of 'String' type found."
If I disable auto configuration, the error goes away.
Can I exclude a class from auto configuration to "fix" this? How do I know which?
Here's the #ServiceActivator:
#Slf4j
#MessageEndpoint
public class StringProcessor {
#ServiceActivator(inputChannel = "channel1")
public void processString(String s) {
log.info(s);
}
}
IntelliJ error message
The error can be toggled by the #EnableAutoConfiguration (#SpringBootApplication) annotation:
//#SpringBootApplication
#ComponentScan
#Configuration
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
How do I figure out if this is an IntelliJ false alarm, a Spring Integration issue, or am I holding it wrong? ;o)
That's IntelliJ IDEA false alarm and wrong assumption at the same time.
The signature and structure of that #ServiceActivator was never designed for injections.
Such a method is called at runtime when a message is appeared in that channel1. So, an endpoint activate that service method and passes a payload of message into an s argument of the method. With respective converting if necessary. The atuwiring assumption over there is a bug in the IDE.
See more about service activator in the docs: https://docs.spring.io/spring-integration/reference/html/messaging-endpoints.html#service-activator
It is a false alarm, and somebody has raised this as an issue with JetBrains
https://youtrack.jetbrains.com/issue/IDEA-264916
Would you mind clicking the thumbs up next to the title so Jetbrains knows that other people would like this issue fixed.
In the meantime you can disable the introspection at the method level with the following
#Slf4j
#MessageEndpoint
public class StringProcessor {
#SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
#ServiceActivator(inputChannel = "channel1")
public void processString(String s) {
log.info(s);
}
}
This type of warning is always a 'best guess' from IDEA, and as configs get more complicated, particular if you are refering to beans created in external jars it will happen more often.
They do not stop compilation or initial execution, but Spring itself will terminate execution if the warning is actually correct.

Can't Find A Reponsitory

I have a repository interface as
#Repository
public interface WordRepository extends ReactiveCrudRepository<Word, Long> {}
And in the #SpringApplication class, I have
#Bean
ApplicationListener<applicationReadyEvent> ready(WordRepository rep) {
...
}
to populate some data to the database. It won't be compiled. After the message "APPLICATION FAILED TO START", it says
Action:
Consider defining a bean of type 'com.example.reactive.wordservice.WordRepository' in your configuration.
With or without the annotation #Repository won't yield a different outcome. I change to another approach with a new class instead.
#Component
class WordDataInitializer {
private static Logger log = LoggerFactory.getLogger(WordDataInitializer.class);
private WordRepository wordRepository;
public WordDataInitializer(WordRepository wordRepository) {
this.wordRepository = wordRepository;
}
#EventListener(ApplicationReadyEvent.class)
public void initializeDB() throws URISyntaxException, IOException {
...
}
}
The outcome is still the same. I have done that many times and don't know why it doesn't work this time with Reactor. The Spring Boot is the latest version, 2.3.0 release.
What is missing?
After waking up this morning, I recognized that a dependency I added might cause the problem. I added the Spring Boot starter data JPA to get the #Entity annotation. Removing the dependency solves the problem.
The Reactive DB works differently. The Entity is one case. Also, a schema.sql file won't get picked up automatically as the JPA approach does. I need to write some code to pick up the schema file.

#EnableAspectJAutoProxy deactivate my bean definition

I setting up a new Spring App(not spring boot) in IDEA,and manual download aspectjweaver,
writing the following code to practice aop.
A root configuration class is:
#Configuration
/*#EnableAspectJAutoProxy*/
#ComponentScan
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext();
ctx.register(Main.class);
ctx.refresh();
Performance performance=ctx.getBean(WoodStock.class);
//System.out.println(ctx.getBean(Audience.class));
performance.performance();
}
}
and the project layout is:
+com.dawn.www
-Main.java
+aspect
-Audience.java
+music
-Performance.java
-WoodStock.java
I want the Audience being the aspect of WoodStock(seeing it in spring in action)
#Aspect
#Component
public class Audience {
#Before("execution(* com.dawn.www.music.Performance.performance(..))")
public void silenceCellPhones(){
System.out.println("-----------Silencing cell phones");
}
}
Performance is a simple interface which implements by WoodStock
public interface Performance {
void performance();
}
#Component
public class WoodStock implements Performance{
#Override
public void performance() {
System.out.println("WoodStock Performance start,singer singing+++++");
}
}
#ComponentScan should find theWoodStockbean which is defined in application context,however when I run it:
No qualifying bean of type 'com.dawn.www.music.WoodStock' available
but when I comment out #EnableAspectJAutoProxy, WoodStock can be fetched from
application context?that's why?
When you are using #EnableAspecjAutoProxy, spring will automatically
create proxy for all the matching beans(i.e. WoodStock via Audience aspect).
Now, Since you haven't used 'proxyTargetClass=true' on
#EnableAspectJAutoProxy, it will fall back on JDK proxy instead of
CGLIB.
JDK proxy is interface based, hence your proxy is of type
'Performance'.
Thats the reason you are getting 'No qualifying bean of type
'com.dawn.www.music.WoodStock' available' when you try to find bean
using WoodStock type
Now, after commenting out #EnableAspectJAutoProxy, WoodStock becomes a
simple bean and is accessible via ctx.getBean(..)
with 'proxyTargetClass=true', CGLIB proxy is enabled and it creates
proxy of type WoodStock
Suggestions
Use 'proxyTargetClass=true' with ctx.getBean(WoodStock.class)
or
Use 'proxyTargetClass=false' with ctx.getBean(Performance.class)
Performance performance=ctx.getBean(Performance.class);
Spring Aop only support Interface-level proxy when not using CGLIB,so don't use class,use interface.

Spring does not load data beans (#Repository) from dependency [duplicate]

I have a myapp parent pom type maven project with myapp-core and myapp-web modules. myapp-core module is added as dependency to myapp-web.
All the classes in myapp-core module reside in root package com.myapp.core and all classes in myapp-web module reside in root package com.myapp.web
The main Application.java is also in com.myapp.web package. As my core module root package is different I am including common base package "com.myapp" for ComponentScan as follows:
#Configuration
#ComponentScan(basePackages="com.myapp")
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Now the surprising thing is if I run this app using Run As -> Spring Boot App it is working fine. But if I run it as Run As -> Java Application it is failing with error saying it can't found beans defined in myapp-core module.
If I move my Application.java to com.myapp package it is working fine.
It should work even if i run it as Java Application also, right?
After enabling debug log level for spring and going through extensive logs I found that scanning for various components like JPA Repositories, JPA Entities etc are depending on the Application.java's package name.
If the JPA Repositories or Entities are not in sub packages of Application.java's package then we need to specify them explicitly as follows:
#Configuration
#ComponentScan(basePackages="com.sivalabs.jcart")
#EnableAutoConfiguration
#EnableJpaRepositories(basePackages="com.sivalabs.jcart")
#EntityScan(basePackages="com.sivalabs.jcart")
public class Application{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
With the above additional #EnableJpaRepositories, #EntityScan I am able to run it using Run As -> Java Application.
But still not sure how it is working fine when Run As -> Spring Boot App!!
Anyway I think it is better to move my Application.java to com.myapp package rather than fighting with SpringBoot!
I have the same problem. Only adding the #EnableJpaRepositories annotation can solve the issue. I tried to define basePackages in #SpringBootApplication, to no avail.
I think the package of the Application class is fed to the scanning process of JpaRepositories, but other packages defined in #SpringBootApplication are ignored.
It looks like a bug/improvement of Spring Boot.
I had a similar issue with Redis repositories that was fixed in a similar way:
#Configuration
#EnableConfigurationProperties({RedisProperties.class})
#RequiredArgsConstructor
#EnableRedisRepositories(basePackages = {"com.example.another"})
public class RedisConfig {
private final RedisConnectionFactory redisConnectionFactory;
#Bean
public RedisTemplate<?, ?> redisTemplate() {
RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
template.setConnectionFactory(redisConnectionFactory);
template.afterPropertiesSet();
return template;
}
}

why can't spring find the #Source bean channel created by spring cloud stream?

I'm trying to use Spring Cloud Stream to publish and consume Kafka messages. I've been working off of the documentation here on Accessing Bound Channels. I'm trying to use a custom name on the channel for my topic, so I have a #Qualifier when I'm trying to inject it, but spring can't find the relevant bean. It says "For each bound interface, Spring Cloud Stream will generate a bean that implements the interface", but the auto-wiring isn't working.
The error I'm getting is "Parameter 0 of constructor in com...MessagingManager required a bean of type 'org.springframework.messaging.MessageChannel' that could not be found."
I tried using #Autowired before the MessagingManager constructor like in the example, but then got a similar error in bean factory about there being 2 of them, so I took it out, and got the current error.
It's probably complicated by my trying to use a Processor.
Here are my components. I'm running it with spring boot and trying to test it with this :
#Component
public class StartupTester implements ApplicationListener<ContextRefreshedEvent> {
MessagingManager messagingManager;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
messagingManager.sendThingCreatedMessage(new ThingCreated("12345", "667788"));
}
}
#Component
public class MessagingManager {
private MessageChannel thingCreatedChannel;
public MessagingManager(#Qualifier(ThingChannelProcessor.THING_CREATED) MessageChannel output) {
thingCreatedChannel = output;
}
public void sendThingCreatedMessage(ThingCreated thingCreated) {
thingCreatedChannel.send(MessageBuilder.withPayload(thingCreated).build());
}
}
#Component
public interface ThingsChannelProcessor extends Processor {
String THING_REQUEST = "thing-request";
String THING_CREATED = "thing-created";
#Input(THING_REQUEST )
SubscribableChannel thingsRequest();
#Output(THING_CREATED )
MessageChannel thingCreated();
}
And I also have #EnableBinding(ThingsMessagingManager.class) on my main class which is annotated with #SpringBootApplication.
I could not reproduce your error. But I have a few points you could follow:
You don't need to annotate the interface with #Component
It seems that you have a typo on your #EnableBinding you should have #EnableBinding(ThingsChannelProcessor.class) not ThingsMessagingManager
You don't need to extend Processor either, that may be the reason why you got 2 beans in the first time. If you are customizing your channels, you don't need to descend from Sink/Source/Processor, look at the Barista example in the docs
Listen for an contextRefresh won't work either, as we do the binding after the context was refreshed
Actually, let me a bit more clear on 4. We create a child context, so in order to make sure that your context has fully initialized, make sure you also implement ApplicationContextAware on your Starter, and before sending the message check if the contexts are the same otherwise you will get an error if(this.context.equals(event.getApplicationContext()))

Resources