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.
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());
}
}
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].
So I am trying to autowire my http object in my test class and I have tried to integrate with #SpringBootTest however my http object still remains null.
My test class looks like this.
//#RunWith(SpringRunner.class)
#SpringBootTest(classes=Http.class)
public class GetItemTests {
private static final Logger LOGGER = LoggerFactory.getLogger(GetItemTests.class);
#Autowired
private Http httpClass;
}
My SpringBootMain class looks like this
#SpringBootConfiguration
#SpringBootApplication
public class SpringBootMain implements CommandLineRunner {
#Bean
ResourceConfig resourceConfig() {
return new ResourceConfig().registerClasses(Version1Api.class,TokenUtilityClass.class, Paypal.class);
}
#Override
public void run(String... args) throws Exception {
//test.authenticationToken();
}
public static void main(String[] args) {
SpringApplication.run(SpringBootMain.class);
}
}
I have tried running with the SpringRunner as well as this but I receive errors about failing to load the application context.
Unless your Http class is annotated with #Component (meaning it is a bean maintained by the Spring IoC Container) you will not be able to #Autowire it in the way you wrote.
Please also post the exception you are getting and the implementation of your Http class so we can potentially provide further help.
Summary: Adding the #ComponentScan (or #SpringBootApplication) annotation to my application class changes the behaviour of SpringApplicationBuilder.properties() and breaks my integration test.
I am using a cut-down version of the Spring Boot sample:
spring-boot-sample-websocket-jetty
I have removed everything except what is required for the "echo" example (and I'm using Spring Boot 1.3.3).
I am left with the following SampleJettyWebSocketsApplication code:
#Configuration
#EnableAutoConfiguration
//#ComponentScan // --- If I uncomment this the test breaks ---
#EnableWebSocket
public class SampleJettyWebSocketsApplication
implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(echoWebSocketHandler(), "/echo").withSockJS();
}
#Bean
public EchoService echoService() {
return new DefaultEchoService("Did you say \"%s\"?");
}
#Bean
public WebSocketHandler echoWebSocketHandler() {
return new EchoWebSocketHandler(echoService());
}
public static void main(String[] args) {
SpringApplication.run(SampleJettyWebSocketsApplication.class, args);
}
}
And the following test class (code straight from the Spring Boot samples):
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(SampleJettyWebSocketsApplication.class)
#WebIntegrationTest({"server.port=0"})
#DirtiesContext
public class SampleWebSocketsApplicationTests {
private static Log logger = LogFactory.getLog(SampleWebSocketsApplicationTests.class);
#Value("${local.server.port}")
private int port = 1234;
#Test
public void echoEndpoint() throws Exception {
logger.info("Running the echoEndpoint test. Port: " + port + ". Path: /echo/websocket");
ConfigurableApplicationContext context = new SpringApplicationBuilder(
ClientConfiguration.class, PropertyPlaceholderAutoConfiguration.class)
.properties("websocket.uri:ws://localhost:" + this.port
+ "/echo/websocket")
.run("--spring.main.web_environment=false");
long count = context.getBean(ClientConfiguration.class).latch.getCount();
AtomicReference<String> messagePayloadReference = context
.getBean(ClientConfiguration.class).messagePayload;
context.close();
assertThat(count).isEqualTo(0);
assertThat(messagePayloadReference.get())
.isEqualTo("Did you say \"Hello world!\"?");
}
#Configuration
static class ClientConfiguration implements CommandLineRunner {
#Value("${websocket.uri}")
private String webSocketUri;
private final CountDownLatch latch = new CountDownLatch(1);
private final AtomicReference<String> messagePayload = new AtomicReference<String>();
#Override
public void run(String... args) throws Exception {
logger.info("Waiting for response: latch=" + this.latch.getCount());
if (this.latch.await(10, TimeUnit.SECONDS)) {
logger.info("Got response: " + this.messagePayload.get());
}
else {
logger.info("Response not received: latch=" + this.latch.getCount());
}
}
#Bean
public WebSocketConnectionManager wsConnectionManager() {
logger.info("Setting up SimpleClientWebSocketHandler...");
WebSocketConnectionManager manager = new WebSocketConnectionManager(client(),
handler(), this.webSocketUri);
manager.setAutoStartup(true);
return manager;
}
#Bean
public StandardWebSocketClient client() {
return new StandardWebSocketClient();
}
#Bean
public SimpleClientWebSocketHandler handler() {
logger.info("Creating new SimpleClientWebSocketHandler using SimpleGreetingService...");
return new SimpleClientWebSocketHandler(greetingService(), this.latch,
this.messagePayload);
}
#Bean
public GreetingService greetingService() {
return new SimpleGreetingService();
}
}
}
Running the Application and the unit test as above all is fine but if I uncomment the #ComponentScan annotation on the application class the application still runs OK but the test breaks with the error:
Could not resolve placeholder 'websocket.uri' in string value "${websocket.uri}".
I have read at setting-the-run-time-properties-on-springapplicationbuilder that:
The properties you configure on SpringApplicationBuilder are made available in your application's Environment, not as system properties.
And in the #ComponentScan javadoc that:
If specific packages are not defined, scanning will occur from the package of the class that declares this annotation.
But I don't understand why the behaviour changes when the #ComponentScan annotation is added.
How can I set the System Property websocket.uri in the test when the application class is annotated with #ComponentScan (or #SpringBootApplication)?
(I aim to use #SpringBootApplication, which incorporates #ComponentScan, but I can't until I get this working.)
There are several ways to add a system properties.
Solution 1:
Add arguments for Test in format of -Dabc=xyz, that will add property abc to system properties.
Solution 2:
Just like floor 0.
Solution 3:
Just let spring-boot load the properties, such as classpath:bootstrap.yml, and you can specify whatever properties in there.
The annotation #ComponentScan will enable auto scanning based on current package or ComponentScan#basePackages. Which means SampleWebSocketsApplicationTests.ClientConfiguration will be scanned cause they have same base package samples.websocket.jetty.
However, SampleWebSocketsApplicationTests.ClientConfiguration should not be parsed by SpringJUnit4ClassRunner cause we need parse it in SampleWebSocketsApplicationTests#echoEndpoint manually. It's should only be parsed by ApplicationContext created in echoEndpoint().
What's more, #SpringBootApplication equals to use #Configuration and #EnableAutoConfiguration and #ComponentScan together, so comment out #ComponentScan or #SpringBootApplication will have same effect.
My suggestion is move class SampleWebSocketsApplicationTests into package samples.websocket.jettytest(different from samples.websocket.jetty) and enable #ComponentScan or #SpringBootApplication on SampleJettyWebSocketsApplication and try again. It should work.
Adding my thoughts on this (from whatever i could gather from your code):
-Try adding the property websocket.uri in you application properties or if your project contains src/test/resources/test.properties then add it into your test.properties file.#ComponentScan should pick it up.
-Else,you could just say :
public static void main(String[] args) {
System.setProperty("websocket.uri","<your uri>");
SpringApplication.run(SampleJettyWebSocketsApplication.class, args);
}
Hope it helps.
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 {...}