1

I have a multi-Maven module Spring boot project having following structure:

parent
   |_ pom.xml
   |_ webservices
        |_ src/main/java
             |_ webservices
                  |_ WebServicesConfig.java
                  |_ WebServicesStarter.java
                  |_ GlobalPropertiesLoader.java
        |_ pom.xml
   |_ backend
        |_ src/main/java
             |_ backend
                  |_ BackendStarter.java
        |_ pom.xml
   |_ commons
        |_ src/main/java
             |_ commons
                  |_ GlobalPropertiesDAO.java
                  |_ GlobalPropertiesRepository.java
                  |_ CommonsConfig.java;
        |_ pom.xml

Both webservices, and backend are individual Spring boot applications (they generate a jar file which I use for launching them) and they depend on the commons module. So I have included commons as a dependency in webservices and backend's pom.xml.

I have few questions about starting my applications.

  1. How do I start both backend and webservices in a single JVM? (On the same port)
  2. I want to auto-wire GlobalPropertiesRepository (located within commons project) in my backend and webservices project. How do I do this? Can I auto-wire across different modules? Just importing commons doesn't work. It throws a "no bean definition error". I think this is because GlobalPropertiesRepository is not launched by the Spring container if I import it.

============= UPDATE =============

Adding my Configuration classes for the applications:

The commons application has an empty Configuration class for now since I only have my Repository class over there. Below is the empty Configuration class:

package commons;

import org.springframework.context.annotation.Configuration;

@Configuration
public class CommonsConfig {

}

And this is the GlobalPropertiesRepository:

package commons;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface GlobalPropertiesRepository extends CrudRepository<GlobalPropertiesDAO, Long>{
}

Below are the necessary classes in the webservices application:

The starter class:

package webservices;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;

@SpringBootApplication
@EnableJpaRepositories
@ComponentScan({"commons", "webservices"})
public class WebServicesStarter {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(WebServicesStarter.class, args);

        ClassPathScanningCandidateComponentProvider provider =
                new ClassPathScanningCandidateComponentProvider(true);
    }

}

The Configuration class:

package webservices;


import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import commons.CommonsConfig;

@Configuration
@Import(CommonsConfig.class)
public class WebServicesConfig {

    @Autowired CommonsConfig commonsConfig;
    public WebServicesConfig() {
    }
}

And the class where I'm trying to autowire the repository:

package webservices;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import commons.GlobalPropertiesDAO;
import commons.GlobalPropertiesRepository;

@Component
@Scope("singleton")
public class GlobalPropertiesLoader {

    @Autowired
    public GlobalPropertiesRepository globalPropertiesRepository;

    private GlobalPropertiesDAO globalProperties;

    @PostConstruct
    public void init(){
        globalProperties = globalPropertiesRepository.findOne(1L);
    }

    public GlobalPropertiesDAO getGlobalProperties(){
        return globalProperties;
    }

}

This is the error I get:

Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: public commons.GlobalPropertiesRepository webservices.GlobalPropertiesLoader.globalPropertiesRepository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [commons.GlobalPropertiesRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)

Thanks.

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
drunkenfist
  • 2,958
  • 12
  • 39
  • 73
  • If you have already included the commons module as a maven dependency in webservices module, then you do not need to run both the modules. Webservices module will have access to the common dependency. If you want to autowire the repository, first you need to import the configuration class from the common module. See [this] (http://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch04s03.html) for example – jrao77 May 14 '15 at 18:37
  • @jrao77 Thanks. But I need to launch two separate applications. I had simplified my question earlier. I have edited my question now. – drunkenfist May 14 '15 at 18:52
  • I am not sure if that can be done. You could try packaging them as War (Spring boot has support for this) instead and drop both into the same container? See this for example http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file – jrao77 May 15 '15 at 09:40
  • Ok, I'll try that. But I'm still not able to autowire the repository for another application. I tried `@import`ing the Configuration class from the other application, but I have not declared the repository in the Configuration class. So I don't think that is the way to go. My question is similar to this: http://stackoverflow.com/questions/22442822/spring-boot-and-componentscan-between-two-jars. I have done whatever is suggested in that question, but it still doesn't work. – drunkenfist May 16 '15 at 18:57
  • Can you share your `@Configuration` class for the repository set up and the backend module where you are wiring that to and its config? – jrao77 May 16 '15 at 19:56
  • I have updated my question with the necessary code. – drunkenfist May 16 '15 at 20:38
  • Thanks, Can you also add the error that you get? – jrao77 May 17 '15 at 17:50
  • I have updated the question with the error I get. – drunkenfist May 18 '15 at 07:10

1 Answers1

1

I think I know whats causing the error.

In your app starter class even though you have the @ComponentScan({"commons", "webservices"}) for some reason the @Repository bean is being ignored. I think the problem is in the class ClassPathBeanDefinitionScanner. According to the documentation JavaDoc, it does state that by default it will scan for all components, including @Repository. But when it identifies the repository interfaces it fails another check in the method isCandidateComponent. (if you look in the source)

So we get this log when you enable debug

2015-05-18 11:10:13.399 DEBUG 67917 --- [ main] o.s.c.a.ClassPathBeanDefinitionScanner : Ignored because not a concrete top-level class: file [****/jpa-test/dl/target/classes/commons/GlobalPropertiesRepository.class]

Furthermore, because EnableJpaRepositories only scans the package which it is declared in , the repository is not being initialised.

If you set the baseClassPackage (type safe) property like @EnableJpaRepositories(basePackageClasses = GlobalPropertiesRepository.class) then the repository does get initialised. Also make sure you add the @EntityScan(basePackageClasses = GlobalPropertiesDAO.class)

jrao77
  • 142
  • 5
  • Wow thx a lot. This works. I never knew we had `basePackages` with '@EnableJPARepositories'. I used `basePackages` instead of `basePackageClasses`. Also, why do I need to add `@EntityScan` ? It seems to work without it. – drunkenfist May 18 '15 at 21:40
  • @EntityScan is to scan for the JPA entities. When I was testing, it complained that the GlobalPropertiesDAO (which I had created as an Entity) was not managed. So I thought you could run into a similar issue. yeah _basePackages_ works too, but its a string value and its easy to get it wrong :) – jrao77 May 18 '15 at 21:52