Uploaded image for project: 'Spring Data Commons'
  1. Spring Data Commons
  2. DATACMNS-926

Unable to write custom implementation of CRUD method when custom class uses CGLib proxies



    • Type: Bug
    • Status: Closed
    • Priority: Minor
    • Resolution: Works as Designed
    • Affects Version/s: 1.12.4 (Hopper SR4)
    • Fix Version/s: None
    • Component/s: Core
    • Labels:


      When my customization classes require auto-wiring (or perhaps a @Transactional annotation), writing a custom implementation of a CRUD method such as save(…) does not work. It seems to be because these classes are implemented with CGlib proxies and this results in the save(…) methods on these classes having the signature:

      MyEntity save(MyEntity entity);

      without any generic type parameters. The DefaultRepositoryInformation.parametersMatch(…) method then returns false for these as they are not generic method declarations and this results in the custom method never being called. If I change the newly added DefaultRepositoryInformation unit test for this case (following the fix for DATACMNS-912), which makes the use case very clear, by replacing SampleRepositoryImpl with:

      static class SampleRepositoryImpl {
      	public Sample save(Sample entity) {
      		return entity;

      The test fails which is expected. I then wrote a similar test as my original test which reproduces the issue, this time with JDK proxies (using an interface for the custom implementation so I could use JDK proxies and not CGlib):

      package org.springframework.data.repository.core.support;
      import static org.hamcrest.Matchers.*;
      import static org.junit.Assert.*;
      import static org.mockito.Mockito.*;
      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
      import org.junit.Test;
      import org.springframework.data.repository.CrudRepository;
      import org.springframework.data.repository.core.RepositoryInformation;
      import org.springframework.data.repository.core.RepositoryMetadata;
      public class DefaultRepositoryInformation2UnitTests {
      	public void discoversCustomlyImplementedCrudMethodWithGenericParameters()
      			throws SecurityException, NoSuchMethodException {
      		BossRepositoryCustom customImplementation = (BossRepositoryCustom) Proxy.newProxyInstance(BossRepositoryCustom.class.getClassLoader(),
      				new Class[] { BossRepositoryCustom.class },
      				new InvocationHandler() {
      			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      				return null;
      		RepositoryMetadata metadata = new DefaultRepositoryMetadata(BossRepository.class);
      		RepositoryInformation information = new DefaultRepositoryInformation(metadata, RepositoryFactorySupport.class,
      		Method customBaseRepositoryMethod = BossRepository.class.getMethod("save", Object.class);
      		assertThat(information.isCustomMethod(customBaseRepositoryMethod), is(true));
      		BossRepository repository = mock(BossRepository.class);
      		repository.save(new Boss());
      	interface BossRepository extends CrudRepository<Boss, Long> {}
      	interface BossRepositoryCustom {
      		public <S extends Boss> S save(S boss);
      	static class Boss {}

      I hope my sample test is clear. At first I thought I would remove auto-wiring so I rewrote my customization beans with required argument constructors but then realized I also required @Transactional annotations in some places, which again required proxying my customization beans.




            mp911de Mark Paluch
            gerard.nantel Gerard Nantel
            Last updater:
            Mark Paluch
            0 Vote for this issue
            2 Start watching this issue