mmo
mmo

Reputation: 4216

ArchUnit: how to check for access to Bean-creation methods?

Spring supports the creation of Beans using methods. E.g. a definition like so:

    @Bean
    public DataSource springBatchDataSource() {
        SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
        ... rest of init code omitted for brevity ...
        return dataSource;
    }

defines a Bean "SpringBatchDataSource". That Bean can then be used for Autowiring etc.

However, we had a couple of cases where junior colleagues hadn't understood that concept and had instead called the method to create an object of that type, i.e. instead of

...
@Autowire
DataSource ds;
...

they had written something like:

...
DataSource ds = springBatchDataSource();
...

If you do that at several places you end up having multiple objects instead of all talking to the same, single Bean (as was intended - that Bean should be (considered) a singleton in your application). The multiple objects then caused major issues in our application.

I thus now want to add an ArchUnit-check to prevent the access to methods that are actually Bean creators. How do I do that?

I found a callMethodWhere(...)-method that takes a predicate on methods and I found a predicate areAnnotatedWith(<class>.class), so that would become: areAnnotatedWith(Bean.class). But I am lost in how to combine and apply these, i.e. what would need to replace the ??? in my below snippet?

    @ArchTest
    void checkNoCallToBeanCreators(JavaClasses classes) {

        ArchRule rule = noClasses()
            .should().callMethodWhere(???.areAnnotatedWith(Bean.class));

        rule.check(classes);
    }

Or is there some better, more elegant or even straight forward way of checking this?

Upvotes: 0

Views: 158

Answers (1)

Manfred
Manfred

Reputation: 3142

I found a callMethodWhere(...)-method that takes a predicate on methods

You're wrong in a relevant detail: callMethodWhere(...) takes a predicate for JavaMethodCalls, not for JavaMethods. The actually called method is the target of a method call.

With JavaAccess.Functions.Get.target, you can conveniently transform a predicate for JavaMethods to a predicate for JavaMethodCalls. With

import static com.tngtech.archunit.core.domain.JavaAccess.Functions.Get.target;
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;

you can write your rule as

noClasses().should().callMethodWhere(target().is(annotatedWith(Bean.class)))

Upvotes: 0

Related Questions