javaspringspring-bootaopmethod-interception

Spring ProxyFactoryBean @Autowired not working


I need to intercept methods from a interface, and found this implementation of MethodInterceptor, which I tested on a new spring app and worked.

The problem is, I can't seem to get it working on the spring application I need it to.

@Configuration
public class TestMethodConfig {

@Autowired
private TestService testService;


  @Bean
  @Primary
  public ProxyFactoryBean testProxyFactoryBean() {
    ProxyFactoryBean testProxyFactoryBean = new ProxyFactoryBean();
    testProxyFactoryBean.setTarget(testService);
    testProxyFactoryBean.setInterceptorNames("testMethodInterceptor");
    return testProxyFactoryBean;
  }
}

@Service
public class TestServiceImpl implements TestService{
  @Override
  public void testMethod(String test) {
    System.out.println("testService String");
  }
}

public interface TestService{
  void testMethod(String test);
}

@RestController
public class Controller {
  @Autowired
  private TestService testProxyFactoryBean;

  @GetMapping(value = "/test")
  public void test(){
    testProxyFactoryBean.testMethod("valor");
  }
}

@Component
public class TestMethodInterceptor implements MethodInterceptor {

  @Override
  public Object invoke(MethodInvocation invocation) throws Throwable {
    System.out.println("before method");
    System.out.println("invocation: " +      Arrays.toString(invocation.getArguments()));
    Object retVal = invocation.proceed();
    System.out.println("after method");
    return retVal;
  }
}

I used Spring Actuator to check the beans relations, and I found that the @Autowired TestService on Controller should be getting assigned to testProxyFactoryBean, but its getting assigned to the TestServiceImpl bean instead, so I believe there is a problem creating the proxy.

testProxyFactoryBean is Autowiring to testServiceImpl instead of testProxyFactoryBean


Solution

  • In short

    I don't know how/why it was:

    on a new spring app and worked.

    but:

    I can't seem to get it working on the spring application I need it to.

    ..can probably be fixed!

    Make it consistent

    Or:

    @Configuration
    public class TestMethodConfig {
    
      @Autowired
      private TestService testService;
    }
    ...
    // !!
    public class TestServiceImpl implements TestService{
      @Override
      public void testMethod(String test) {
        System.out.println("testService String");
      }
    }
    ...
    @Service // !!!
    public interface TestService{
      void testMethod(String test);
    }
    ...
    @RestController
    public class Controller {
      @Autowired
      private TestService testProxyFactoryBean;
      ...
    

    Or: Impl! (Use Interface and Impl consistently!)


    In Detail

    6.4. Using the ProxyFactoryBean to Create AOP Proxies

    esp. Proxying Interfaces.

    So with "least impact" (and java config), it should be:

    @Configuration
    public class TestMethodConfig {
    
      // !!! Impl from component-scan (@Service), NOT interface:
      @Autowired
      private TestServiceImpl testServiceImpl; // or define custom, or "inline"...
    
      @Bean
      @Primary // only if you need it, better would be: distinct!
      public ProxyFactoryBean testProxyFactoryBean() {
        ProxyFactoryBean testProxyFactoryBean = new ProxyFactoryBean();
         // !!! set proxyInterface as documented:
        testProxyFactoryBean.setProxyInterface(TestService.class);
        testProxyFactoryBean.setTarget(testServiceImpl);
        testProxyFactoryBean.setInterceptorNames("testMethodInterceptor");
        // ...
        return testProxyFactoryBean;
      }
    }
    

    ..enjoy! ;)