Last updated Mar 27, 2024

This page describes how to convert a plugin from Atlassian Spring Scanner to Spring Java configuration.

POM changes

  • Remove all dependencies and plugins with spring-scanner in their name.
  • Add the necessary dependencies.

Create a Java config class

  1. Create a class for your Spring config, called (for example) SpringBeans, in a new package somewhere under src/main/java, for example com.example.myplugin.spring.
  2. Annotate that class with @org.springframework.context.annotation.Configuration.

Change your Spring XML

You will currently have a Spring XML file in src/main/resources/META-INF/spring (it could be called anything ending in .xml). Modify this file as follows:

  • Remove all mentions of the atlassian-scanner namespace from the root element.
  • Remove the <atlassian-scanner:scan-indexes/> element.
  • Register your new @Configuration classes.

Importing services from OSGi

Replace all usages of Spring Scanner’s @ComponentImport annotation with methods like this in your Spring config class (one such method for each service you are importing):

1
2
@Bean
public FooService fooService() {
    return importOsgiService(FooService.class);
}

… where the importOsgiService method is statically imported from com.atlassian.plugins.osgi.javaconfig.OsgiServices.

Exporting services to OSGi

Replace each usage of Spring Scanner’s @ExportAsService annotation with a pair of methods like this in your Spring config class:

1
2
@Bean
public BarService barService() {
    // TODO instantiate and return your BarService implementation
}

@SuppressWarnings("rawtypes") // ServiceRegistration is used as a raw type in the exportOsgiService method
@Bean
public FactoryBean<ServiceRegistration> exportBarService(
        final BarService barService) {
    ExportOptions options = ExportOptions.as(MyPluginComponent.class).withProperty("some_key", "some_value"); // or whatever other properties you want (including none)
    return exportOsgiService(barService, options);
}

… where exportOsgiService and ExportOptions are imported via:

1
2
import com.atlassian.plugins.osgi.javaconfig.ExportOptions;
import static com.atlassian.plugins.osgi.javaconfig.OsgiServices.exportOsgiService;

You can also remove any @Named or @Component annotation from the class that implements BarService (since you’re now instantiating it programmatically).

Defining plugin module types

Replace each usage of Spring Scanner’s @ModuleType annotation with a pair of methods like this in your Spring config class:

1
2
@Bean
public ListableModuleDescriptorFactory myModuleDescriptorFactory() {
    return new MyModuleDescriptorFactory();
}

@SuppressWarnings("rawtypes") // ServiceRegistration is used as a raw type in the exportAsModuleType method
@Bean
public FactoryBean<ServiceRegistration> exportMyModuleType(
        final ListableModuleDescriptorFactory moduleDescriptorFactory) {
    return exportAsModuleType(moduleDescriptorFactory);
}

… where the exportAsModuleType method is statically imported from com.atlassian.plugins.osgi.javaconfig.OsgiServices.

Migrating scanned dependencies

If the Spring Scanner plugin configuration that you deleted from your POM contained a scannedDependencies element, then it’s highly likely that Spring Scanner was creating beans for (and/or exporting to OSGi) various classes within those dependencies. You will need to add further @Bean methods to your new config class to ensure that you are still creating and/or exporting those beans. You might want to check what Spring beans previously existed in your plugin at runtime (e.g. using the QuickReload plugin’s REST API), and check them again after the conversion, to be sure you haven’t missed any.

Removing dependency injection annotations

One of the advantages of Spring Java config is that many beans are created explicitly via their constructor, for example new FooServiceImpl(…). Such classes no longer need any of the following dependency injection annotations:

  • @Autowired (from spring-beans)
  • @Component (from spring-context)
  • @Inject (from javax.inject)
  • @Named (from javax.inject)

Decomposing your config classes (optional)

If your @Configuration class has become too big or messy, you can easily split it up as follows:

  1. Pick one of your config classes (or create a new config class) to be the “main” config class.
  2. Move some/all of its beans to one or more new @Configuration classes, according to whatever categorisation makes sense (for example put all your web-related beans into MyWebBeans.class, or split up the beans by feature).
  3. At the top of the main config class, add an @Import annotation that lists these subsidiary config classes, for example:
1
2
@Configuration
@Import({
    MyPersistenceBeans.class,
    MyServiceBeans.class,
    MyWebBeans.class
})
public class MainSpringConfig {}

Rate this page: