Difference between having a proxy for a class and an interface

Viewed 32

I'm trying to load the proxy by class and by interface for another object.

To ensure that a proxy is created, I have wrapped it with @Cacheable and @CacheEvict annotations on it.

For having a reference C1 which is for a class, works.

But when having a reference for C2 which is for a class that implements another interface I2 it does not work.

If I replace the reference of C2 to I2 then it works again.

Could somebody please point me what is happening?

What else I need to read in order to understand?

Why is it thrown an exception for the c2 case?

Here is the code:

@Component
class HolderC {

    @Autowired
    private C1 c1;

    @Autowired
    private C2 c2;

    @Autowired
    private I2 i3;

    @PostConstruct
    public void postConstruct() {
        System.out.println(c1.getClass());
        System.out.println(c2.getClass());
        System.out.println(i3.getClass());
    }
}

@Component
@Caching
class C1 {
    @CacheEvict
    public void m1() {
    }
}

interface I2 {
    void m2();
}

@Component
@Caching
class C2 implements I2 {
    @CacheEvict
    @Override
    public void m2() {
    }
}

Along with :

@ComponentScan
@EnableCaching
public class ApplicationConfig {

    @Bean
    public Caffeine caffeineConfig() {
        return Caffeine.newBuilder().expireAfterWrite(60, TimeUnit.MINUTES);
    }

    @Bean
    public CacheManager cacheManager(Caffeine caffeine) {
        CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
        caffeineCacheManager.setCaffeine(caffeine);
        return caffeineCacheManager;
    }
}

And it is printing :

  • for c1:
21:25:24.244 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'c1'
21:25:24.290 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'c2'
21:25:24.297 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'holderC'
class java.config.context.C1$$EnhancerBySpringCGLIB$$a871b65d
21:25:24.302 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'caffeineConfig'
21:25:24.320 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'cacheManager'
  • for c2
21:26:53.706 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'c1'
21:26:53.755 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'c2'
21:26:53.764 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'holderC'
21:26:53.769 [main] WARN org.springframework.context.annotation.AnnotationConfigApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'holderC': Unsatisfied dependency expressed through field 'c2'; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'c2' is expected to be of type 'java.config.context.C2' but was actually of type 'java.config.context.$Proxy22'
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'holderC': Unsatisfied dependency expressed through field 'c2'; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'c2' is expected to be of type 'java.config.context.C2' but was actually of type 'java.config.context.$Proxy22'
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1411)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:849)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549)
    at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:88)
    at java.config.context.Runner.main(Runner.java:9)
Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'c2' is expected to be of type 'java.config.context.C2' but was actually of type 'java.config.context.$Proxy22'
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.checkBeanNotOfRequiredType(DefaultListableBeanFactory.java:1672)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1650)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1213)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
    ... 14 more
  • for c3
21:28:26.331 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'c1'
21:28:26.386 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'c2'
21:28:26.397 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'holderC'
class java.config.context.$Proxy22
21:28:26.405 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'caffeineConfig'
21:28:26.431 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'cacheManager'
1 Answers

Spring creates java proxy by default if its possible. Proxy works only on interface, so for class it has to fallback to CGLIB.

You can annotate C2 with @Scope setting its proxyMode to TARGET_CLASS. This will force this class to use CGLIB.

You can also use this setting globally by using this configuration annotation @EnableAspectJAutoProxy(proxyTargetClass=true) you will need to import additional dependency though

Related