SMF
SMF

Reputation: 33

Difference between having a proxy for a class and an interface

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 :

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'
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
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'

Upvotes: 1

Views: 738

Answers (2)

marcin
marcin

Reputation: 1

Also worth adding to this thread, the bahaviour of proxy on the application context is slightly different depending whether it's an interface based proxy or class based (CGLIB) proxy. Something to be aware of.

This is nicely demonstrated on this SpringDeveloper conference talk taking Spring security as an example: https://www.youtube.com/watch?v=9eoi1TViceM&ab_channel=SpringDeveloper

Upvotes: 0

mlecz
mlecz

Reputation: 1026

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

Upvotes: 1

Related Questions