Spring

Spring is an application framework and inversion of control container for Java.

These notes focus on Spring Boot, which is a distribution of Spring and related packages geared toward web application development.

Configuration

Configuration applies based on a predefined precedence.

It appears that an application.yml in the test/resources/ completely shadows one in main/resources/. As a result, you can’t depend on necessary configuration to exist in the main and only overriding certain attributes in the test one.

Due to the auto-configuration mechanism described below, this could result in classes/functionality you expect to be taking effect, to not take effect during your tests.

A @Configuration class can be loaded based on the active profiles by using the @Profile("some_profile") annotation. This annotation supports an expressive grammar allowing for rules such as "!a & !b" to mean that it should only be loaded if neither of profile a or b are loaded.

Overriding Configurations for Tests

Sometimes it can be useful to override certain beans in a test environment. This can be accomplished by enabling overrides in tests:

spring.main.allow-bean-definition-overriding=true

Then mark a test-only configuration with @TestConfiguration instead of @Configuration.

Finally, explicitly import this configuration in the test that should apply it by annotating it with the @Import annotation:

@Import(MyTestConfiguration.class)
class SomeTest {
  
}

Instead of explicitly importing the configuration, if a configuration is very specific to a given test and likely not to be reused, it can simply be defined as a static inner class, in which case the enclosing test will automatically discover and load it.

@SpringBootTest
public class SomeTest {
  @Autowired SomeClass someClass;

  @Test
  void someTest() {  }

  @TestConfiguration
  public static class Configuration {
    @Bean
    public SomeClass getSomeClass() {
      return new SomeClass();
    }
  }
}

Auto-Configuration

Spring Boot auto-configures different application components based on dependencies present in the classpath (e.g. a database pool if a database dependency is detected). It appears it also does this based on the presence of properties, and many other reasons.

In general, there are several annotations applied to certain classes which determine whether or not a class/configuration gets loaded. For example, the following appears to depend on classes GraphQL and GraphQlSource being in the classpath, the GraphQlSource bean not already existing, and the GraphQlProperties properties being defined, which it appears means at least the “root” (i.e. spring.graphql) such that the object gets created.

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({GraphQL.class, GraphQlSource.class})
@ConditionalOnMissingBean(GraphQlSource.class)
@EnableConfigurationProperties(GraphQlProperties.class)
public class GraphQlAutoConfiguration {
  

These conditions can form a dependency graph. For example, this next class itself appears to depend on the above class having been loaded.

@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass({GraphQL.class, GraphQlHttpHandler.class})
@ConditionalOnBean(GraphQlSource.class)
@AutoConfigureAfter(GraphQlServiceAutoConfiguration.class)
public class GraphQlWebMvcAutoConfiguration {
  

This can be opted-out of on a per-class basis with the annotation:

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class MyApplication {
  
}

Logging

Startup logging can be disabled with spring.main.log-startup-info=false.

You can produce more in-depth logging (full conditions report) by enabling the debug property with e.g. --debug or -Ddebug=true.

Debugging

When a Configuration does not Apply

When a configuration doesn’t appear to apply, set the org.springframework log-level to DEBUG:

<logger name="org.springframework" level="DEBUG" />

This will emit messages of the form:

GraphQlWebMvcAutoConfiguration:
  Did not match:
      - @ConditionalOnBean (types: org.springframework.graphql.ExecutionGraphQlService; SearchStrategy: all) did not find any beans of type org.springframework.graphql.ExecutionGraphQlService (OnBeanCondition)
  Matched:
      - @ConditionalOnClass found required classes 'graphql.GraphQL', 'org.springframework.graphql.web.webmvc.GraphQlHttpHandler' (OnClassCondition)
      - found 'session' scope (OnWebApplicationCondition)
March 27, 2022
329ce08 — May 23, 2024