admin管理员组

文章数量:1394206

Certain frameworks require the empty constructor of a class to be present, e.g. a JakartaEE stateless service, but they do not have the appropriate dependencies injected. I would like to annotate these constructors with something like a DoNotUse annotation (with a reason), and then use ArchUnit to check if no one is actually using it.

Something like this:

        ArchRuleDefinition.noConstructors()
                .that()
                .areAnnotatedWith(DoNotUse.class)
                .should()
                .beCalled();

But beCalled or beUsed AFAIK does not exist. I solved it now by adding a never matching condition:

        ArchRuleDefinition.constructors()
                .that()
                .areAnnotatedWith(DoNotUse.class)
                .should()
                .onlyBeCalled()
                .byClassesThat()
                .haveSimpleName("ThisOnPurposeDoesNotMatchAnything");

How do I write a beCalled or the inverse neverBeCalled?

Certain frameworks require the empty constructor of a class to be present, e.g. a JakartaEE stateless service, but they do not have the appropriate dependencies injected. I would like to annotate these constructors with something like a DoNotUse annotation (with a reason), and then use ArchUnit to check if no one is actually using it.

Something like this:

        ArchRuleDefinition.noConstructors()
                .that()
                .areAnnotatedWith(DoNotUse.class)
                .should()
                .beCalled();

But beCalled or beUsed AFAIK does not exist. I solved it now by adding a never matching condition:

        ArchRuleDefinition.constructors()
                .that()
                .areAnnotatedWith(DoNotUse.class)
                .should()
                .onlyBeCalled()
                .byClassesThat()
                .haveSimpleName("ThisOnPurposeDoesNotMatchAnything");

How do I write a beCalled or the inverse neverBeCalled?

Share Improve this question asked Mar 13 at 12:29 tbeernottbeernot 2,6405 gold badges26 silver badges32 bronze badges 6
  • why create a method/constructor that should never be called? – Stultuske Commented Mar 13 at 12:29
  • you can usually declare those empty constructors to be private, the frameworks will stick pick them up – Rick Commented Mar 13 at 12:40
  • Some frameworks do require public default constructors. Even if it could be avoided in this case, it's a valid question. – kapex Commented Mar 13 at 12:42
  • 1 I already explained that: frameworks like JakartaEE require those constructors. When using WildFly you can suffice with a protected constructor (because the generated proxies will extend the class), not private. But then IntelliJ will place an error marker on those classes and your code base is all red. – tbeernot Commented Mar 13 at 12:46
  • I don't have an answer but sometimes it helps to reverse the order, even though it look less intuitive. Instead "this should not be called" reverse it to "nothing should call this" e.g. create a rule: no classes should call constructors annotated with DoNotUse. – kapex Commented Mar 13 at 13:02
 |  Show 1 more comment

1 Answer 1

Reset to default 0

You can use a custom condition using convenient factory methods for ArchConditions and DescribedPredicates:

import static com.tngtech.archunit.base.DescribedPredicate.describe;
import static com.tngtech.archunit.lang.conditions.ArchConditions.be;

// ...

    ArchRule rule =  ArchRuleDefinition.noConstructors()
            .that().areAnnotatedWith(DoNotUse.class)
            .should(be(describe("called", ctor -> !ctor.getCallsOfSelf().isEmpty())));

If you want more descriptive error messages indicating where the unwanted call happens (cf. comment), you can use a custom ArchCondition like this:

import static com.tngtech.archunit.lang.SimpleConditionEvent.satisfied;

// ...

    ArchRule rule = ArchRuleDefinition.noConstructors()
            .that().areAnnotatedWith(DoNotUse.class)
            .should(new ArchCondition<JavaConstructor>("be called") {
                @Override
                public void check(JavaConstructor constructor, ConditionEvents events) {
                    constructor.getCallsOfSelf().forEach(call ->
                        events.add(satisfied(call, call.getDescription()))
                    );
                }
            });

本文标签: