Class AutowireImplRuleTest

java.lang.Object
ubic.gemma.core.architecture.AutowireImplRuleTest

public class AutowireImplRuleTest extends Object
Phase 3 build hygiene: forbid declaring Spring-injected dependencies with an Impl-typed reference in production code.

When Spring's AOP machinery proxies a bean (the most common cause being a @Transactional method, but @Cacheable, @Async, @PreAuthorize and similar advice all do the same), the resulting proxy implements the bean's interfaces but is not a subclass of the underlying Impl. A field declared with the Impl type cannot be satisfied at injection time and Spring fails with BeanNotOfRequiredTypeException (JDK dynamic proxy) or causes subtle aliasing bugs under CGLIB. The cure is to declare the field with the interface type — Spring then injects the proxy transparently.

This rule was prompted by the AspectJ deeper recce (commit b16450a5e8 on the unmerged worktree-aspectj-deeper branch), which identified one historical instance of the pattern (EeWriteServiceImpl.persisterHelper) that has since been remediated. The rule locks the codebase down against re-introduction.

Two complementary rules ship together:

Scope: production classes only (tests excluded via ImportOption.DoNotIncludeTests). Mocks and test doubles legitimately need concrete types.

  • Field Details

    • autowired_fields_must_not_be_impl_typed

      public static final com.tngtech.archunit.lang.ArchRule autowired_fields_must_not_be_impl_typed
      Intentionally vacuous when no @Autowired Impl-typed fields exist — that is the goal state of the migration. Vacancy only means that once classes_are_actually_imported(JavaClasses) has confirmed there were classes to check. allowEmptyShould(true) suppresses ArchUnit's default "rule matched no classes" failure so the guard keeps locking the codebase down without flagging the clean state.
    • constructor_parameters_must_not_be_impl_typed

      public static final com.tngtech.archunit.lang.ArchRule constructor_parameters_must_not_be_impl_typed
      Intentionally vacuous when no constructor-injected Impl-typed parameters exist — that is the goal state, subject to the same caveat as above: see classes_are_actually_imported(JavaClasses). allowEmptyShould(true) suppresses ArchUnit's default "rule matched no classes" failure.
  • Constructor Details

    • AutowireImplRuleTest

      public AutowireImplRuleTest()
  • Method Details

    • classes_are_actually_imported

      public static void classes_are_actually_imported(com.tngtech.archunit.core.domain.JavaClasses classes)
      🛑 The guard against a guard that checks nothing.

      Both rules below are allowEmptyShould( true ) because a clean codebase legitimately matches no fields — which means neither can distinguish "clean" from "ArchUnit imported nothing". That distinction mattered: archunit 1.3.0's bundled ASM could not read Java 25 bytecode (major version 69) and imported zero classes silently, so this test passed while checking nothing from the day it was written until 2026-08-26, when the same failure was found in a sibling rule. Fixed by archunit 1.4.1.

      This sentinel states the expectation the other two cannot: an empty import is a broken toolchain.