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 ?
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)