Spring - AOP

Spring - AOP

In the chapter on how to achieve AOP (on) introduced

AOP
The reason for the emergence of technology and some important concepts, it is necessary to understand before we can implement it ourselves
AOP
How the bottom layer works, so let s take a look at this article
AOP
Some core basic technologies that the realization depends on.
AOP
Is to use
Dynamic proxy
with
Bytecode generation technology
To achieve this, a proxy object is generated for the target object at runtime (note: not at compile time!), and then the crosscutting logic is woven into the generated proxy object. Finally, the system uses the proxy object with crosscutting logic. Instead of being a proxy object, the proxy object is forwarded to the proxy object.

Agency model

The root of the dynamic proxy is the proxy mode in the design pattern . The description of the proxy mode in GoF is as follows:

Provide a surrogate or placeholder for another object to control access to it.

It can be seen from its definition that the proxy mode is mainly to control the access of the object, and usually also has all the functions of the proxy. Through the proxy mode, we can add some functions to the proxy class by introducing the proxy class without changing the proxy class. At this time, a famous sentence in the computer science community floats in our minds (PS basic knowledge is very important, you can see this ):

Any problem in computer science can be solved by adding an intermediate layer.

In fact, the agency model is often encountered in real life. For example, when renting a house in first-tier cities, most of them are renting agencies to look at the house, negotiate the price, and sign the contract, because the landlord of the house has fully entrusted the house to it. The intermediary dealt with it. The renting intermediary here is actually acting as a solution to the agency object in the agency model, and the real agent (target object) is actually the landlord of the house, and we are all renting intermediaries (agent objects) when dealing with us. The structure of the proxy model class diagram is shown in the following figure:

The meaning of each part in the figure is as follows:

  • ISubject is an abstraction of the capabilities provided by the proxy object.
  • SubjectImpl is the specific implementation class of the agent.
  • SubjectProxy is the proxy implementation class, usually this class holds
    ISubject
    An instance of the interface.
  • Client is the abstract role of the visitor, to visit
    ISubject
    Type of resource.

can be seen

SubjectImpl
with
SubjectProxy
All implement the same interface
ISubject
, In the proxy object
SubjectProxy
Hold within
ISubject
References when
Client
access
doOperation()
When the proxy object forwards the request to the proxy object, from the perspective of this process alone, if the proxy object is only to forward the request, is it a bit redundant? Consider again with the definition of the proxy mode, can't you add some access control before forwarding (after the latter)?

In the proxy object (

SubjectProxy
) Forward the request to the proxy object (
SubejctImpl
) Before or after, some processing logic is added as needed, without the need to modify the specific implementation logic of the proxy object, assuming
SubjectImpl
Is in our system
Joinpoint
Where the object is, at this time
SubjectImpl
This is our target object. We only need to create a proxy object for this target object, and then add the cross-cutting logic to the proxy object, and expose the created proxy object to the outside, and the cross-cutting logic can be integrated with the original logic. Together.

Dynamic proxy

So far, everything is so beautiful. When only adding crosscutting logic to the same target object type, you only need to create a proxy object, but in

Joinpoint
When the same but different target object types, you need to create a separate proxy object for each different target object type, and the cross-cutting logic of these proxy objects is actually the same. According to the DRY principle , you need to find another technology to solve it. this problem. in
JDK 1.3
Introduced
Dynamic proxy mechanism
You can specify the interface in
Runtime
To dynamically generate proxy objects, use this
Dynamic proxy mechanism
Can solve the above problems, so that we can not create a proxy class for each original class in advance, but at runtime
Dynamically generated
Agent class. in
Java
In, the use of dynamic proxy is relatively simple, it has used reflection to achieve the syntax of dynamic proxy, mainly by a class
Proxy
And an interface
InvocationHandler
The composition, the use of dynamic proxy mechanism to achieve the previous example is as follows:

/** * @author mghio * @Since 2021-05-29 */ public interface Subject { void doOperation () ; } public class SubjectImpl implements Subject { @Override public void doOperation () { System.out.println( "SubjectImpl doOperation..." ); } } public class JDKInvocationHandler implements InvocationHandler { private final Subject target; public JDKInvocationHandler(Subject target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //add pre process logic if necessary System.out.println("Proxy before JDKInvocationHandler doOperation..."); Object result = method.invoke(target, args); System.out.println("Proxy after JDKInvocationHandler doOperation..."); //add post process logic if necessary return result; } } public class Client { public static void main (String[] args) { //Save the generated proxy class in the root directory (com/sun/proxy/XXX.class) System.setProperty( "sun.misc.ProxyGenerator.saveGeneratedFiles " , "true" ); Subject target = new SubjectImpl(); Subject proxy = (Subject) Proxy.newProxyInstance(SubjectImpl.class.getClassLoader(), new Class[]{Subject.class}, new JDKInvocationHandler(target)); proxy.doOperation(); } } Copy code

As can be seen from the above code, use

JDK
The dynamic agent only needs 3 steps:

  1. Define the public interface
    Subject
    To create a proxy object
    SubjectImpl
  2. Create the processing object of the proxied object
    JDKInvocationHandler
    , Holding the target audience
    Subject
    Reference
  3. use
    JDK
    of
    Proxy
    Static method of class
    newProxyInstance
    Create proxy object

By setting

sun.misc.ProxyGenerator.saveGeneratedFiles
Properties, you can save the dynamically generated proxy class in the project root directory. The proxy class generated by running the above sample code is as follows:

public final class $ Proxy0 extends Proxy implements Subject { private static Method m1; private static Method m2; private static Method m3; private static Method m0; static { try { m1 = Class.forName( "java.lang.Object" ).getMethod( "equals" , Class.forName( "java.lang.Object" )); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("cn.mghio.designpattern.proxy.dynamicproxy.Subject").getMethod("doOperation"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } public $Proxy0(InvocationHandler var1) throws { super (var1); } public final void doOperation () throws { try { super .h.invoke( this , m3, (Object[]) null ); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } //omit toString, equals, hashCode method... } Copy code

As can be seen from the code of the dynamically generated proxy class,

JDK
The proxy class generated by the dynamic proxy is inherited
JDK
Provided in
Proxy
And implement the interface implemented by the proxy class. Further two points can be drawn from this implementation: 1. Use
JDK
Why can only be used in dynamic proxy
The interface reference points to the proxy
, And cannot use the proxy specific class reference to point to the proxy; 2. The proxy class must implement the interface, because
JDK
The proxy class generated by the dynamic proxy must inherit from
Proxy
,and
Java
Multiple inheritance is not supported, so it can only be achieved through the interface.

By default,

Spring AOP
Found that the target object implements the interface, will use
JDK
The dynamic proxy mechanism dynamically generates proxy objects for it, although it is advocated
Interface-oriented programming
, But there are also scenarios where the target object does not implement the interface. When the target object being proxied does not implement the interface, it cannot be used
JDK
Dynamic proxy, then in this case you need to use a third-party tool to help.

Bytecode generation technology

When the target object does not implement the interface, you can inherit the target object through dynamic bytecode generation to dynamically generate the corresponding subclass, in the generated subclass

Rewrite
The behavior of the parent class target object, and then the crosscutting logic is placed in the subclass, and the subclass of the target object is used in the system. The final effect is that the proxy mode is the same, and the CGLIB dynamic bytecode generation class library (it is actually also An abstraction layer, the lower layer is ASM ) can dynamically generate and modify the bytecode of a class. When the target object of the above sample code does not implement the interface, modify it to
CGLIB
The way to dynamically generate bytecode is implemented as follows:

/** * @author mghio * @Since 2021-05-29 */ public class RealSubject { public void doOperation () { System.out.println( "RealSubject doOperation..." ); } } public class CglibMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //add pre process logic if necessary System.out.println("Cglib before RealSubject doOperation..."); Object result = methodProxy.invokeSuper(o, args); System.out.println("Cglib after RealSubject doOperation..."); //add post process logic if necessary return result; } } public class Client { public static void main(String[] args) { // System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/mghio/IdeaProjects/designpattern/cglib/proxy/"); //1. Enhancer Enhancer enhancer = new Enhancer(); //2. enhancer.setSuperclass(RealSubject.class); //3. ( MethodInterceptor ) enhancer.setCallback(new CglibMethodInterceptor()); //4. RealSubject proxy = (RealSubject) enhancer.create(); proxy.doOperation(); } }

CGLIB
4

  1. Enhancer
  2. ( )
    final
  3. (
    MethodInterceptor
    )
  4. Enhaner
    create()

DebuggingClassWriter.DEBUG_LOCATION_PROPERTY

CGLIB
RealSubject
Factory
final
JDK
CGLIB
final
final

Spring AOP
AOP

JDK CGLIB
JDK
Proxy
CGLIB
Factory
final
final
final
JDK