Home > other >  Spring service NullPointerException jakarta webservice
Spring service NullPointerException jakarta webservice

Time:09-30

@Path("/abc")
@Produces(MediaType.APPLICATION_JSON)
public class ABCController
{
   @Autowired
   private MyService myService;

   @GET
   public Response getSome(@QueryParam("identification") List<String> ids)
   {
      GenericEntity<List<Label>> entity = new GenericEntity<>(myService.findSomething(ids))
      {
      };
      return Response.ok(entity).build();
   }

MyService:

@RequiredArgsConstructor
@Service
public class MyService
{
   private final MyMapper myMapper;

   private final JpaRepository myRepository;

   public List<String> findSomething(ids){
         myRepository.findXYZ(ids);
   }

My MainClass:

@SpringBootApplication
public class MainClass
{
   public static void main(String[] args)
   {
      SpringApplication.run(MainClass.class, args);
   }

   @Bean
   public MyMapper myMapper()
   {
      return new MyMapperImpl();
   }
}

build.gradle:

buildscript {
    ext {
        LOMBOK_VERSION = "1.18.22"
        SPRING_BOOT_VERSION = "2.6.6"
        JERSEY_VERSION = "3.0.8"
        JACKSON_VERSION = "2.13.1"
        MSSQL_JDBC_VERSION = "9.4.1.jre11"
        MAPSTRUCT = "1.5.1.Final"
    }
}

plugins {
    id "org.gretty" version "4.0.3"
}

apply plugin: 'java-library'
apply plugin: 'java'
apply plugin: 'war'

repositories {
    mavenLocal()
    maven {
        url = '...'
    }
}


dependencies {
    compileOnly "org.projectlombok:lombok:${LOMBOK_VERSION}"
    testImplementation "org.projectlombok:lombok:${LOMBOK_VERSION}"
    annotationProcessor "org.projectlombok:lombok:${LOMBOK_VERSION}"

    implementation "org.mapstruct:mapstruct:${MAPSTRUCT}"
    annotationProcessor "org.mapstruct:mapstruct-processor:${MAPSTRUCT}"

    implementation "org.springframework.boot:spring-boot-starter-data-jpa:${SPRING_BOOT_VERSION}"

    implementation "com.microsoft.sqlserver:mssql-jdbc:${MSSQL_JDBC_VERSION}"

    implementation "org.glassfish.jersey.containers:jersey-container-servlet-core:${JERSEY_VERSION}"
    implementation "org.glassfish.jersey.inject:jersey-hk2:${JERSEY_VERSION}"
    implementation "org.glassfish.jersey.media:jersey-media-json-jackson:${JERSEY_VERSION}"
}

When i call http://localhost:8080/myservice/abc?identification=61234561234569,71234561234568,71234561234569

i got a NullPointerException of myService.findSomething(ids)

I start the application with gradle command appRun

CodePudding user response:

Though spring brings his own "framework for REST-Service development"(), there should be no reason, why JAX-RS could not use and even .


The problem (for that particular NPE) is:

For auto-wiring: both (wired & wiring) objects need to be "spring managed (beans)"! ;) In MyService this is achieved with @Service annotation.

To fix the problem, we need to participate ABCController in spring context as well!


"Least intrusive"/simplest approach - to annotate ABCController with @Component:

...
import org.springframework.stereotype.Component;

@Component // !
@Path("/abc")
@Produces(MediaType.APPLICATION_JSON)
public class ABCController ...

This ( correct "component scan") will "lift" ABCController into spring context and enable auto-wiring (from/to) that object.


Another approach, decoupling the "implementations" from spring:

For this we need to remove all (possible) org.springframework.* imports from our "spring-independent" classes. (Of course it is not possible in very spring specific classes (spring-web(Rest)Controllers,Spring-data-repos, ...).

  • To replace @Component/Service/Repository annotations, we move the instantiations to @Bean annotated methods to our spring configuration(class(es)) (..see MyMapper bean!)
  • To replace @Autowired we'd have to "manually wire" them or use "implicit constructor auto wire" (within the mentioned @Bean methods)

So your Config(/SpringBootApp), could look like:

@SpringBootApplication
public class MainClass
{
   public static void main(String[] args)
   {
      SpringApplication.run(MainClass.class, args);
   }

   @Bean
   public MyMapper myMapper()
   {
      return new MyMapperImpl();
   }
   
   @Bean // singleton is default "scope"!!...
   public MyService myService(/*implicitly @Autowired :*/ JPARepository repo, MyMapper mapper /* ..and taken care (hopefully;) by spring*/) {
    return new MyService(mapper, repo);
   }
   // and:
   @Bean 
   public ABCController abcController(/*implicitly @Autowired :*/ MyService service /* ..and taken care (hopefully;) by spring*/) {
    ABCController bean = new ABCController();
    bean.setMyService(service);
    return bean; 
  }
}

With the according adjustments to ABCController:


@Path("/abc")
@Produces(MediaType.APPLICATION_JSON)
public class ABCController
{
   // NOT @Autowired
   private MyService myService;
   ...
   // Setter(/getter) for myService !

CodePudding user response:

The are a dependency for jersey in spring-boot: spring-boot-starter-jersey

The build.gradle looks like this:

buildscript {
    ext {
        LOMBOK_VERSION = "1.18.22"
        SPRING_BOOT_VERSION = "2.6.6"
        JERSEY_VERSION = "3.0.8"
        JACKSON_VERSION = "2.13.1"
        MSSQL_JDBC_VERSION = "9.4.1.jre11"
        MAPSTRUCT = "1.5.1.Final"
    }
}

apply plugin: 'java-library'
apply plugin: 'java'
apply plugin: 'war'

repositories {
    mavenLocal()
    maven {
       ...
    }
}


dependencies {
    compileOnly "org.projectlombok:lombok:${LOMBOK_VERSION}"
    testImplementation "org.projectlombok:lombok:${LOMBOK_VERSION}"
    annotationProcessor "org.projectlombok:lombok:${LOMBOK_VERSION}"

    implementation "org.mapstruct:mapstruct:${MAPSTRUCT}"
    annotationProcessor "org.mapstruct:mapstruct-processor:${MAPSTRUCT}"

    implementation "org.springframework.boot:spring-boot-starter-jersey:${SPRING_BOOT_VERSION}"
    implementation "org.springframework.boot:spring-boot-starter-data-jpa:${SPRING_BOOT_VERSION}"

    implementation "com.microsoft.sqlserver:mssql-jdbc:${MSSQL_JDBC_VERSION}"
}

The controller becomes the @Component

@Path("/abc")
@Produces(MediaType.APPLICATION_JSON)
@Component
public class ABCController
{
   @Autowired
   private MyService myService;

   @GET
   public Response getSome(@QueryParam("identification") List<String> ids)
   {
      GenericEntity<List<Label>> entity = new GenericEntity<>(myService.findSomething(ids))
      {
      };
      return Response.ok(entity).build();
   }

The MainClass is servlet:

@SpringBootApplication
public class MainClass extends SpringBootServletInitializer
{
   public static void main(String[] args)
   {
      new MainClass().configure(new SpringApplicationBuilder(MainClass.class)).run(args);
   }

   @Bean
   public MyMapper myMapper()
   {
      return new MyMapperImpl();
   }
}

MyService is a @Service

@RequiredArgsConstructor
@Service
public class MyService
{

I added a JerseyConfig:

@Component
public class JerseyConfig extends ResourceConfig
{
   public JerseyConfig()
   {
      register(ABCController.class);
   }
}
  • Related