英文:
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<Hotel> 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<Hotel> 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<JavaMethod>("not set field of @Service class") {
@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("beans should not have state");
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 <HotelsService.fetchAllHotels()> sets field <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("beans should not have state");
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论