TargetSource of SpringAOP source code analysis (4)

Foreword

In the Spring framework, TargetSource is an interface used to encapsulate the logic of obtaining the target object (that is, the proxied object). Its main function is to provide the target object used by the proxy object, and to allow the target object to be switched dynamically at runtime. TargetSource is very important in Spring’s AOP (aspect-oriented programming). It defines who the proxy object targets and when to obtain the target object.

Specifically, the TargetSource interface defines the following two methods:

  1. getTarget(): This method is used to obtain the target object. When the AOP proxy object executes a method, it will first call the getTarget() method to obtain the target object, and then execute the corresponding method on the target object.

  2. releaseTarget(Object target): This method is used to release the target object. In some special scenarios, it may be necessary to manually release the target object so that it can be garbage collected or cached for next use.

  3. isStatic() method: This method is used to indicate whether the target object is static. If the target object is static, TargetSource can determine the type of the proxy object when the proxy is created, allowing for better performance optimization. If the target object is dynamic (such as switching between different instances based on conditions at runtime), return false.

  4. getTargetClass() method: This method returns the class type of the proxy’s target object. When using an interface-based proxy (such as a JDK dynamic proxy), this method returns the type of the interface. When using a class-based proxy (such as a CGLIB proxy), this method returns the type of the target object. This information is typically used to determine the type of agent.

The role of TargetSource is reflected in the following aspects:

  • AOP Proxy: In AOP, TargetSource allows you to control who the target object of the proxy object is. For example, you can create a TargetSource implementation that selects different target objects based on specific conditions. This flexibility allows you to dynamically choose which aspect logic to apply on the proxy object.

  • Performance Optimization: Through TargetSource, you can implement some performance optimization techniques, such as Object Pooling. You can cache objects instead of creating new objects each time, thereby reducing object creation and destruction overhead.

  • Multiple data source switching: In some applications, it may be necessary to select different data sources according to different requests. Through a customized TargetSource, you can dynamically switch the target database according to the request.

  • State management and caching: TargetSource can be used for state management and caching strategies. You can add state management logic to the getTarget() method to ensure that the target object obtained is in a specific state each time. At the same time, caching logic can also be implemented here to cache frequently used objects to improve system performance.

Official TargetSource address

Default implementation of TargetSource in Spring

PrototypeTargetSource, SingletonTargetSource, SimpleBeanTargetSource, ThreadLocalTargetSource and CommonsPool2TargetSource are all TargetSource implementations in the Spring framework. They are suitable for different scenarios and have different characteristics and uses.

1. PrototypeTargetSource:

  • Role: PrototypeTargetSource is used for prototype Bean, and a new instance will be created every time it is requested.
  • Features: Suitable for target objects that need to create new instances on each request, maintaining state isolation.

2. SingletonTargetSource:

  • Function: SingletonTargetSource is used for singleton Beans. There is only one instance in the entire application life cycle.
  • Features: Suitable for state-independent, shareable targets.

3. SimpleBeanTargetSource:

  • Role: SimpleBeanTargetSource is used for simple Bean objects, usually those classes without interfaces or special requirements.
  • Features: Convert ordinary Java objects into proxy objects that Spring AOP can handle.

4. ThreadLocalTargetSource:

  • Function: ThreadLocalTargetSource saves the target object in ThreadLocal so that each thread has its own target object instance.
  • Features: Suitable for multi-threaded environments, each thread has an independent target object instance.

5. CommonsPool2TargetSource:

  • Use: CommonsPool2TargetSource uses Apache Commons Pool 2 to manage the pooling of target objects.
  • Features: The size and behavior of the object pool can be flexibly controlled, which is suitable for scenarios that require object pool management.

Summary of differences and usage scenarios:

  • PrototypeTargetSource and SingletonTargetSource are suitable for scenarios where a new instance needs to be created for each request and the same instance needs to be shared throughout the application life cycle, respectively.

  • SimpleBeanTargetSource is suitable for ordinary Java objects without interfaces and special requirements.

  • ThreadLocalTargetSource is suitable for multi-threaded environments where each thread needs to have an independent target object instance.

  • CommonsPool2TargetSource is suitable for scenarios that require object pool management. It can flexibly control the behavior of the object pool, such as the maximum pool size, minimum pool size, etc.

TargetSource used for common AOP proxy creation

In daily work, Spring AOP proxy beans are basically created using AutoProxyCreator#wrapIfNecessary().
The TargetSource it uses is SingletonTargetSource, which means that SingletonTargetSource#getTarget() returns the same object every time.

TargetSource#isStatic()

In Spring AOP, TargetSource can be divided into two categories: “static” and “dynamic”. The difference between the two is whether the target object of the proxy object is determined when the proxy is created (static) or dynamically determined at runtime (dynamic) .

1. Static TargetSource:

  • Target object certainty: For static TargetSource, the target object is already determined when the proxy object is created, and it will not change with time or specific conditions. Usually, the target object of a static TargetSource is a singleton, that is, there is only one instance in the entire application life cycle.
  • Function: Static TargetSource is suitable for target objects that share the same instance throughout the entire application. Such target objects are usually stateless and shareable, such as service classes, tool classes, etc. Using a static TargetSource can bring performance advantages because the proxy object only needs to determine the target object once when it is created.

2. Dynamic TargetSource:

  • Dynamism of the target object: After the proxy object is created, the dynamic TargetSource can dynamically change the target object of the proxy object at runtime. This change can be dynamically determined based on specific conditions, user permissions, request content, etc. The target object of a dynamic TargetSource can change with time, requests, or other factors.
  • Function: Dynamic TargetSource is suitable for scenarios where the target object needs to be determined based on specific conditions or dynamic situations. For example, when switching between multiple data sources, select different database connections based on user requests or business logic needs. A dynamic TargetSource allows the proxy’s target object to be dynamically selected at runtime.

Comparison of functions:

  • Static TargetSource is suitable for target objects that require only one instance during the entire application life cycle, such as stateless service classes.

  • Dynamic TargetSource is suitable for scenarios that require dynamic selection of target objects based on conditions, such as dynamic selection of different instances based on user permissions, request content, etc.

Choosing a static or dynamic TargetSource depends on your application needs. If your target object is stateless and shareable, using a static TargetSource can bring performance advantages. If your target object is stateful and needs to be dynamically switched based on conditions, using dynamic TargetSource can implement more flexible proxy target object selection logic.

In Spring AOP, CglibAopProxy and JdkDynamicAopProxy are two different AOP proxy methods, which use CGLIB (Code Generation Library) and JDK dynamic proxy technology to create proxy objects respectively. The two differ in how they handle the isStatic() method.

3. CglibAopProxy:

In CglibAopProxy, StaticUnadvisedExposedInterceptor and DynamicUnadvisedExposedInterceptor are two different interceptor classes. They are used to handle static proxies and dynamic proxies respectively.

StaticUnadvisedExposedInterceptor:

StaticUnadvisedExposedInterceptor is mainly used to handle static proxies based on interfaces. Its instances are used to intercept target objects that implement the interface. The constructor of this interceptor accepts a target object as a parameter, and this target object is determined when the interceptor is created. It is static and will not change at runtime.

new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget())

Here, this.advised.getTargetSource().getTarget() is a static target object, which is determined when the proxy is created and will not change dynamically during subsequent runs.

DynamicUnadvisedExposedInterceptor:

DynamicUnadvisedExposedInterceptor is used to handle proxy objects created by CGLIB or other dynamic proxy methods. The constructor of this interceptor accepts a TargetSource as a parameter, and TargetSource can dynamically determine the target object. The target object of DynamicUnadvisedExposedInterceptor is dynamic and may change at runtime based on conditions or other factors.

new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource())

Here, this.advised.getTargetSource() returns a TargetSource object, which may switch target objects at runtime based on specific conditions. Therefore, DynamicUnadvisedExposedInterceptor is used to handle dynamic proxies, where the target object may change at runtime.

4. JdkDynamicAopProxy:

Jdk’s isStatic processing is slightly different from cglib. Each time Jdk Proxy is called, it will obtain the target object through TargetSource#getTarget(). Finally, if isStatic is false, it will go to releaseTarget.