1

I have a use-case that I need to separate my Spring configuration into 2 completely separate applications.

Application #1 will contain DAO models with Hibernate Mappings, JPA Repositories, DTO models, and logic to convert from a DAO to a DTO.

Application #2 will contain the RESTful Controllers and Service classes that contain all the business logic.


Application #1 configuration:

// For application startup
package com.appone.pkg.one

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

// For global configuration files across numerous projects
package com.appone.pkg.two

@Configuration
public class GlobalAppConfig {

    @Bean
    MapperFacade mapperFacade() {
        MapperFactory factory = new DefaultMapperFactory.Builder().build();

        factory.classMap(Group.class, GroupDTO.class).byDefault().register();
        factory.classMap(Role.class, RoleDTO.class).byDefault().register();
        factory.classMap(User.class, UserDTO.class).byDefault().register();

        return factory.getMapperFacade();
    }
 }

// For configuration aspects important for 1 project in particular
package com.appone.pkg.three

@Configuration
@EnableJpaRepositories("com.pkg.three.jpa.repository")
public class AppTwoConfig {

    @Autowired
    private EntityManager em;
    private RepositoryFactorySupport factorySupport;

    private void initialize(){
        if(factorySupport == null){
            factorySupport = new JpaRepositoryFactory(em);
        }
    }

    @Bean
    GroupRepository groupRepository(){
        initialize();
        return factorySupport.getRepository(GroupRepository.class);
    }
    @Bean
    RoleRepository roleRepository(){
        initialize();
        return factorySupport.getRepository(RoleRepository.class);
    }
    @Bean
    UserRepository userRepository(){
        initialize();
        return factorySupport.getRepository(UserRepository.class);
    }
    @Bean
    GroupValidator groupValidator(){
        return new GroupValidator();
    }
    @Bean
    UserValidator userValidator(){
        return new UserValidator();
    }
    @Bean
    ResourceMapper resourceMapper(){
        return new ResourceMapper();
    }
}

Application #2 configuration:

package com.apptwo.pkg.one;

@SpringBootApplication
@EnableAutoConfiguration
@EntityScan(basePackageClasses = {com.appone.pkg.three.AppTwoConfig.class})
public class ApplicationTwo {
    public static void main(String[] args) {
        SpringApplication.run(ApplicationTwo.class, args);
    }
}

Application #2 data load:

package com.apptwo.pkg.two;

@Component
public class DataLoader implements ApplicationRunner {

    @Autowired
    private GroupRepository groupRepository;
    @Autowired
    private RoleRepository roleRepository;
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private ResourceMapper resourceMapper;

    /**
     * Loads initial data into the h2 database.
     */
    public void run(ApplicationArguments args) {
        // Load the initial data ... 
    }
}

This is the exception I am getting when I try to launch Application #2. Application #1 starts up fine on it's own, but I am not using the Repositories there and believe it could throw errors if I tried. Ultimately I want the logic only in Application #2 though. Do you have any ideas on what I am doing wrong?

I need to initialize all the dependencies in Application #1 configuration and then run that configuration from inside Application #2 before I run the Application #2 configuration.

Description: Field groupRepository in com.apptwo.pkg.two.DataLoader required a bean of type 'com.appone.pkg.three.jpa.repository.GroupRepository' that could not be found.

Action: Consider defining a bean of type 'com.appone.pkg.three.jpa.repository.GroupRepository' in your configuration.

ItamarG3
  • 4,092
  • 6
  • 31
  • 44
wheeleruniverse
  • 1,511
  • 2
  • 20
  • 37
  • You shouldn't include a spring boot app in another spring boot app. I doubt that app1 needs to be a spring boot app instead probably should be a regular jar file. – M. Deinum Jun 27 '17 at 05:00
  • If it's not Spring Boot will I still be able to use Spring JPA Repositories, Autowired, and all that other fun stuff? – wheeleruniverse Jun 27 '17 at 05:01
  • Why not. app1 isn't an app just a jar with dependencies. – M. Deinum Jun 27 '17 at 05:03
  • Oh, I guess I over complicated it making everything Spring Boot. I will try and convert AppOne to a standard Maven application when I get a chance and see if that fixes it. – wheeleruniverse Jun 27 '17 at 05:05
  • Thanks for the quick reply. – wheeleruniverse Jun 27 '17 at 05:06
  • If it doesn't need to be a standalone application don't make it a spring boot application. You can still use the starters etc. basically the only thing you need to do is remove the `spring-boot-maven-plugin`. – M. Deinum Jun 27 '17 at 05:12

2 Answers2

2

Thanks to a combination of people I found a solution.

Part of the help came from this post: Can't Autowire @Repository annotated interface in Spring Boot

Step #1:

Removed the Application.class from Application #1 so that it cannot be run as a stand-alone Spring Boot App.

Step #2:

I changed AppTwoConfig.java like so,

// For configuration aspects important for 1 project in particular
package com.appone.pkg.three

@Configuration
@EnableJpaRepositories("com.pkg.three.jpa.repository")
@EntityScan("com.pkg.three.jpa.model")
public class AppTwoConfig {

    @Bean
    GroupValidator groupValidator(){
        return new GroupValidator();
    }
    @Bean
    UserValidator userValidator(){
        return new UserValidator();
    }
    @Bean
    ResourceMapper resourceMapper(){
       return new ResourceMapper();
    }
}

Step #3:

I changed the Application.java inside of Application #2 that should be run as a Spring Boot app.

package com.apptwo.pkg.one;

@SpringBootApplication
@EnableAutoConfiguration
@ComponentScan(basePackageClasses = {
        com.apptwo.pkg.one.config.AppConfig.class,
        com.appone.pkg.two.GlobalAppConfig.class,
        com.appone.pkg.three.AppTwoConfig.class
})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
wheeleruniverse
  • 1,511
  • 2
  • 20
  • 37
0

The component Scan only Scans subpackages of com.apptwo.pkg.one. You have to configure the right package in @SpringBootApplication

@SpringBootApplication(basePackages ={"com.appone", "com.apptwo"})
Jens
  • 67,715
  • 15
  • 98
  • 113