Spring boot application won't run when trying to run from the jar file

You have spring-boot-starter-jersey in your pom.xml. The error you are getting is /BOOT-INF/classes (No such file or directory). This is the issue with jersey which doesn't work well with spring-boot fat jar. Please refer this and this for more details. For work around you have to package the classes that should be scanned by jersey in a jar and extract the jar as given at Spring-Boot documentation Extract specific libraries when an executable jar runs


@abaghel answer has the correct explanation, and arguably the best solution as well.

Since Jersey's scanning step is what's failing, another way to work around it is to explicitly register each resource. I replaced this:

@Component
public JerseyConfig() extends ResourceConfig {
    packages("com.mydomain.resource");
}

with

@Component
public JerseyConfig() extends ResourceConfig {
    register(TimeResource.class);
    register(SpaceResource.class);
    // etc.
}

(Tested with Spring Boot 2.1.3)

My code has about 25 Jersey of these resource classes to register. This highlights the downside to this approach, namely that the explicit registration has to be maintained. New resource classes won't be found automatically, and it can be a confusing error to run into.

On the other hand, you don't need to split your project up. So explicit registration might occasionally be good enough.


To add onto @Bampfer's answer since Googleing brought me here. This is a way to not have to unpack your resources in a separate module and have dynamic component scanning.

Create an custom annotation @JaxRsResource that you put on any resource class.

public @interface JaxRsResource {
}
@Named
@JaxRsResource
@Path("/my/api/path")
public class MyResource {
    @GET
    public String helloWorld() { return "Hello World"; }
}

I then utilized spring's component scanning api from this post: Scanning Java annotations at runtime and registered the components I found with my custom annotation and called the register function.

@Configuration
public class JaxrsApplication extends ResourceConfig {

    private final Logger logger = LoggerFactory.getLogger(JaxrsApplication.class);

    public JaxrsApplication() {

        final ClassPathScanningCandidateComponentProvider scanner =
                new ClassPathScanningCandidateComponentProvider(false);

        scanner.addIncludeFilter(new AnnotationTypeFilter(JaxRsResource.class));

        for (final BeanDefinition resourceBean : scanner.findCandidateComponents("my.base.package")) {
                try {
                    final Class resourceClass = getClass().getClassLoader().loadClass(resourceBean.getBeanClassName());
                    register(resourceClass);
                } catch (final ClassNotFoundException e) {
                    logger.error(String.format("Unable to load Jax-RS resource {}.", resourceBean.getBeanClassName()), e);
                }
            }
        }
    }
}

Note: You have to load the class from the class loader and pass the Class object to register(). If you only pass the class name as a string to the register method it won't load the resource.