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.
Related
I have a class named MyConfig.java.
import javax.validation.constraints.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
#Configuration
public class MyConfig {
#NotNull
#Value("${test.key}")
public String queryIndex;
}
My question is, how is it getting validated on the StartUp of Spring Boot application without any #Validated or #Valid. I Understand #Configuration classes are picked up by Spring container on Startup (to generate bean definitions if any) and javax.validator is on classpath.
The below code snippet makes sense because added #Valid annotation on POJO class :
#PostMapping
public String test(#Valid #RequestBody Employee emp) {
return "";
}
From Spring Doc:-
Link Here for reference
3.8.1. Overview of the JSR-303 Bean Validation API
JSR-303 standardizes validation constraint declaration and metadata for the Java platform. Using this API, you annotate domain model properties with declarative validation constraints and the runtime enforces them.
Here is the solution:-
MyConfig.java
import javax.validation.constraints.NotBlank;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
#Configuration
#Validated
#ConfigurationProperties(prefix="test")
public class MyConfig {
private Logger logger = LoggerFactory.getLogger(MyConfig.class);
private static final String MYNAME = MyConfig.class.getSimpleName();
#NotBlank(message = "Key should not be empty")
#Value("${test.key}")
public String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
}
application.properties
server.port=5555
test.key=
test.name=
This dependency is a must in pom.xml
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
So, #Validated, #ConfigurationProperties combined with javax.validation constraints are working behind the scenes.
Error Description:-
Binding to target org.springframework.boot.context.properties.bind.BindException:
Failed to bind properties under 'test' to com.cgi.demo.MyConfig$$EnhancerBySpringCGLIB$$3dcd81ec failed:
Property: test.test
Value:
Reason: Key should not be empty
For your reference from SprinBoot Doc's:-
Click Here for more info
2.7.9. #ConfigurationProperties Validation
Spring Boot attempts to validate #ConfigurationProperties classes whenever they are annotated with Spring’s #Validated annotation. You can use JSR-303 javax.validation constraint annotations directly on your configuration class. To do so, ensure that a compliant JSR-303 implementation is on your classpath and then add constraint annotations to your fields.
I have a new springboot application I am attempting to get started.
The error I receive is
org.springframework.context.ApplicationContextException: Unable to start reactive web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ReactiveWebApplicationContext due to missing ReactiveWebServerFactory bean.
at org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext.onRefresh(ReactiveWebServerApplicationContext.java:76) ~[spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE]
src/main/java/bubbleshadow/RootController.java
package bubbleshadow;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
#RestController
public class RootController {
public RootController() {
}
#GetMapping("/")
public Mono<HttpStatus> returnOk() {
return Mono.just(HttpStatus.OK);
}
}
src/test/java/test/bubbleshadow/RootControllerTest.java
package test.bubbleshadow;
import bubbleshadow.RootController;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
// import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes=RootController.class, webEnvironment = WebEnvironment.RANDOM_PORT)
#AutoConfigureWebTestClient
public class RootControllerTest {
#Autowired
WebTestClient webTestClient;
#Test
public void baseRouteShouldReturnStatusOK() {
webTestClient.head().uri("/").exchange().expectStatus().isOk();
}
}
Your configuration is not sufficient for reactive tests.
The reactive WebTestClient as well as ReactiveWebApplicationContext need reactive server in the application context. Add annotation #EnableAutoConfiguration to your RootControllerTest and let Spring's do it for you.
The autoconfiguration searches your class path and after find reactive classes and reactive context then create ReactiveWebServerFactory bean.
I assume you are using maven to get your dependencies.
I solved the problem by using:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
Instead of:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
For me, the error was being caused by a missing #SpringBootApplication annotation on the Spring class containing the main() method entry point which actually starts the Boot application. Using the following resolved the error:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Likely a corrupt download. Try removing ~/.m2/repository.
You actually just need to change webEnvironment = WebEnvironment.RANDOM_PORT to webEnvironment = WebEnvironment.MOCK in your #SpringBootTest annotation.
#vdou's answer helped me to resolve my issue.
In addition to adding #EnableAutoConfiguration, I also had to manually add the spring application type:
spring:
main:
web-application-type: reactive
There is obviously something in my dependencies that is causing Spring not to be able to discover the type.
I hope this helps somebody...
If you are using Kotlin, check if in your Application class that contains the main method, doesnt have this:
runApplication<Application>{
webApplicationType = WebApplicationType.REACTIVE
}
Then change the "REACTIVE" to "SERVELET", will work like a charm.
If None of the above solutions work, try adding
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
It may help you
import org.springframework.test.context.support.AnnotationConfigContextLoader;
Yet another reason this can occur is if you're importing in a configuration class for your test that is not marked with #TestConfiguration annotation
I am configuring a spring boot application. So I created a property bean class like this:
import javax.validation.constraints.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
#Configuration
#EnableConfigurationProperties
#PropertySource("classpath:/application.properties")
public class APPStaticProperties {
#NotNull
#Value("${dblog.system.name}")
String db_logsystem_name;
/**
* #return the db_logsystem_name
*/
public String getDb_logsystem_name() {
return db_logsystem_name;
}
/**
* #param db_logsystem_name the db_logsystem_name to set
*/
public void setDb_logsystem_name(String db_logsystem_name) {
this.db_logsystem_name = db_logsystem_name;
}
}
And I am creating a object by in the controller class:
#Autowired
APPStaticProperties appStaticProperties;
But I wanted to know how do I pass the this object for use in other projects? Because my code flow goes from the controller to a JSP and then to a class in another project. I need the object available for use in that class. Well to be honest, in many other projects as well! I am not able to figure it out how to do it. Neither are there too many examples out there for reference.
You wouldn't normally inject an #Configuration bean into other Spring managed beans, because it sets up configuration to be used by other Spring managed beans.
For example, because you have declared an #PropertySource, to access your properties in other Spring managed means you can just inject properties into your code elsewhere using #Value. You don't need to inject your APPStaticProperties bean to do this.
I'm learning Spring as it seems to be a very powerful framework. I've already did many getting started guides and now I'm trying with this tutorial. In it, all classes are put in the same package, but to make it more interesting I tried using different packages according to the class (entity, controller, etc.). I was about to test it before the Testing a REST Service section but got an error building the application. This is how my project is structured:
The only difference with the classes in the tutorial is the marked ServletInitializer which comes with the initializr utility (actually I used the one that comes with STS, but it's the same). As far as I understand, it has nothing to do with the problem so the content of this class is irrelevant.
Another minor difference with the tutorial is that the Application class here is called RestServicesApplication but the content is the same.
When I try to build the application (using Gradle's bootRun instead of Maven) I got the following error message:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method init in com.example.restservices.RestServicesApplication required a bean of type 'com.example.repository.AccountRepository' that could not be found.
Action:
Consider defining a bean of type 'com.example.repository.AccountRepository' in your configuration.
:bootRun FAILED
So I tried to annotate AccountRepository with #Bean but it gives me a compilation error saying that the annotation is disallowed for that location. Next I tried with the #Component annotation (also on BookmarkRepository) and adding #ComponentScan("com.example") in RestServicesApplication. After that the error remains but the message changed to
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.example.controller.BookmarkRestController required a bean of type 'com.example.repository.BookmarkRepository' that could not be found.
Action:
Consider defining a bean of type 'com.example.repository.BookmarkRepository' in your configuration.
:bootRun FAILED
I added #Component annotation to BookmarkRestController but the same error message remains. What am I missing here?
Thanks in advance for your answers.
Edit #1
The classes involved in the problem are the following (copied from my project, not the ones in the tutorial, although the differences are minimal):
RestServicesApplication
package com.example.restservices;
import java.util.Arrays;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import com.example.entity.Account;
import com.example.entity.Bookmark;
import com.example.repository.AccountRepository;
import com.example.repository.BookmarkRepository;
#SpringBootApplication
#ComponentScan("com.example")
public class RestServicesApplication {
public static void main(final String[] args) {
SpringApplication.run(RestServicesApplication.class, args);
}
#Bean
CommandLineRunner init(final AccountRepository accountRepository,
final BookmarkRepository bookmarkRepository) {
return (evt) -> Arrays.asList(
"jhoeller,dsyer,pwebb,ogierke,rwinch,mfisher,mpollack,jlong".split(","))
.forEach(
a -> {
final Account account = accountRepository.save(new Account(a,
"password"));
bookmarkRepository.save(new Bookmark(account,
"http://bookmark.com/1/" + a, "A description"));
bookmarkRepository.save(new Bookmark(account,
"http://bookmark.com/2/" + a, "A description"));
});
}
}
BookmarkRestController
package com.example.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.repository.AccountRepository;
import com.example.repository.BookmarkRepository;
#RestController
#RequestMapping("/{userId}/bookmarks")
public class BookmarkRestController {
private final BookmarkRepository bookmarkRepository;
private final AccountRepository accountRepository;
#Autowired
public BookmarkRestController(final BookmarkRepository bookmarkRepository,
final AccountRepository accountRepository) {
this.bookmarkRepository = bookmarkRepository;
this.accountRepository = accountRepository;
}
// #RequestMapping methods...
}
AccountRepository
package com.example.repository;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;
import com.example.entity.Account;
#Component
public interface AccountRepository extends JpaRepository<Account, Long> {
Optional<Account> findByUsername(String username);
}
BookmarkRepository
package com.example.repository;
import java.util.Collection;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;
import com.example.entity.Bookmark;
#Component
public interface BookmarkRepository extends JpaRepository<Bookmark, Long> {
Collection<Bookmark> findByAccountUsername(String username);
}
Note: I added the imports so you can see where the classes and annotations come from
Edit #2
I tried another thing: I refactored my Project to fit the tutorial and I put everithing in the same package (com.example.bookmarks) and removed the extra annotations. The Project compiles but when I run the Project I get a 404 HTTP status when trying to accesss a REST service. I'm still interested in make it run with my original structure but I want to let you know that this refactoring makes the Project work.
To have Spring create a bean that implements JpaRepository interface, you need to use Spring JPA namespace and activate the repository support using the appropriate element. In xml:
<jpa:repositories base-package="com.example.repository" />
In annotation:
#EnableJpaRepositories
See this docs
This scans all packages below com.example.repository for interfaces extending JpaRepository and creates a Spring bean for it that is backed by an implementation of SimpleJpaRepository.
I think you have to create package again. You packing looking not right. Recreate your package
I am using Jax-RS 2.0 with Jersey 2.22.1 and Java SE 8, deployed in Tomcat 8.0.30.
I have the POJO annotated with the appropriate JAX-RS annotations, and its working as expected. I have also annotated the POJO with #Singleton. The class gets lazily instantiated as a singleton, which is the expected behavior of #Singleton. But I would like to instantiated the class eagerly, at application startup. Is there a way to do that? I have looked at the #Startup annotation but unfortunately, that is part of the EJB package and I am not using EJB's (nor would I like to import the EJB jar file).
I am also using the Springframework 4.2.4 which would by default eagerly instantiate singletons when using the #Service or #Component annotations but unfortunately, I cannot use those annotations on a JAX-RS POJO (this is why the class extends from SpringBeanAutowiringSupport.
I have attached the java code but since its working as expected I'm not sure that would add anything useful.
import javax.inject.Singleton;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.GET;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.core.MediaType;
import org.xxx.profile.util.GeneralUtil;
import org.xxx.profile.util.WebServiceLogger;
import org.xxx.profile.util.datatransfer.AccountDTO;
import org.xxx.profile.web.exception.RestException;
import org.xxx.profile.web.exception.FailureResponse;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;
#Singleton
#Path( "account" )
public class AccountWebService extends SpringBeanAutowiringSupport{
#Autowired
private AccountLogic accountLogic;
#Autowired
protected SimpleParser simplerParser;
#GET
#Path("get/{id: [a-zA-Z][a-zA-Z_0-9]*}")
#Produces( MediaType.APPLICATION_JSON )
#Consumes( MediaType.APPLICATION_JSON )
public AccountDTO retrieveAccount(#PathParam("id") String customerId) throws RestException {
try {
return accountLogic.retrieveAccount(customerId);
}
catch(Exception e){
String transactionID = GeneralUtil.getUniqueID();
WebServiceLogger.severe( transactionID, "Unable to retrieve an account for the customerId: " + customerId, e, this.getClass() );
throw new RestException( new FailureResponse( FailureResponse.Status.FAIL, "add user friendly message here", transactionID ), e );
}
}
}
See Jersey 2.22.1 User Guide:
If you want to use Jersey Spring DI support you will need to add the jersey-spring3 module into the list of your dependencies:
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring3</artifactId>
<version>2.22.1</version>
</dependency>
It seems to work for Spring 4, too. See Question 21443088