Trang chủ > Uncategorized > Some facts about Spring’s scoped proxy

Some facts about Spring’s scoped proxy

I just want to give some more details about some aspects of Spring’s scoped proxy. Please take a look at Spring reference first.

Proxy is not always necessary

Spring creates proxies only when it needs to attach behaviors to your methods, for example transactional management, or your own AOP advices. If a class uses Spring only for dependency injection, it will not be proxied. Also, in this case, you can wire a property whose type is a concrete class instead of an interface. The following code works, without any help from JDK proxy or CGLIB proxy:

@Autowired Interface property;
@Autowired InterfaceImplement property;

Proxy can be avoided using byte code weaving, but it is not covered here.

How Spring creates scoped proxy

A scoped proxy can be created using JDK dynamic proxy or CGLIB.

Let’s say we have the interface Vehicle and it’s implementation—class Motor.

interface Vehicle { 
    void start();
}
class Motor implements Vehicle {
    void start() { }
}

Using JDK dynamic proxy

Spring will create a class, say JdkProxyBasedVehicle that also implements Vehicle.

class JdkProxyBasedVehicle implements Vehicle {
    void start() {
        Vehicle target = getBeanOfTypeMotor();
        target.start();
    }
}

In this case JdkProxyBasedVehicle is not assignable from Motor. Once the class Motor is proxied this way, the following code will not work:

@Autowired Motor motor;

Using CGLIB

Spring will create a subclass of Motor, say CglibBasedVehicle.

class CglibBasedVehicle extends Motor {
    CglibBasedVehicle() {
        super();
    }
    void start() {
        Vehicle target = getBeanOfTypeMotor();
        target.start();
     }
}

As a consequence, there are some side effects:

  • CglibBasedVehicle is assignable from Motor, and this code will work:
    @Autowired Motor motor;
  • The method start() can be overridden only when it is not marked as final.
  • The constructor of Motor will be executed twice, by getBeanOfTypeMotor() and CglibBasedVehicle’s constructor. CglibBasedVehicle’s constructor must delegate to Motor’s constructor because it extends Motor, so its internal states must also be initialized consistently like the parent class. The class Motor might has some final methods, and these methods would need to access these internal states when being called from a CglibBasedVehicle instance.

Note that regardless of how Spring create scoped proxy, it is the target (the bean of type Motor) that gets injected with dependencies, not the proxy instance. Therefore, inside CglibBasedVehicle instances all @Autowired properties inherited from Motor will not be injected. Later in this post we will see why it might get you into a trouble.

The word "subclass" might make you wonder why Spring does not inject directly into CglibBasedVehicle instances, and use these instances as Motor beans.

class CglibBasedVehicle extends Motor {
    void start() {
        super.start(); // actually this method is not overridden.
    }
}

Looks good. The constructor will not be executed twice. But then scoped proxies would be useless.

What scoped proxy does?

It helps wire differently-scoped beans together. A typical usage is to inject a shorter-lived scoped bean (for example a request scoped bean) to a longer-lived scoped bean (for example a singleton scoped bean). In the above example, the magic is inside the method getBeanOfTypeMotor(). Based on your configuration, the proxy will decide whether to look up the bean inside the Spring container, or create a fresh Motor instance.

A small idea with scoped proxy

I do not have any chance to verify, but in Spring it should be possible to build a custom scope to add thread safety to non thread-safe components. In our example, if the class Motor is not thread-safe, getBeanOfTypeMotor() can return different beans for different threads.

Be aware of using final method and dependency injection with CGLIB based proxies

So let’s tailor our previous example.

interface Vehicle { 
    void start();
}
class Motor implements Vehicle {
    @Autowired Engine engine;
    void start() {
        engine.start();
    }
}
class CglibBasedVehicle extends Motor {
    // engine is null, because dependency injection is not done inside proxy
    void start() {
        Vehicle target = getBeanOfTypeMotor();
        target.start();
    }
}

The proxy’s start() works fine because target.engine is injected. But what will happen if start() is marked as final?

class Motor implements Vehicle {
    @Autowired Engine engine;
    final void start() {
        engine.start();
    }
}
class CglibBasedVehicle extends Motor {
    // engine is null, because dependency injection is not done inside proxy
    // cannot overridden start();
}

In this case, the proxy’s start() will act the same way as Motor, but its field engine is null, and we have a NullPointerException.

Since the overhead with both JDK proxy-based and CGLIB-based approaches is not a problem (http://blog.springsource.org/2007/07/19/debunking-myths-proxies-impact-performance/), and interfaces help inject mocks and stubs easily, I think if we use Spring we should use interfaces exclusively. For well-structured codebases it is truly an urban myth that interfaces make code bloat.

Tags:
  1. Chưa có phản hồi.
  1. No trackbacks yet.

Gửi phản hồi

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s

%d bloggers like this: