I want to make use of spring #Autowired in a java rest project. For the last days, I am trying to set up a simple spring java project with java configuration without explicit bean configuration to check that functionality out. But I can't get it to work. I may be missing something fundamental.
None of the approaches I found in the web and on this site solved my problem so far. I couldn't find a sample for exactly what I'm trying to achieve too. This is mainly due to the amount of different spring versions and approaches spread over the web.
Here is an as easy as I could come up with Java Spring rest sample. I added a few comments with how I interpret spring annotations, as I may err here too:
App base class
package restoverflow;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("/")
public class App extends Application {
}
Config class
package restoverflow;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration //this is a configuration class and also found by spring scan
#ComponentScan //this package and its subpackages are being checked for components and its subtypes
public class AppConfig {
}
Some Pojo
package restoverflow;
public class Pojo {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
A service
package restoverflow;
import org.springframework.stereotype.Service;
#Service //this is a subtype of component and found by the componentscan
public class PojoService {
public Pojo getPojo(){
Pojo pojo = new Pojo();
pojo.setName("pojoName");
return pojo;
}
}
And finally a resource where the autowiring of the service should be done
package restoverflow;
import javax.ws.rs.GET;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path("/resource")
#Controller //this is a subtype of component and found by the componentscan
public class Resource {
#Autowired //this tells to automatically instantiate PojoService with a default contructor instance of PojoService
private PojoService pojoService;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Pojo getPojo() {
return pojoService.getPojo();
}
}
Pom:
...
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
...
I want the pojoService to be instantiated. But I get a NullPointerException.
It looks like you are using Field level injection.
Please go through below link to understand all types of injections:
https://www.vojtechruzicka.com/field-dependency-injection-considered-harmful/
Can't see any clear reason why pojoService is coming null.
Please check whether pojoService bean is being initialized properly. It might be due to pojoService bean has not been initialized and you are getting null in your controller.
A nullpointer instead of a NoSuchBeanDefinitionException is more of an indication that the Spring context is not loaded at all, rather than loaded improperly.
If you're using Spring boot, modify your main class to initialize Spring:
#SpringBootApplication
#ApplicationPath("/")
public class App extends Application {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
Or else (as the pom.xml snippet did not mention Spring boot), initialize Spring manually by initializing a ClassPathXmlApplicationContext and adding <context:component-scan base-package="restoverflow" /> in your applicationContext.xml.
I have the following test code:
package soundSystem;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class )
#ContextConfiguration(classes = CDPlayerConfig.class)
public class SonyCDPlayerTest {
#Autowired
private ICompactDisk cd;
#Test
public void cdShouldNotBeNull() {
assertNotNull(cd);
}
}
This is a maven project, the problem is the exact same code would run in eclipse, but not in intellij.
I just can't find a way to resolve #RunWith
The #RunWith annotation has been replaced with #ExtendWith in JUnit 5.0 and above (which the latest spring version is now using).
Example:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = { SpringTestConfiguration.class })
public class GreetingsSpringTest {
// ...
}
Quoted from Baeldung:
Note that SpringExtension.class is provided by Spring 5 and integrates
the Spring TestContext Framework into JUnit 5.
Ref: https://www.baeldung.com/junit-5-runwith
Simple: your IDE is not configured to for unit testing.
In other words: you are missing all the JUnit related classes. You can see that all those JUnit imports are underlined; as IntelliJ simply doesn't know about the JARs that contain the corresponding classes.
See here on how to fix that.
I'm trying to run a simple Junit test to see if my CrudRepositories are indeed working.
The error I keep getting is:
Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
java.lang.IllegalStateException
Doesn't Spring Boot configure itself?
My Test Class:
#RunWith(SpringRunner.class)
#DataJpaTest
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class JpaTest {
#Autowired
private AccountRepository repository;
#After
public void clearDb(){
repository.deleteAll();
}
#Test
public void createAccount(){
long id = 12;
Account u = new Account(id,"Tim Viz");
repository.save(u);
assertEquals(repository.findOne(id),u);
}
#Test
public void findAccountByUsername(){
long id = 12;
String username = "Tim Viz";
Account u = new Account(id,username);
repository.save(u);
assertEquals(repository.findByUsername(username),u);
}
My Spring Boot application starter:
#SpringBootApplication
#EnableJpaRepositories(basePackages = {"domain.repositories"})
#ComponentScan(basePackages = {"controllers","domain"})
#EnableWebMvc
#PropertySources(value {#PropertySource("classpath:application.properties")})
#EntityScan(basePackages={"domain"})
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
My Repository:
public interface AccountRepository extends CrudRepository<Account,Long> {
public Account findByUsername(String username);
}
}
Indeed, Spring Boot does set itself up for the most part. You can probably already get rid of a lot of the code you posted, especially in Application.
I wish you had included the package names of all your classes, or at least the ones for Application and JpaTest. The thing about #DataJpaTest and a few other annotations is that they look for a #SpringBootConfiguration annotation in the current package, and if they cannot find it there, they traverse the package hierarchy until they find it.
For example, if the fully qualified name for your test class was com.example.test.JpaTest and the one for your application was com.example.Application, then your test class would be able to find the #SpringBootApplication (and therein, the #SpringBootConfiguration).
If the application resided in a different branch of the package hierarchy, however, like com.example.application.Application, it would not find it.
Example
Consider the following Maven project:
my-test-project
+--pom.xml
+--src
+--main
+--com
+--example
+--Application.java
+--test
+--com
+--example
+--test
+--JpaTest.java
And then the following content in Application.java:
package com.example;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Followed by the contents of JpaTest.java:
package com.example.test;
#RunWith(SpringRunner.class)
#DataJpaTest
public class JpaTest {
#Test
public void testDummy() {
}
}
Everything should be working. If you create a new folder inside src/main/com/example called app, and then put your Application.java inside it (and update the package declaration inside the file), running the test will give you the following error:
java.lang.IllegalStateException: Unable to find a #SpringBootConfiguration, you need to use #ContextConfiguration or #SpringBootTest(classes=...) with your test
Configuration is attached to the application class, so the following will set up everything correctly:
#SpringBootTest(classes = Application.class)
Example from the JHipster project here.
It is worth to check if you have refactored package name of your main class annotated with #SpringBootApplication. In that case the testcase should be in an appropriate package otherwise it will be looking for it in the older package . this was the case for me.
It work fo me
the package name of the above test class is changed to the same as the package name of the normal class.
change to this
In addition to what Thomas Kåsene said, you can also add
#SpringBootTest(classes=com.package.path.class)
to the test annotation to specify where it should look for the other class if you didn't want to refactor your file hierarchy. This is what the error message hints at by saying:
Unable to find a #SpringBootConfiguration, you need to use
#ContextConfiguration or #SpringBootTest(classes=...) ...
In my case the packages were different between the Application and Test classes
package com.example.abc;
...
#SpringBootApplication
public class ProducerApplication {
and
package com.example.abc_etc;
...
#RunWith(SpringRunner.class)
#SpringBootTest
public class ProducerApplicationTest {
After making them agree the tests ran correctly.
I had the same issue and I solved by adding an empty class annotated with SpringBootApplication in the root package of the folder src/test/java
package org.enricogiurin.core;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class CoreTestConfiguration {}
The test slice provided in Spring Boot 1.4 brought feature oriented test capabilities.
For example,
#JsonTest provides a simple Jackson environment to test the json serialization and deserialization.
#WebMvcTest provides a mock web environment, it can specify the controller class for test and inject the MockMvc in the test.
#WebMvcTest(PostController.class)
public class PostControllerMvcTest{
#Inject MockMvc mockMvc;
}
#DataJpaTest will prepare an embedded database and provides basic JPA environment for the test.
#RestClientTest provides REST client environment for the test, esp the RestTemplateBuilder etc.
These annotations are not composed with SpringBootTest, they are combined with a series of AutoconfigureXXX and a #TypeExcludesFilter annotations.
Have a look at #DataJpaTest.
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Inherited
#BootstrapWith(SpringBootTestContextBootstrapper.class)
#OverrideAutoConfiguration(enabled = false)
#TypeExcludeFilters(DataJpaTypeExcludeFilter.class)
#Transactional
#AutoConfigureCache
#AutoConfigureDataJpa
#AutoConfigureTestDatabase
#AutoConfigureTestEntityManager
#ImportAutoConfiguration
public #interface DataJpaTest {}
You can add your #AutoconfigureXXX annotation to override the default config.
#AutoConfigureTestDatabase(replace=NONE)
#DataJpaTest
public class TestClass{
}
Let's have a look at your problem,
Do not mix #DataJpaTest and #SpringBootTest, as said above #DataJpaTest will build the configuration in its own way(eg. by default, it will try to prepare an embedded H2 instead) from the application configuration inheritance. #DataJpaTest is designated for test slice.
If you want to customize the configuration of #DataJpaTest, please read this official blog entry from Spring.io for this topic,(a little tedious).
Split the configurations in Application into smaller configurations by features, such as WebConfig, DataJpaConfig etc. A full featured configuration(mixed web, data, security etc) also caused your test slice based tests to be failed. Check the test samples in my sample.
In my case
Make sure your (test package name) of YourApplicationTests is equivalent to the (main package name).
When all the classes were in same package, test classes were working. As soon as I moved all the java classes to different package to maintain proper project structure I was getting the same error.
I solved it by providing my main class name in the test class like below.
#SpringBootTest(classes=JunitBasicsApplication.class)
I think that the best solution for this issue is to align your tests folders structure with the application folder structure.
I had the same issue which was caused by duplicating my project from a different folder structure project.
if your test project and your application project will have the same structure you will not be required to add any special annotations to your tests classes and everything will work as is.
Make sure the test class is in a sub-package of your main spring boot class
This is more the the error itself, not answering the original question:
We were migrating from java 8 to java 11. Application compiled successfully, but the errors Unable to find a #SpringBootConfiguration started to appear in the integration tests when ran from command line using maven (from IntelliJ it worked).
It appeared that maven-failsafe-plugin stopped seeing the classes on classpath, we fixed that by telling failsafe plugin to include the classes manually:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<additionalClasspathElements>
<additionalClasspathElement>${basedir}/target/classes</additionalClasspathElement>
</additionalClasspathElements>
</configuration>
...
</plugin>
In my case I was using the Test class from wrong package.
when I replaced import org.junit.Test; with import org.junit.jupiter.api.Test; it worked.
None of the suggested solutions worked because I don't have #SpringBootApplication in the system. But I figured it out - this is how I fixed:
// AppConfig.java
#Configuration
#ComponentScan("foo")
#ConfigurationPropertiesScan
public class AppConfig {
// ...
}
// HelloWorld.java
#Service
public class HelloWorld {
public void foo() {
// ...
}
}
// HelloWorldTest.java
#SpringBootTest(classes = { AppConfig.class, HelloWorld.class })
public class HelloWorldTest {
#Autowired private HelloWorld helloWorld;
#Test
public testFoo() {
this.helloWorld.foo(); // Testing logic
}
}
The key is including both AppConfig.class and HelloWorld.class in #SpringBootTest.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#DataJpaTest
#SpringBootTest
#AutoConfigureWebMvc
public class RepoTest {
#Autowired
private ThingShiftDetailsRepository thingShiftDetailsRepo;
#Test
public void findThingShiftDetails() {
ShiftDetails details = new ShiftDetails();
details.setThingId(1);
thingShiftDetailsRepo.save(details);
ShiftDetails dbDetails = thingShiftDetailsRepo.findByThingId(1);
System.out.println(dbDetails);
}
}
Above annotations worked well for me. I am using spring boot with JPA.
I have the following class:
package hello;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
#Component
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
If I reference it in my main camel route, like so:
package com.example.integration;
import hello.*;
import org.apache.camel.ProducerTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestCamelSpring {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("camelspring.xml");
ProducerTemplate camelTemplate = context.getBean("camelTemplate", ProducerTemplate.class);
Application.main(args);
System.out.println("Message Sending started");
camelTemplate.sendBody("jms:queue:testQSource","Sample Message");
System.out.println("Message sent");
}
}
Do my annotations in Application.class still get accessed even though I only reference Application.main?
I ask because the #EnableAutoConfiguration is supposed to configure the application for Tomcat, but now that I am not running Application.class directly, the application is defaulting to jetty and then I get an error that WebSockets are only supported in Tomcat.
Has anyone had this issue before or know how to solve it?
Here is the stack trace. I can see from the console log that it never starts the Tomcat instance that it does when the whole class is accessed in the example. It seems to be continuing as if it is a jetty app rather than Tomcat. Please correct me if any of these assumptions are wrong:
Caused by: java.lang.IllegalStateException: Websockets are currently only supported in Tomcat (found class org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory).
at org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration$1.customize(WebSocketAutoConfiguration.java:74)
at org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor.postProcessBeforeInitialization(EmbeddedServletContainerCustomizerBeanPostProcessor.java:67)
at org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor.postProcessBeforeInitialization(EmbeddedServletContainerCustomizerBeanPostProcessor.java:54)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:407)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1545)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
... 16 more
First of all, annotations cannot be "called".
Annotations are data, not code. In your case Spring Boot reads your annotations when you call SpringApplication.run(Application.class, args); and performs necessary configurations, therefore it doesn't matter how you call Application.main().
I guess your problem is caused by the fact that you have Jetty in the classpath, and it forces Spring Boot to use Jetty rather than Tomcat as embedded servlet container.
So, try to do the following:
Find out how Jetty appeared in your classpath
Use mvn dependency:tree -Dverbose if you use Maven
If you don't need Jetty in the classpath, exclude it from dependencies
Otherwise, you need to force Spring Boot to ignore presence of Jetty
Something like exclude = EmbeddedServletContainerAutoConfiguration.EmbeddedJetty.class in #EnableAutoConfiguration may help
We are using Spring Framework (XML Version) 4.0.5.RELAESE in our Java project.
In our application the context.xml is instantiated at the begin of the application start, and provides every properties via dependecy injection.
Now I am wondering what is the best (and commonly used) strategy on where to instantiate it in the test environment. (We have some Unit, Integration and System tests who at least need the databaseBaseConnector bean provided in the context.xml,)
I thought about making an abstract class which every test extends from, but in this case it would be newly instantiated before every test. I would like to a have a solution similiar to the main application, where the context is only instantiated one time, and everything else needed is set via dependency injection.
Researching this topic didn`t help much yet, so I decided to ask the question.
Spring comes with an SpringJUnit4ClassRunner and a #ContextConfiguration -annotation. If you use them, then spring will reuse the same Spring Context in different tests.
So a Spring test class can look like this:
package com.queomedia;
import javax.annotation.Resource;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
#RunWith(SpringJUnit4ClassRunner.class)
//#Transactional
#ContextConfiguration(SpringTestContext.APPLICATION)
public class SpringContextTest {
#Autowire
private ApplicationContext applicationContext;
//Test that the spring context can been loaded
#Test
public void testSpringContextLoad() {
Assert.assertNotNull("application context expected", this.applicationContext);
}
}