Koray Tugay
Koray Tugay

Reputation: 23780

Spring Scoped Proxy working for fields of the Scoped Proxy Beans fields but not the beans itself?

This is the code I have:

SpringConfiguration:

package biz.tugay;
/* User: [email protected] Date: 25/12/15 Time: 19:30 */

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "biz.tugay")
public class SpringConfiguration {
}

Foo:

package biz.tugay;
/* User: [email protected] Date: 25/12/15 Time: 19:36 */

public interface Foo {
    String getState();
}

FooImpl:

package biz.tugay;
/* User: [email protected] Date: 25/12/15 Time: 19:31 */

import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;

import java.util.UUID;

@Component
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class FooImpl implements Foo {
    private final String state = UUID.randomUUID().toString();
    public String getState() {
        return state;
    }
}

Bar:

package biz.tugay;
/* User: [email protected] Date: 25/12/15 Time: 19:35 */

public interface Bar {
    Foo getFoo();
}

BarImpl:

package biz.tugay;
/* User: [email protected] Date: 25/12/15 Time: 19:33 */

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope(value = "singleton")
public class BarImpl implements Bar {

    private Foo foo;

    @Autowired
    public void setFoo(Foo foo) {
        this.foo = foo;
    }

    public Foo getFoo() {
        return foo;
    }
}

and the Test Class:

package biz.tugay;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import static java.lang.System.out;

public class TestClass {

    final static Class<SpringConfiguration> SPRING_CONFIGURATION_CLASS = SpringConfiguration.class;

    public static void main(String[] args) {

        final ApplicationContext applicationContext;
        applicationContext = new AnnotationConfigApplicationContext(SPRING_CONFIGURATION_CLASS);

        final Bar barOne = applicationContext.getBean(BarImpl.class);
        final Bar barTwo = applicationContext.getBean(BarImpl.class);

        out.println("barOne == barTwo: " + (barOne == barTwo));
        out.println("barOne.getFoo == barTwo.getFoo: " + (barOne.getFoo() == barTwo.getFoo()));

        out.println(barOne.getFoo().getState());
        out.println(barTwo.getFoo().getState());

    }
}

And the output will be:

barOne == barTwo: true
barOne.getFoo == barTwo.getFoo: true
064bdb74-cad7-4d13-86fe-a4fc0021e5a0
c6f2d9be-09b4-456d-9414-8ece2f2acc9e

My question is,

I understand that 064bdb74-cad7-4d13-86fe-a4fc0021e5a0 is not equal to c6f2d9be-09b4-456d-9414-8ece2f2acc9e because FooImpl is actually ScopedProxy.

What I do not understand is, how come barOne.getFoo can be equal to barTwo.getFoo?

How is this possible:

barOne.getFoo() == barTwo.getFoo()
barOne.getFoo().getState() != barTwo().getFoo().getState()

???

Upvotes: 1

Views: 688

Answers (2)

Babl
Babl

Reputation: 7646

To unterstand the output at first you need to understand how and what is doing the Spring Framework. Lets start with the idea that when you are injecting "short lived" scope bean into a "long lived" scope bean (in your case the prototype bean into singleton bean) Spring injects an AOP proxy in place of the scoped bean.

That is, you need to inject a proxy object that exposes the same public interface as the scoped object but that can also retrieve the real target object from the relevant scope (such as an HTTP request) and delegate method calls onto the real object.

Basically there are two main types of proxies the JDK proxy and CGLIB proxies (default).

So lets go back to the example code and try to understand what is going on ... When your application starts Spring Framework creates two beans the Foo and the Bar but both beans are not just a simple implementations (FooImpl and BarImpl) but are proxies (the CglibAopProxy's). So in your TestClass class you have 2 references to the same AopProxy of BarImpl the barOne and barTwo. As the Bar is scoped as singleton the underling target class of the AopProxy is the same object (BarImpl) which is pre-inililized. And this Bar AopProxy has a injected AopPorxy of type Foo, so when you are executing following code barOne.getFoo() == barTwo.getFoo() it evaluates to true, as both barOne and barTwo have the same instance of AopProxy (not the target class), but when you are calling the barTwo().getFoo().getState() the AopProxy is creating a new instance of target class (FooImpl) and exeutes the getState() method on it. So each call to the bar.getFoo().getState() will create a new instance of FooImpl and then execute its getState() method.

Upvotes: 3

Tech Enthusiast
Tech Enthusiast

Reputation: 287

Highlight of code:

  • The code is using prototype scoped bean inside Singleton.
  • Then the bean proxy mode ScopedProxyMode.TARGET_CLASS is used.

The result is expected. why?

Upvotes: 0

Related Questions