spring-modulith icon indicating copy to clipboard operation
spring-modulith copied to clipboard

Can't use event features without @EnableAutoConfiguration

Open padeledicq opened this issue 2 years ago • 4 comments
trafficstars

Hi,

I try to use your modular monolithic approach for some month now, and I have an application in which I don't use @SpringBootApllication annotation (or @Modulith, I am using @Modulithic instead), so I can control what is loaded into the Spring context manually by using @ImportAutoConfiguration.

I know you generally advice to reduce the class visibility as much as possible; but in this case, I can't use any event features (with JdbcEventPublicationAutoConfiguration or even @ApplicationModulesEndpointConfiguration) because those auto configuration classes have package visibility. And I can't manually declare the needed beans because their classes aren't public neither.

Is it possible to change the visibility of auto configuration classes to public? Or do I have to change my approach?

Thank you in advance. And thank you for your work.

Regards.

padeledicq avatar Jan 04 '23 14:01 padeledicq

You're correct, the autoconfiguration classes aren't accessible from outside the packages they reside in right now. This is primarily as the configuration class arrangement has proven to change quite a bit over time, and we didn't want to commit to a certain arrangement just yet.

What's the reason that you cannot or don't want to enable @EnableAutoConfiguration? For one, I imagine that the list of configuration classes you would have to list gets pretty extensive. Also, manually activating the classes for the production application subverts the purpose of autoconfiguration because there's nothing automatic going on then anymore. Not only do you have to list everything you want to activate in addition to e.g. configuring your classpath, you'd also have to make sure you activate all upstream configuration needed.

Especially, the event publication registry JARs are deliberately designed in a way that the inclusion of them on the classpath triggers bean declarations of a certain scope. I.e. to some degree it's even considered a mistake to add, e.g. the JPA support on the classpath but not activate it. Because, why would you do the former if you don't want the latter?

I get the desire for control about which autoconfiguration is running. I just wonder whether using the AOT means introduced in Spring Framework 6 are a better way to avoid running unnecessary autoconfiguration detection at runtime?

odrotbohm avatar Jan 09 '23 09:01 odrotbohm

There are a bunch of classes to list but it is not that big when you restrict them to what you want to use. And I group them by purpose in different configuration classes. It was just a bit tedious the first time I did it. Now I am just comparing the Condition Evaluation Report with auto conf enabled when adding a new dependency, or upgrading versions.

As for why I do this, I started after really checking the Condition Evaluation Report, and I was curious how it could decrease the startup time of an app if I removed the negative matches and some positive I thought I didn't need. And to my surprise, the app started a few seconds faster. The spring-boot-autoconfigure jar does contain a lot of classes now.

Then I find convenient the fact that I can disable a module with its datasource connection, especially when I use different datasources. It happens when developping locally or because a module is not finished or because I don't run a certain module continuously. But in this case, I know I can exclude the configurations, and re-import them in each module. That's what I am doing now on a project to test spring-modulith.

Using AOT might be a good idea for docker based app. I am kind of doing it for some dependencies I use locally only, but with the spring-boot-maven-plugin repackage goal instead.

As you said, it is mainly a desire for control.

Thank you for taking time to reply.

padeledicq avatar Jan 13 '23 18:01 padeledicq

In fact, it does not work to exclude a configuration class in @SpringBootApplication, then reimport it elsewhere. I forgot I had to comment the exclusion in the main application class. So I am still interested in this

padeledicq avatar Jan 13 '23 18:01 padeledicq

I do also have slightly different usecase. I defined a bunch of custom "slices" for Testing. In some I disabled the autoconfiguration of features that take "a long time" to start. For example Flyway, Hibernate, JPA, ...

I then encountered the problem that the JpaEventPublicationAutoConfiguration was not working due to the fact that I excluded the database autoconfiguration. The error message was No qualifying bean of type 'jakarta.persistence.EntityManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

As a workaround I excluded the autoconfiguration by setting the spring.autoconfigure.exclude property. Here an example:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@SpringBootTest(properties = {
    "spring.main.banner-mode=off"
})
@AutoConfigureMockMvc
@EnableAutoConfiguration(exclude = {
    // database
    DataSourceAutoConfiguration.class, FlywayAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
    JpaRepositoriesAutoConfiguration.class, FlywayEndpointAutoConfiguration.class,
    DataSourceTransactionManagerAutoConfiguration.class, SpringDataWebAutoConfiguration.class,
    SqlInitializationAutoConfiguration.class,
    // jms
    JmsAutoConfiguration.class,
    // security
    OAuth2ClientAutoConfiguration.class, OAuth2ResourceServerAutoConfiguration.class,
    SecurityAutoConfiguration.class, ReactiveSecurityAutoConfiguration.class,
    SecurityFilterAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class,
    // kafka
    KafkaAutoConfiguration.class, KafkaMetricsAutoConfiguration.class,
    // metrics
    ObservationAutoConfiguration.class, LogbackMetricsAutoConfiguration.class, MetricsAutoConfiguration.class,
    RepositoryMetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class,
    MetricsEndpointAutoConfiguration.class, TomcatMetricsAutoConfiguration.class,
    // unused features
    ArtemisAutoConfiguration.class, NettyAutoConfiguration.class, WebSocketServletAutoConfiguration.class,
    SqlInitializationAutoConfiguration.class
})
// note: modulith autoconfiguration depends on jpa and does fail if database autoconfiguration is disabled
// furthermore it can't be disabled with the mechanism above as the classes are not public
@TestPropertySource(properties=
    {"spring.autoconfigure.exclude=org.springframework.modulith.events.jpa.JpaEventPublicationAutoConfiguration,org.springframework.modulith.runtime.autoconfigure.SpringModulithRuntimeAutoConfiguration,org.springframework.modulith.actuator.autoconfigure.ApplicationModulesEndpointConfiguration"})
public @interface ActuatorTestConfig {
    @AliasFor(annotation = SpringBootTest.class, attribute = "classes")
    Class<?>[] value() default {};
}

So there are two relevant points I see around autoconfiguration:

  • It seems that all (at least most) auto configuration of spring boot is public. Probably because that way they can be referenced in annotations
  • It would be good if the JpaEventPublicationAutoConfiguration (maybe other too) had a condition that it will only be activated if an EntityManager is available (ConditionalOnBean/ConditionalOnClass) or possibly another relevant bean.

patrickuhlmann avatar Dec 14 '23 14:12 patrickuhlmann