ArchUnit 不要改变 Spring bean 内部实例变量的状态。

huangapple go评论91阅读模式
英文:

ArchUnit to stop changing the state of instance variables within Spring beans

问题

我应该阻止我的开发人员在此Bean内创建实例变量,并开始在方法内更改状态(请参见下面的代码片段,我不希望发生这种情况)。需要通过archUnit执行此操作。不想在代码审查过程中捕获此问题。

@Service
public class HotelsService {
    private int someInteger;

    public List<Hotel> fetchAllHotels() {
        this.someInteger=1; // 仅作示例
        return dummyHotelsList();
    }
}

我可以拥有实例变量并仅读取它。但不想对其进行写操作。

在ArchUnit中有任何简单的实现方法吗?

英文:

I have Spring beans this way

@Service
public class HotelsService {

    public List&lt;Hotel&gt; fetchAllHotels() {
        return dummyHotelsList();
    }
}

I should stop my developers from creating an instance variable within this bean and start changing the state within a method (see below snippet which i dont want to happen). Need to do this via archUnit. Didn't want to catch this during code review process

@Service
public class HotelsService {
    private int someInteger;

    public List&lt;Hotel&gt; fetchAllHotels() {
        this.someInteger=1; // just for example
        return dummyHotelsList();
    }
}

I am ok to have instance variables and just read it. But don't want to write on it.

Any straightforward implementation in ArchUnit ?

答案1

得分: 1

你可能需要实现一个自定义(但很直观... 😁)ArchCondition

ArchRule service_methods_should_not_set_service_fields =
    methods()
        .that().areDeclaredInClassesThat().areAnnotatedWith(Service.class)
        .should(new ArchCondition<JavaMethod>("不应设置@Service类的字段") {
            @Override
            public void check(JavaMethod method, ConditionEvents events) {
                method.getFieldAccesses().stream()
                    .filter(accessType(SET))
                    .filter(target(With.owner(annotatedWith(Service.class))))
                    .forEach(access -> events.add(violated(access, access.getDescription())));
            }
        })
        .because("bean不应该有状态");

使用:

import com.tngtech.archunit.core.domain.JavaMethod;
import com.tngtech.archunit.core.domain.properties.HasOwner.Predicates.With;
import com.tngtech.archunit.lang.ArchCondition;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.lang.ConditionEvents;

import static com.tngtech.archunit.core.domain.JavaFieldAccess.AccessType.SET;
import static com.tngtech.archunit.core.domain.JavaFieldAccess.Predicates.accessType;
import static com.tngtech.archunit.core.domain.JavaFieldAccess.Predicates.target;
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;
import static com.tngtech.archunit.lang.SimpleConditionEvent.violated;

你的示例失败了:

规则 '在被注解为@Service的类中声明的方法不应设置@Service类的字段,因为bean不应该有状态' 被违反了(1次):
方法 <HotelsService.fetchAllHotels()> 在 (源代码位置) 设置了字段 <HotelsService.someInteger>。

英文:

You may have to implement a custom (but straightforward... 😁) ArchCondition:

ArchRule service_methods_should_not_set_service_fields =
    methods()
        .that().areDeclaredInClassesThat().areAnnotatedWith(Service.class)
        .should(new ArchCondition&lt;JavaMethod&gt;(&quot;not set field of @Service class&quot;) {
            @Override
            public void check(JavaMethod method, ConditionEvents events) {
                method.getFieldAccesses().stream()
                    .filter(accessType(SET))
                    .filter(target(With.owner(annotatedWith(Service.class))))
                    .forEach(access -&gt; events.add(violated(access, access.getDescription())));
            }
        })
        .because(&quot;beans should not have state&quot;);

using

import com.tngtech.archunit.core.domain.JavaMethod;
import com.tngtech.archunit.core.domain.properties.HasOwner.Predicates.With;
import com.tngtech.archunit.lang.ArchCondition;
import com.tngtech.archunit.lang.ArchRule;
import com.tngtech.archunit.lang.ConditionEvents;

import static com.tngtech.archunit.core.domain.JavaFieldAccess.AccessType.SET;
import static com.tngtech.archunit.core.domain.JavaFieldAccess.Predicates.accessType;
import static com.tngtech.archunit.core.domain.JavaFieldAccess.Predicates.target;
import static com.tngtech.archunit.core.domain.properties.CanBeAnnotated.Predicates.annotatedWith;
import static com.tngtech.archunit.lang.SimpleConditionEvent.violated;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods;

Your example fails with:
> Rule 'methods that are declared in classes that are annotated with @Service should not set field of @Service class, because beans should not have state' was violated (1 times):
Method &lt;HotelsService.fetchAllHotels()> sets field &lt;HotelsService.someInteger> in (source code location)

答案2

得分: 0

也许问题可以在需求层面简化:
你真的需要允许服务中的非常量字段吗?

一个(直截了当!😁)防止这些情况的规则是:

ArchRule services_should_only_have_constant_fields =
    fields()
        .that().areDeclaredInClassesThat().areAnnotatedWith(Service.class)
        .should().beStatic().andShould().beFinal()
        .because("beans should not have state");
英文:

Maybe the problem can be simplified at the level of requirements:
Do you really have to allow for non-constant fields in your services?

A (straightforward! 😁) rule to prevent those would be:

ArchRule services_should_only_have_constant_fields =
    fields()
        .that().areDeclaredInClassesThat().areAnnotatedWith(Service.class)
        .should().beStatic().andShould().beFinal()
        .because(&quot;beans should not have state&quot;);

huangapple
  • 本文由 发表于 2023年6月1日 00:07:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/76375426.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定