This is an internal documentation. There is a good chance you’re looking for something else. See Disclaimer.

Configuration

Spring / Spring boot manages and distributes instances of objects. These are called beans. Bean management and the opportunity for dependency injection are some of Spring’s main features. Spring collects bean instances and uses them at the appropriate time.

There are two main ways of how we create beans:

  1. a class is annotated by @Component (or a derivative, e.g. @Listener, @Service, @RestResource, …). Any class that is annotated that way will be automatically instantiated and made available to be injected.

  2. it is created by a @Bean annotated method in a @Configuration class

With spring boot it is possible to create code that may be injected into other classes without an interface.

E.g.

@Component
public class TestService {

    public String generateString() {
        // ...
    }

    public void processString(String param) {
        // ...
    }
}
@Listener(filter = "User")
@ThreadScope
public class TestListener extends CollectingEntityListener {

    private final TestService service;

    public TestListener(TestService service) {
        this.service = service;
    }

}

If such a service should be available in another module, an “visible” interface in - for example - the api package may still be required.

@ThreadScope

If a bean should be recreated for each thread (e.g. CollectingEntityListeners) the annotation @ThreadScope should be used.

Accessing Beans

Beans will be automatically injected into @Autowired annotated members, methods or as constructor parameters if spring is able to unambiguously identify them. (E.g. if there is only one bean of the given type, or if the parameter name is named like a specific implementation).

If there are multiple implementations of an interface that all need to be injected, @Autowired can be used to inject lists of beans.

Example:

public interface TestInterface {
    String getString();
}

@Component
public class TestImplementationA implements TestInterface {
    @Override
    public String getString() {
        return "A";
    }
}

@Component
public class TestImplementationB implements TestInterface {
    @Override
    public String getString() {
        return "B";
    }
}

@Component
public class Test {
    private final TestInterface testImplementationA;

    private List<TestInterface> implementations;

    public Test(TestInterface testImplementationA) { //TestImplementationA will be injected here
        this.testImplementationA = testImplementationA;
    }

    @Autowired //will inject both TestImplementationA and TestImplementationB
    public void setImplementations(List<TestInterface> implementations) {
        this.implementations = implementations;
    }
}

Naming a parameter / variable like a specific implementation can be used to get instances of objects in other modules. This is for example used to register specific validators in other modules.

@Bean
public EntitiesValidatorContribution pagePathUniquenessValidator(EntitiesValidator pathUniquenessValidator) {
    EntitiesValidatorContribution contribution = new EntitiesValidatorContribution();
    contribution.setValidator(pathUniquenessValidator);
    contribution.setFilter("Page");
    return contribution;
}

@Qualifier

If there are beans of a more generic type (e.g. String), @Qualifier can be used to distinguish them. This is used to replace old “string contributions” as shown below:

// annotation class
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface DisabledLegacyAction {
}

// configuration in @Configuration classes
// ...
@Bean
@DisabledLegacyAction
public String disableCreateDebitorExportAction() {
    return "nice2.optional.finance.CreateDebitorExportAction";
}
// ...
@Bean
@DisabledLegacyAction
public String disablePublishAllAction() {
    return "nice2.dms.PublishAllAction";
}

// usage in component
//all String beans with Qualifier @DisabledLegacyAction will be autowired
@Autowired(required = false)
@DisabledLegacyAction
public void setDisabledLegacyActions(List<String> disabledLegacyActions) {
    this.disabledLegacyActions.addAll(disabledLegacyActions);
}

File-References

To reference module-resources findModelResource or findJavaResource of AppModule should be used. If file access is required in @Configuration classes AbstractConfiguration should be extended to get access to its convenient findModelResource method.

@Bean
public TemplateSnippetContributionsBuilder toccoTemplateSnippetsContributions() {
    return TemplateSnippetContributionsBuilder.create()
        .uniqueId("jira_issue_cellrenderer")
        .label("templatesnippet.jira_issue_cellrenderer")
        .freemarker(findModelResource("templatesnippet/jira_issue_cellrenderer.ftl"))
        .categories("cellrenderer");
}

To access files from other modules, the module manager can be used to get the other module first:

@Bean
public LabelContentSynchronisationDescription address3660LabelContentDescription() {
    AppModule reportingModule = moduleManager.getModule("nice.core.reporting");
    LabelContentSynchronisationDescription description = new LabelContentSynchronisationDescription();
    description.setIdentifier("zf_3660_address");
    description.setModule("Address");
    description.setLabelKey("report.Label_content.zf3660");
    description.setFreemarker(findModelResource(reportingModule, "outputtemplate/labelcontents/content_3660.ftl"));
    description.setLess(findModelResource(reportingModule, "outputtemplate/labelcontents/content_3660.less"));
    return description;
}