11

I'm working on a data access library and would like to be able to include it as a dependency in other projects with minimal configuration (ideally just autowire a repo). In particular, this library sets itself up using an autoconfiguration class (enabled in spring.factories) and needs to disable other autoconfiguration classes to work (DataSourceAutoConfiguration and HibernateJpaAutoConfiguration).

Is it possible to do this outside of the dependent project?

To make configuration as simple as possible I'd like to avoid putting excludes in the dependent project's @SpringBootApplication annotation or its spring.autoconfigure.exclude property.

Update:

On my @Configuration I have tried adding the annotations:

@EnableAutoConfiguration(exclude={
    DataSourceAutoConfiguration.class,
    HibernateJpaAutoConfiguration.class})

this causes

IllegalStateException: Configuration problem: A circular @Import has been detected

and

@ImportAutoConfiguration(exclude={
    DataSourceAutoConfiguration.class, 
    HibernateJpaAutoConfiguration.class})

Which simply does nothing.

yunandtidus
  • 3,847
  • 3
  • 29
  • 42
Steve
  • 939
  • 1
  • 6
  • 20
  • can't you use `@AutoConfigureBefore(...)`? Your custom autoconfiguration probably provides beans like datasource and so... Spring boot autoconfig won't interfere afterwards. – Dirk Deyne May 23 '18 at 18:26
  • I really need to disable them, the purpose is to be able to create a project and include multiple of these data access libraries as dependencies. I'll get unsatisfied dependency issues if there are multiple beans of the same type on the classpath. Yes, I could use primary on one of the libraries but there's no guarantee I'll be using that library in each project. I've successfully set it up where each library has a "primary" config and a "secondary" config (for when a datasource already exists) but the code duplication is nasty. – Steve May 23 '18 at 18:40

2 Answers2

34

There's a very handy interface called AutoConfigurationImportFilter, which decides on which auto-configuration should be loaded. This is also how @ConditionalOnClass annotation works.

In your library, add the following class:

public class MyExclusionFilter
        implements AutoConfigurationImportFilter {

    private static final Set<String> SHOULD_SKIP = new HashSet<>(
            Arrays.asList("org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration",
                    "org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration"));

    @Override
    public boolean[] match(String[] classNames, AutoConfigurationMetadata metadata) {
        boolean[] matches = new boolean[classNames.length];

        for(int i = 0; i< classNames.length; i++) {
            matches[i] = !SHOULD_SKIP.contains(classNames[i]);
        }
        return matches;
    }
}

This class needs to be registered in spring.factories to work. Add the following line in the library into META-INF/spring.factories:

org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=com.mycompany.db.MyExclusionFilter

You don't need to make any changes to the dependent project. Just add the library as a dependency and the auto-configuration you specified won't be loaded for the whole application.

NOTE: You can add more than one import filter, only the auto-configuration classes not filtered in all import filters will get loaded.

For details, see the source code of org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#filter and org.springframework.boot.autoconfigure.condition.OnClassCondition.java classes.

MuratOzkan
  • 2,599
  • 13
  • 25
  • Thanks, this solved the problem! The only issue is that it breaks `@DataJpaTest` test classes, for me using `@SpringBootTest` instead is an option since my libraries are fairly small. Do you have suggestions for getting `@DataJpaTest` to work when `@SpringBootTest` isn't an option / is too slow? – Steve May 29 '18 at 18:25
  • Unfortunately, the only thing comes to my mind involves creating a Test configuration which defined the beans that `@DataJpaTest` requires. However this begs for some testing. – MuratOzkan May 31 '18 at 10:25
  • 4
    Figured it out, even with the filter you can still import the configurations. So adding `@Import({DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})` does the trick when using `@DataJpaTest`. – Steve May 31 '18 at 14:10
2

you can exclude then via

@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class}))

but you could exclude them in your @Configuration by adding @EnableAutoConfiguration(exclude= {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})

Dirk Deyne
  • 6,048
  • 1
  • 15
  • 32
  • 1
    Thanks for the response. I should've mentioned that I've tried the latter. Unfortunately, it causes an IllegalStateException with "Configuration problem: A circular @Import has been detected" – Steve May 23 '18 at 17:15
  • hmm, correct. I tested it in combination with _spring.factories_ and got same exception... – Dirk Deyne May 23 '18 at 18:13