Stuying Springboot, I got myself into an infinite wormhole of errors. Here is the last one:
No qualifying bean of type 'ca.company.hello.A' available
However, what puzzles me is that I do define the bean:
#Configuration
public class Config {
#Bean
public B b() {
return new B();
}
#Bean
public A a() {
return new A();
}
}
And use it like this:
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
#Profile("client_app_profile_name")
#SpringBootApplication
public class Helloer {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Helloer.class, args);
A a = ctx.getBean(A.class);
a.speak();
}
}
Here is my file structure:
Here is class A, just in case:
#Component
public class A {
#Autowired
private B b;
#Value("Covid 19")
private String calamity;
public void speak() {
b.writeToScreen(this.calamity);
}
}
Can someone please give me a hint as to what more Springboot wants from me? ;)
P.S.
If I remove the Bean A from Config, same error persists:
#Configuration
public class Config {
#Bean
public B b() {
return new B();
}
}
I could reproduce your problem.
1 - Move your Helloer class to inside ca.company package.
Spring classpath scanning won't work if Helloer is at the same level as ca.company and you will get some error like below:
This can also happen if you are #ComponentScanning a springframework package (e.g. if you put a #ComponentScan in the default package by mistake)
Your structure should be:
- java
- ca.company
- config
- hello
Helloer
2 - With spring classpath scan working, you can remove your bean definitions from your Configuration classes, as you will get a double bean definition error.
3 - Remove the annotations you added to suppress the errors in [1].
Related
I created Spring project via Spring Initializr with project following struct:
I defined property in application.properties file :
my.prop=testvalue
I inject this value into MyClass as following :
#Component
class MyClass {
#Value("${my.prop}")
private String myProp;
public String getMyProp() {
return myProp;
}
}
ConfigBeans defined as following:
package com.example.propertiesdemo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class ConfigBeans {
#Bean
public MyClass myLitoBean() {
return new MyClass();
}
}
PropertiesdemoApplication.java :
package com.example.propertiesdemo;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
#SpringBootApplication
public class PropertiesdemoApplication {
public static void main(String[] args) {
ApplicationContext context
= new AnnotationConfigApplicationContext(
ConfigBeans.class);
MyClass myClass = context.getBean(MyClass.class);
System.out.println(myClass.getMyProp());
}
}
I am expecting that after executing line
System.out.println(myClass.getMyProp());
will be printed value of myprop defined in application.properties (i.e testvalue), but after running (via Netbeans IDE) I get output :
${my.prop}
What was missed / wromg in this code ? Thanks in advance
You are creating MyClass bean twice.
Using #component annotation
using #bean annotation in the config class (use method name lowerCamelCase i.e. in your case myClass())
Create bean only once using any one of the above.
You dont need to create an application context in the main method like this. The presented code is a kind of mixture of "traditional" spring and spring boot. So you're kind of bypassing all the goodies that spring boot offers, among which is automatic application.properties loading.
If you're using spring boot (there is a #SpringBootApplication annotation) then it will create everything by itself.
Usually it should be something like this
public static void main(String[] args) {
SpringApplication.run(PropertiesdemoApplication.class, args);
}
Right, as Navnath Adsul said, you need the bean to be created once, and also, since you are using Spring Boot, you need to raise the context using a special method
#SpringBootApplication
public class PropertiesdemoApplication implements CommandLineRunner {
// Inject Bean
#Autowired
private MyClass myClass;
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(PropertiesdemoApplication.class)
.run(args);
// or SpringApplication.run(PropertiesdemoApplication.class, args);
}
#Override
public void run(String[] args) throws Exception {
System.out.println(myClass.getMyProp());
}
}
#SpringBootApplication
public class PropertiesdemoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(PropertiesdemoApplication.class, args);
MyClass myClass = context.getBean(MyClass.class);
System.out.println(myClass.getMyProp());
}
}
I have a service which needs to create Agents on the runtime. Agents inherit from a base Agent class. I would like to use the Autowired ability of spring instead of doing my own dependency injections.
But I am running into this issue, even though I am marking the component as scope=prototype, and even #Lazy to prevent anything from happening at compile-time.
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.my.project.AgentType1 required a bean of type 'com.my.project.POJO' that could not be found.
This is the service that tries to create the agents:
#Service
public class ProjectMain {
#Autowired
ApplicationContext context;
List<IAgent> agents = new ArrayList<>();
void SetupAgents(List<POJO> agentPojos) {
for(POJO agentPojo: agentPojos) {
IAgent agent = AgentFactory.CreateAgent(agentPojo, context);
agents.add(agent);
}
}
}
This is the factory class, not marked as #Component etc. It uses the context passed to it to create the child class beans. It tries to pass the constructor argument via the getBean method.
public class AgentFactory {
public static IAgent CreateAgent(POJO agentPojo, ApplicationContext context) {
if (agentPojo.type.equals("AgentType1")) {
return context.getBean(AgentType1.class, agentPojo);
} else {
return context.getBean(AgentType2.class, agentPojo);
}
}
}
This is a custom annotation which I found is needed for inheritance scenarios.
#Target({ ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Component
#Inherited
#Lazy
#Scope("prototype")
public #interface AgentAnnotation {}
These are the base and child agent classes, which need a custom data structure called POJO to work.
#AgentAnnotation
public class BaseAgent implements IAgent {
#Autowired
Environment env;
public BaseAgent(POJO agentPojo, String someotherdata) {
}
}
public class AgentType1 extends BaseAgent {
public AgentType1(POJO agentPojo) {
super(agentPojo, "mydata1");
...
}
}
public class AgentType2 extends BaseAgent {
public AgentType2(POJO agentPojo) {
super(agentPojo, "mydata2");
...
}
}
This is the starter app.
#ComponentScan(basePackages = "com.my.project", includeFilters = #ComponentScan.Filter(AgentAnnotation.class))
#EnableScheduling
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
I also tried the configuration approach:
#Configuration
public class BaseAgentConfig {
#Bean
#Scope("prototype")
public AgentType1 agentType1(POJO agentPojo) {
return new AgentType1(agentPojo);
}
#Bean
#Scope("prototype")
public AgentType2 agentType2(POJO agentPojo) {
return new AgentType2(agentPojo);
}
}
In this case, I removed the #AgentAnnotation from the baseAgent class as we are now instantiating through this config. Also removed the ComponentScan line from the main App.
This time around, the #Autowired doesn't work. All Autowired references in the baseAgent class are null.
Please advise on the best approach to solve this error. Thanks.
Found the issue and solution.
Basically, I was expecting child classes to inherit #Component and #Scope, which it doesn't.
So essentially, I need to annotate each child class with #Component and #Scope("prototype").
The other problem was that I was expecting Autowired items in the constructor, which was too early. Adding a #PostConstruct addressed that issue.
So I ended up deleting the custom annotation and the configuration class and making the changes I just described.
I have a class Foo in package com.example.dao -
#Component
public class Foo {
public static final String nameAbc = "Abc";
public static final String nameDef = "Def";
public static List<String> getNames() {
return ImmutableList.of(nameAbc, nameDef);
}
// I created this for testing purpose.
// I was testing if maybe Spring needs an instance of class to inject.
public static Foo instance = new Foo();
}
In a Configuration BeanConfig class I wish to inject a List<Foo> and perform some operator on it -
#Configuration
public class BeanConfig {
private List<Foo> foos;
#Autowired
public void setFoos(List<Foo> foos) {this.foos = foos;}
#Bean
public Bar bar() {
// using foos in some logic here for creating Bar bean
}
}
I have also tried using #ComponentScan on BeanConfig class -
#ComponentScan(basePackages = "com.example.dao")
but class Foo is still not injected as I get an empty list. What exactly is the mistake here?
Update -
It came out to be a different issue - some beans in com.example.dao package did not had default constructors which caused #ComponentScan to throw an exception while creating their instances. Updating the constructors resolved the issue.
I used Spring Boot to test your code. Please check your configuration
2016-12-21 20:45:36.326 INFO 25224 --- [ main] com.mycompany.app.Application : Started Application in 11.997 seconds (JVM running for 12.861)
Result: [Abc, Def]
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
#Autowired
private Environment env;
private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
#PostConstruct
public void initApplication() throws IOException {
LOGGER.info("Running with Spring profile(s) : {}", env.getActiveProfiles());
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
List<String> list = Foo.getNames();
System.out.println(list);
}
}
It came out to be a different issue - some beans in com.example.dao package did not had default constructors which caused #ComponentScan to throw an exception while creating their instances. Updating the constructors resolved the issue.
If Spring bean configured with JavaConfig, it BeanDefinition can not resolve BeanClassName, and return null.
Same with xml or annotation config work well.
What's the problem? How to fix?
Example code with trouble for Spring Boot, only add imports:
interface Foo {}
class FooImpl implements Foo {}
#ComponentScan
#EnableAutoConfiguration
#Configuration
public class App implements CommandLineRunner {
public static void main(String... args) {
SpringApplication.run(App.class, args);
}
#Bean(name = "foo")
Foo getFoo() { return new FooImpl(); }
#Autowired
private ConfigurableListableBeanFactory factory;
#Override
public void run(String... args) {
BeanDefinition definition = factory.getBeanDefinition("foo");
System.out.println(definition.getBeanClassName());
}
}
I've faced the same problem while watching a Spring workshop on the YouTube, which used XML-based configuration. Of course, my solution is not production ready and looks like a hack, but it solved my problem:
BeanDefinition beanDefinition;
AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) bd;
StandardMethodMetadata factoryMethodMetadata = (StandardMethodMetadata) annotatedBeanDefinition.getFactoryMethodMetadata();
Class<?> originalClass = factoryMethodMetadata.getIntrospectedMethod().getReturnType();
Edit 1
It turns out that if you define your bean outside of configuration class using stereotype annotation, everything would work fine:
#Configuration
#ComponentScan(value = "foo.bar")
public class Config {}
and
package foo.bar.model.impl
#Component
class FooImpl implements Foo {...}
I am attempting to test my #Service and #Repository classes in my project with spring-boot-starter-test and #Autowired is not working for the classes I'm testing.
Unit test:
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(classes = HelloWorldConfiguration.class
//#SpringApplicationConfiguration(classes = HelloWorldRs.class)
//#ComponentScan(basePackages = {"com.me.sbworkshop", "com.me.sbworkshop.service"})
//#ConfigurationProperties("helloworld")
//#EnableAutoConfiguration
//#ActiveProfiles("test")
// THIS CLASS IS IN src/test/java/ AND BUILDS INTO target/test-classes
public class HelloWorldTest {
#Autowired
HelloWorldMessageService helloWorldMessageService;
public static final String EXPECTED = "je pense donc je suis-TESTING123";
#Test
public void testGetMessage() {
String result = helloWorldMessageService.getMessage();
Assert.assertEquals(EXPECTED, result);
}
}
Service:
#Service
#ConfigurationProperties("helloworld")
// THIS CLASS IS IN /src/main/java AND BUILDS INTO target/classes
public class HelloWorldMessageService {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message=message;
}
}
The commented class annotations on the unit test represent the various things I've tried to get this working. The test and the project packages are in the same package paths and the #ComponentScan works fine from my entry point (#RestController class with main method). The service #ComponentScan's and #Autowire's fine in my #RestController class in the src/main/java side, but does not in the test. I am required to add it again as a #Bean in my #Configuration class in order for #Autowired to work. The class is otherwise in scope just fine and I can reference and instantiate it just fine from the test. The problem appears to be that #ComponentScan does not appear to correctly traverse multiple entries in my test runner classpath, in this case /target/test-classes and /target/classes.
The IDE I am using is IntelliJ IDEA 13.
UPDATE - here are HelloWorldRs and its config:
#RestController
#EnableAutoConfiguration
#ComponentScan
public class HelloWorldRs {
// SPRING BOOT ENTRY POINT - main() method
public static void main(String[] args) {
SpringApplication.run(HelloWorldRs.class);
}
#Autowired
HelloWorldMessageService helloWorldMessageService;
#RequestMapping("/helloWorld")
public String helloWorld() {
return helloWorldMessageService.getMessage();
}
}
...
#Configuration
public class HelloWorldConfiguration {
#Bean
public Map<String, String> map() {
return new HashMap<>();
}
// This bean was manually added as a workaround to the #ComponentScan problem
#Bean
public HelloWorldMessageService helloWorldMessageService() {
return new HelloWorldMessageService();
}
// This bean was manually added as a workaround to the #ComponentScan problem
#Bean
public HelloWorldRs helloWorldRs() {
return new HelloWorldRs();
}
}
First, I'd recommend to use a newer #RunWith(SpringRunner.class) but that makes no difference, it is just shorter (and recommended).
Second, from the #EnableAutoConfiguration I see that you are using spring boot - which is certainly a good thing. There are some good reasons why not to use #ComponentScan directly. Can you try the following?
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(classes=YourApplication_or_other_Configuration.class)
public class HelloWorldTest {
... etc.
I don't know if this will turn out to be the solution, but don't use the default package (i.e. don't put *.java in "src/main/java" directly), and definitely don't use a #ComponentScan or #EnableAutoConfiguration in the default package. You will end up killing your application on startup as it tries to scan everything on the classpath (including all the Spring libraries).
SpringBoot 2.7.3, JUnit 5.8.2
If you want to have full control about the spring's configuration (and not rely on the hidden magic of auto configuration) I suggest to create an explicit configuration class:
#ComponentScan(basePackages = { "my.package.to.scan" })
public class MySpringTestConfig
{
// just for spring configuration annotations
}
and reference it in your test class:
#ContextConfiguration(classes = { MySpringTestConfig.class })
#ExtendWith({ SpringExtension.class })
class MySpringTest
{
...
}