Last updated Apr 19, 2024

Spring Java configuration of app components

Background

Server app developers have traditionally had several ways to configure their app components:

There is now a fourth way, namely Spring Java configuration, that solves many of the shortcomings of the approaches listed above:

  • uses code instead of XML, providing more power and better tooling
  • uses a simpler, more reliable toolchain requiring little experience with Atlassian products
  • allows components to be created conditionally
  • keeps business logic free of dependency injection concerns

Supported products

Spring Java configuration can be used with all supported versions of Atlassian server and Data Center products.

How it works (high-level)

Here's an overview of how to use Spring Java configuration in your app (full details are linked below):

  1. Add dependencies on a few standard Spring & OSGi libraries and (optionally) a small Atlassian helper library.
  2. Add three packages to your Import-Package instructions (done automatically by the helper library).
  3. Create one or more classes annotated with Spring’s @Configuration annotation.
  4. Populate your configuration classes with @Bean methods, one for every component in your app. If you wish, use our helper library to more easily write the @Bean methods that import or export OSGi services, or that define new module types.
  5. Register your @Configuration classes as beans in your Spring XML file (create one if necessary).
  6. If converting an existing plugin, remove all the XML and annotations you had before.

Example

Here’s what a configuration class might look like:

1
2
@Configuration
public class MySpringBeans {

    // An app component
    @Bean
    public FooService fooService() {
        return new FooServiceImpl();
    }
    
    // The above component, exported to OSGi
    // The "exportOsgiService" method comes from our helper library
    @Bean
    public FactoryBean<ServiceRegistration> exportFooService(FooService fooService) {
        return exportOsgiService(fooService, ExportOptions.as(FooService.class));
    }
    
    // A component imported from OSGi
    // The "importOsgiService" method comes from our helper library
    @Bean
    public ApplicationProperties applicationProperties() {
        return importOsgiService(ApplicationProperties.class);
    }
    
    // A component imported from OSGi, with an LDAP filter applied
    // Only a service with the "foo" property set to "bar" will be imported
    // Again, the "importOsgiService" method comes from our helper library (0.2.0 and later)
    @Bean
    public ApplicationProperties applicationProperties() {
        String ldapFilter = "(foo=bar)";
        return importOsgiService(ApplicationProperties.class, ImportOptions.defaultOptions().withFilter(ldapFilter));
    }
    
    // A collection of components imported from OSGi
    // The "importOsgiServiceCollection" method comes from our helper library (0.6.0 and later)
    @Bean
    public List<BarService> barServices() {
        return importOsgiServiceCollection(list(BarService.class));
    }
}

Detailed instructions

Launch announcement

This technique was announced at Atlas Camp 2019. Watch the video to see it compared to previous approaches.

Rate this page: