spring系列-Spring AOP原理&动态代理学习总结

一、动态代理的基础知识

1.1.代理模式及实现

1.1.1.代理模式

代理模式的意图
为其他对象提供一种代理以控制对这个对象的访问.

主要解决:
在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

代理模式实现:

在这里插入图片描述

1.1.2.静态代理

接口:

package com.source.asm.proxy;

public interface UserService {
    String buy();
}

被代理类,两种情况:

//普通用户
public class CommonUserServiceImpl implements UserService {
    @Override
    public String buy() {
        return "common user buy";
    }
}

//vip用户
public class VipUserServiceImpl implements UserService {
    @Override
    public String buy() {
        return "vip user buy";
    }
}

代理类:

package com.source.asm.proxy;

public class UserServiceProxy implements UserService{

    UserService userService;

    public UserServiceProxy(UserService userService) {
        this.userService = userService;
    }

    @Override
    public String buy() {
        beforeBuy();
        String res=userService.buy();
        System.out.println("购买结果:"+res);
        afterBuy();
        return res;
    }

    private void afterBuy() {
        System.out.println("结束购买后,归还购物车");
    }

    private void beforeBuy() {
        System.out.println("开始购买前,找到购物车");
    }
}

测试:

package com.source.asm.proxy;

public class ProxyTest {

    public static void main(String[] args) {
        CommonUserServiceImpl commonUserService = new CommonUserServiceImpl();
        UserServiceProxy userServiceProxy = new UserServiceProxy(commonUserService);
        userServiceProxy.buy();
        VipUserServiceImpl vipUserService = new VipUserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy(vipUserService);
        proxy.buy();
    }
}

开始购买前,找到购物车
购买结果:common user buy
结束购买后,归还购物车
开始购买前,找到购物车
购买结果:vip user buy
结束购买后,归还购物车

静态代理存在的问题:
违反了开闭原则,
1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。如上的代码是只为UserManager类的访问提供了代理,但是如果还要为其他类如Department类提供代理的话,就需要我们再次添加代理Department的代理类。

开闭原则,介绍:
(1)一个软件实体如类,模块和函数应该对扩展开放(对于提供方来说),对修改关闭(对于使用方来说)。用抽象构建框架,用实现扩展细节。

(2)当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

(3)编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。

1.1.3.动态代理

动态代理概念:
动态代理技术是指,在程序运行期间,创建目标对象的代理对象,并对目标对象的方法进行功能性增强.在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法.可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作.

动态代理的实现方案:

  • 基于JDK实现动态代理,通过jdk提供的工具方法Proxy.newProxyInstance动态构建全新的代理类(继承Proxy类,并持有InvocationHandler接口引用)字节码文件并实例化对象放回。
  • 基于CGlib动态代理模式
  • 基于Aspectj实现动态代理
  • 基于instrumentation实现动态代理

以上四个实质都是对class字节码进行操作,一下说下不同点。

技术时机不同点
jdk调用时,生成字节码文件,并加载类1、生成新的class文件 2、基于接口实现的方式生成代理类
cglibasm字节码1、基于继承被代理类生成代理子类,不用实现接口 2、生成新的class
aspectj修改目标类的字节,织入代理的字节,在程序编译的时候插入动态代理的字节码不生成新的class
instrumentation修改目标类的字节码,类装载的时候动态拦截去修改,基于javaagent不生成新的class

本文主要说明jdk和cglib.

1.1.Jdk动态代理

1.1.1.Jdk动态代理使用

目标代理接口:

public interface UserService 
    String userInfo(Long userId);
}

目标类:

@Service
public class UserServiceImpl implements UserService {

    @Override
    public String userInfo(Long userId) {
        return "id:"+userId+" info";
    }
}

代理逻辑:

public class JdkInvocationHandler implements InvocationHandler {

    private Object target;

    public JdkInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("proxy before");
        Object result = method.invoke(target,args);
        System.out.println("proxy after");
        return result;
    }
}

织入代理逻辑:

    public static void main(String[] args) {
        UserService service = new UserServiceImpl();
        UserService proxy =(UserService)  Proxy.newProxyInstance(service.getClass().getClassLoader(),
                new Class[]{UserService.class},
                new JdkInvocationHandler(service)
        );
        String result =proxy.userInfo(1L);
        System.out.println(result);
    }

//输出
proxy before
proxy after
result:id:1 info

1.1.2.Jdk动态代理的原理

查看生成的动态代理类

执行代码时加入以下代码,可以查看生成的动态代理类

System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

测试:

    public static void main(String[] args) {
        System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
        UserService service = new UserServiceImpl();
        UserService proxy =(UserService)  Proxy.newProxyInstance(service.getClass().getClassLoader(),
                new Class[]{UserService.class},
                new JdkInvocationHandler(service)
        );
        String result =proxy.userInfo(1L);
        System.out.println("result:"+result);
    }

动态类:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import com.spring.study.service.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements UserService {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String userInfo(Long var1) throws  {
        try {
            return (String)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    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("com.spring.study.service.UserService").getMethod("userInfo", Class.forName("java.lang.Long"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

动态代理类工作原理

在这里插入图片描述

Jdk 动态代理总结:

  1. 程序运行时动态生成代理对象
  2. 生成的代理对象继承java.lang.reflect.Proxy 类,该类持有了用户自定义的InvocationHandler,并且该InvocationHandler持有目标对象
  3. 代理对象实现用户给定接口,并实现这些接口的方法,当调用代理对象的这些方法时, 转交给java.lang.reflect.Proxy持有的InvocationHandler处理
  4. 最终在InvocationHandler.invoke方法中触发目标对象的执行.

这里不对Proxy.newProxyInstance方法进行分析,后面有时间出文章分析.

1.2.Cglib动态代理

1.2.1.Cglib动态代理使用

接口定义:

public interface UserService {
    String userInfo(Long userId);
}


public interface AopService {
    String aopInfo();
}

接口实现:

public class UserServiceImpl implements UserService , AopService {

    @Override
    public String userInfo(Long userId) {
        return "id:"+userId+" info";
    }

    @Override
    public String aopInfo() {
        return "aopInfo content";
    }
}

cglib拦截器:

public class CglibCallback implements MethodInterceptor {

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        System.out.println("intercept before");
        Object result =methodProxy.invokeSuper(proxy,args);
        System.out.println("intercept after");
        return result;
    }
}

代理织入:

public static void main(String[] args) {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,CglibProxy.class.getResource("").getPath());

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class);
        enhancer.setInterfaces(new Class[]{UserService.class, AopService.class});
        enhancer.setCallback(new CglibCallback());
        AopService aopService = (AopService) enhancer.create();
        String result = aopService.aopInfo();
        System.out.println(result);
        UserService userService = (UserService) enhancer.create();
        String userResult = userService.userInfo(1L);
        System.out.println(userResult);
    }

//输出
intercept before
intercept after
aopInfo content
intercept before
intercept after
id:1 info

1.2.2.Cglib动态代理原理

上面的的测试代码,加入了

 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,CglibProxy.class.getResource("").getPath());

运行后会把生成的代理类,输出到指定位置.

我们看下代理类的结构:

代理类会有三个,只关注第二个:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.spring.study.service.impl;

import com.spring.study.service.AopService;
import com.spring.study.service.UserService;
import java.lang.reflect.Method;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class UserServiceImpl$$EnhancerByCGLIB$$ed72fabc extends UserServiceImpl implements UserService, AopService, Factory {
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$aopInfo$0$Method;
    private static final MethodProxy CGLIB$aopInfo$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$userInfo$1$Method;
    private static final MethodProxy CGLIB$userInfo$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.spring.study.service.impl.UserServiceImpl$$EnhancerByCGLIB$$ed72fabc");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$2$Method = var10000[0];
        CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        CGLIB$toString$3$Method = var10000[1];
        CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        CGLIB$hashCode$4$Method = var10000[2];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = var10000[3];
        CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        var10000 = ReflectUtils.findMethods(new String[]{"aopInfo", "()Ljava/lang/String;", "userInfo", "(Ljava/lang/Long;)Ljava/lang/String;"}, (var1 = Class.forName("com.spring.study.service.impl.UserServiceImpl")).getDeclaredMethods());
        CGLIB$aopInfo$0$Method = var10000[0];
        CGLIB$aopInfo$0$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "aopInfo", "CGLIB$aopInfo$0");
        CGLIB$userInfo$1$Method = var10000[1];
        CGLIB$userInfo$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Long;)Ljava/lang/String;", "userInfo", "CGLIB$userInfo$1");
    }

    final String CGLIB$aopInfo$0() {
        return super.aopInfo();
    }

    public final String aopInfo() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (String)var10000.intercept(this, CGLIB$aopInfo$0$Method, CGLIB$emptyArgs, CGLIB$aopInfo$0$Proxy) : super.aopInfo();
    }

    final String CGLIB$userInfo$1(Long var1) {
        return super.userInfo(var1);
    }

    public final String userInfo(Long var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (String)var10000.intercept(this, CGLIB$userInfo$1$Method, new Object[]{var1}, CGLIB$userInfo$1$Proxy) : super.userInfo(var1);
    }

    final boolean CGLIB$equals$2(Object var1) {
        return super.equals(var1);
    }

    public final boolean equals(Object var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            Object var2 = var10000.intercept(this, CGLIB$equals$2$Method, new Object[]{var1}, CGLIB$equals$2$Proxy);
            return var2 == null ? false : (Boolean)var2;
        } else {
            return super.equals(var1);
        }
    }

    final String CGLIB$toString$3() {
        return super.toString();
    }

    public final String toString() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy) : super.toString();
    }

    final int CGLIB$hashCode$4() {
        return super.hashCode();
    }

    public final int hashCode() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            Object var1 = var10000.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);
            return var1 == null ? 0 : ((Number)var1).intValue();
        } else {
            return super.hashCode();
        }
    }

    final Object CGLIB$clone$5() throws CloneNotSupportedException {
        return super.clone();
    }

    protected final Object clone() throws CloneNotSupportedException {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? var10000.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy) : super.clone();
    }

    public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
        String var10000 = var0.toString();
        switch(var10000.hashCode()) {
        case -1690334309:
            if (var10000.equals("aopInfo()Ljava/lang/String;")) {
                return CGLIB$aopInfo$0$Proxy;
            }
            break;
        case -508378822:
            if (var10000.equals("clone()Ljava/lang/Object;")) {
                return CGLIB$clone$5$Proxy;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return CGLIB$equals$2$Proxy;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return CGLIB$toString$3$Proxy;
            }
            break;
        case 1919932421:
            if (var10000.equals("userInfo(Ljava/lang/Long;)Ljava/lang/String;")) {
                return CGLIB$userInfo$1$Proxy;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return CGLIB$hashCode$4$Proxy;
            }
        }

        return null;
    }

    public UserServiceImpl$$EnhancerByCGLIB$$ed72fabc() {
        CGLIB$BIND_CALLBACKS(this);
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
        CGLIB$THREAD_CALLBACKS.set(var0);
    }

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
        CGLIB$STATIC_CALLBACKS = var0;
    }

    private static final void CGLIB$BIND_CALLBACKS(Object var0) {
        UserServiceImpl$$EnhancerByCGLIB$$ed72fabc var1 = (UserServiceImpl$$EnhancerByCGLIB$$ed72fabc)var0;
        if (!var1.CGLIB$BOUND) {
            var1.CGLIB$BOUND = true;
            Object var10000 = CGLIB$THREAD_CALLBACKS.get();
            if (var10000 == null) {
                var10000 = CGLIB$STATIC_CALLBACKS;
                if (var10000 == null) {
                    return;
                }
            }

            var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
        }

    }

    public Object newInstance(Callback[] var1) {
        CGLIB$SET_THREAD_CALLBACKS(var1);
        UserServiceImpl$$EnhancerByCGLIB$$ed72fabc var10000 = new UserServiceImpl$$EnhancerByCGLIB$$ed72fabc();
        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Callback var1) {
        CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
        UserServiceImpl$$EnhancerByCGLIB$$ed72fabc var10000 = new UserServiceImpl$$EnhancerByCGLIB$$ed72fabc();
        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
        CGLIB$SET_THREAD_CALLBACKS(var3);
        UserServiceImpl$$EnhancerByCGLIB$$ed72fabc var10000 = new UserServiceImpl$$EnhancerByCGLIB$$ed72fabc;
        switch(var1.length) {
        case 0:
            var10000.<init>();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        default:
            throw new IllegalArgumentException("Constructor not found");
        }
    }

    public Callback getCallback(int var1) {
        CGLIB$BIND_CALLBACKS(this);
        MethodInterceptor var10000;
        switch(var1) {
        case 0:
            var10000 = this.CGLIB$CALLBACK_0;
            break;
        default:
            var10000 = null;
        }

        return var10000;
    }

    public void setCallback(int var1, Callback var2) {
        switch(var1) {
        case 0:
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
        default:
        }
    }

    public Callback[] getCallbacks() {
        CGLIB$BIND_CALLBACKS(this);
        return new Callback[]{this.CGLIB$CALLBACK_0};
    }

    public void setCallbacks(Callback[] var1) {
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
    }

    static {
        CGLIB$STATICHOOK1();
    }
}

通过class文件可以看出,生成的代理类结构如下:

在这里插入图片描述

对应目标类方法,例如:userInfo方法

 public final String userInfo(Long var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (String)var10000.intercept(this, CGLIB$userInfo$1$Method, new Object[]{var1}, CGLIB$userInfo$1$Proxy) : super.userInfo(var1);
    }


我们发现,重写方法,调用了我们设置的MethodInterceptor的intercept方法.

所以cglib的动态代理原理如下:

  1. 生成继承目标类的代理类

  2. 在代理类中重写目标类的public\protected方法,重写方法逻辑如下

    1. 如果设置了MethodInterceptor,则调用设置的MethodInterceptor
    2. 如果没有设置MethodInterceptor,直接调用父类对应方法,即目标类(不过好像必须设置MethodInterceptor,后面会出文章详细总结cglib的原理和使用)

MethodInterceptor的参数说明:

  @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        System.out.println("intercept:"+id+" before");
        Object result =methodProxy.invokeSuper(target,args);
        System.out.println("intercept after");
        return result;
    }
  1. target: 当前代理对象
  2. method: 当前之行的方法
  3. args: 执行方法的参数
  4. methodProxy: cglib中的方法代理类,如上面的例子
 Class var0 = Class.forName("com.spring.study.service.impl.UserServiceImpl$$EnhancerByCGLIB$$8ef1f312");
 Class var1;
var1 = Class.forName("com.spring.study.service.impl.UserServiceImpl") 
CGLIB$aopInfo$0$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "aopInfo", "CGLIB$aopInfo$0");

含义就是 当前代理类var0 中的方法CGLIB$aopInfo$0,对应了目标类var1 中的aopInfo方法

因为我们在MethodInterceptor中可能要触发目标对象方法的调用,所以MethodProxy提供了这种关系.

Object result =methodProxy.invokeSuper(target,args);

所以上面的invokeSuper调用才能直接调用父类(目标对象)的方法

二、Spring 对动态代理的封装-AOP

我们上面已经对动态代理有了基础的了解,下面我们来学习spring 中aop.

下面先介绍一下AOP的相关知识.

2.1.AOP基础知识

spring 关于AOP的官方地址:https://docs.spring.io/spring-framework/docs/4.3.25.RELEASE/spring-framework-reference/htmlsingle/#aop-introduction

AOP概念:

  1. Aspect : 切面,切面是对用于增强的方法按照一定理解和规则进行整理分类后的模块,其中包含了符合这一模块的概念和理解的用于增强的方法,你可以理解为一个功能集合,
  2. Join point: 连接点,是指程序运行中的一个时机,如方法调用前、方法调用后、异常发生时
  3. Advice : 增强.是在切面中某一个连接点的动作.spring中支持五种Advice,后面将详细说明这五种advice的作用和区别.
    1. before : 在某一个方法调用前,插入逻辑
    2. after : 在某一个方法调用后,插入逻辑,如果方法抛出异常,并且异常没有处理,将不会执行
    3. afterReturning : 在某一个方法返回之后执行,无论是否抛出异常
    4. afterThrowing: 在目标方法抛出异常,且未处理后,执行
    5. Around: 在方法调用前后执行
  4. PointCut: 切点, 是join point的一个子集,在这些join point地方将会加入对应的advice逻辑
  5. Introduction: 为目标代理对象,引入一个类型的接口,并且指定该接口的实现类,使得目标对象具有该接口的功能.
  6. TargetObject : 代理目标对象,即需要被增强的对象
  7. AOP Proxy : aop 代理对象,spring中可以通过jdk或cglib为目标对象创建代理
  8. Weaving: 织入,将代理对象和目标类进行关联.

2.2.通过自己实现的AOP来学习Spring AOP

2.2.1.Spring AOP 例子

先通过一个简单的例子,看下spring aop的使用.

定义目标接口和实现类:

//目标接口
package com.spring.study.service;
public interface AopService {
    String aopInfo(Long id,String name);

}
//实现类
package com.spring.study.service.impl;
@Service(value = ":aopService")
public class AopServiceImpl implements AopService {

    @Autowired
    private UserService userService;

    @Override
    public String aopInfo(Long id,String name) {
        System.out.println("exec aopInfo ,id:"+id+",name:"+name);
       return "aop info result";
    }
}

切面配置:


@Aspect
@Component
public class LogAspect {

    @Pointcut(value = "execution(* com.spring.study.service.*.*(..))")
    public void point() {
    }


    @Before(value = "point()")
    public void before(JoinPoint joinPoint) {
        System.out.println("before2");
    }


    @After(value = "point()")
    public void after(JoinPoint joinPoint) {

        System.out.println("after");


    }

    @AfterReturning(value = "point()",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result) {
        System.out.println("afterReturning,result:"+ JSON.toJSONString(result));
    }

    @AfterThrowing(value = "point()",throwing = "ex")
    public void afterThrowing(JoinPoint joinPoint,Throwable ex) {
        System.out.println("afterThrowing,ex:"+ex.getMessage());
    }


    @Around(value = "point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint){
        System.out.println("around before");
        Object obj = null;
        try {
            obj = proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("around after");


        return obj;
    }


}

测试main方法:

@Configuration
@DependsOn
@ComponentScan(basePackages = "com.spring.study")
@EnableAspectJAutoProxy
public class SpringApp {


    public static void main(String[] args) {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(SpringApp.class);
        context.refresh();
        AopService aopService = context.getBean(AopService.class);
        aopService.aopInfo(1L,"aop");
    }

}
//输出:
around before
before2
exec aopInfo ,id:1,name:aop
around after
after
afterReturning,result:"aop info result"

使用总结:

  1. 开启AOP功能: @EnableAspectJAutoProxy或者xml中配置
  2. 定义切面
    1. 定义切点
    2. 定义Advice

后面会详细说明这些用法.

2.2.2.Spring AOP的原理

我们将从下面的学习中回答以下几个问题?

  1. 选择创建AOP Proxy的时机
  2. 选择创建AOP Proxy的方式 jdk or cglib
  3. Advice织入的原理
  4. Advice使用说明
2.2.2.1.Spring Bean创建Proxy的时机

在这篇 spring系列-Spring Bean创建过程和循环依赖原理分析文章中,详细介绍了Spring Bean的创建流程.

其中有两个位置可以创建代理Bean:

  1. 接口InstantiationAwareBeanPostProcessor的实现类中通过postProcessBeforeInstantiation方法返回代理类Bean
  2. 接口BeanPostProcessor实现类中通过 postProcessAfterInitialization返回代理类

第一种方式,是为特殊Bean创建代理,该Bean不会执行后续的依赖注入、初始化操作流程.

第二种方式:spring实现AOP的方式,在这一步织入代理逻辑,而且Bean的所有属性依赖注入、初始化接口等已经完成.

2.2.2.2.Spring Bean创建代理的方式选择

Spring中负责织入AOP功能类是:AbstractAutoProxyCreator,

核心方法是:wrapIfNecessary

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// Create proxy if we have advice.
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

先给出一个结构图,在从结构图,来看每一部分:

在这里插入图片描述

wrapIfNecessary方法主要分为两部分:

  1. 查找当前类是否有对应的AOP配置
    1. 查找是否有满足的Advice
    2. 对该类符合的Advice进行排序,包装返回.(后续细节的地方会说,这里先记住)
  2. 如果找到该类对应的Advice,进行创建代理工作.
2.2.2.2.1.创建代理的流程

创建代理的流程入口在createProxy方法:

	
 ---------------------------------AbstractAutoProxyCreator---------------------------------

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}

		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		if (!proxyFactory.isProxyTargetClass()) {
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}

		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		return proxyFactory.getProxy(getProxyClassLoader());
	}

 ---------------------------------ProxyFactory---------------------------------
   //选择一个AopProxyFactory工厂进行代理创建
	public Object getProxy(@Nullable ClassLoader classLoader) {
		return createAopProxy().getProxy(classLoader);
	}


 ---------------------------------ProxyCreatorSupport---------------------------------
   	/**
	 * Create a new ProxyCreatorSupport instance.
	 */
	public ProxyCreatorSupport() {
		this.aopProxyFactory = new DefaultAopProxyFactory();
	}
	/**
	 * Create a new ProxyCreatorSupport instance.
	 * @param aopProxyFactory the AopProxyFactory to use
	 */
	public ProxyCreatorSupport(AopProxyFactory aopProxyFactory) {
		Assert.notNull(aopProxyFactory, "AopProxyFactory must not be null");
		this.aopProxyFactory = aopProxyFactory;
	}



	protected final synchronized AopProxy createAopProxy() {
		if (!this.active) {
			activate();
		}
		return getAopProxyFactory().createAopProxy(this);
	}
/**
	 * Return the AopProxyFactory that this ProxyConfig uses.
	 */
	public AopProxyFactory getAopProxyFactory() {
		return this.aopProxyFactory;
	}

 ---------------------------------DefaultAopProxyFactory---------------------------------

	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}



以上是相关代码,流程梳理:

  1. AbstractAutoProxyCreator.createProxy方法主要是完成创建代理所需相关配置的设置,然后交给ProxyFactory开始创建工作,
    1. 主要是相关的advice
  2. ProxyFactory: 从上面的结构图可以看出,ProxyFactory主要承担两部分责任:
    1. 持有当前Bean 的AOP配置信息
    2. 选择一个AopProxyFactory去生成AopProxy代理
  3. 在spring中AopProxyFactory的默认实现是DefaultAopProxyFactory,也就是在这个实现里面选择是通过jdk或是cglib去实现动态代理
    1. 选择jdk的条件
      1. @EnableAspectJAutoProxy(proxyTargetClass=false) : proxyTargetClass 为false
      2. optimize: 默认为false,不开启优化,
        1. 如果开启优化,有接口就使用jdk,没有就使用cglib
      3. 当前bean有除了SpringProxy以外的接口
    2. cglib
      1. 当前类没有实现接口
  4. AopProxyFactory会选择一种AopProxy实现,去进行动态代理.
2.2.2.2.2.Advice的使用说明

spring aop中支持5中advice:

  1. Before
  2. Around
  3. After
  4. AfterReturning
  5. AfterThrowing

那么有以下两个问题:

  1. 如果这五种advice都配置在了一个切点,那么它们的作用顺序是什么
  2. 如果一个切点定了两个相同的Advice,那么调用顺序是什么?(尽管我们不会这样定义)
Advice的作用顺序

直接说结论了,在spring aop中这五种advice的作用顺序如下.

如果不发生异常,或则发生异常被catch处理了:

around ->before->目标类方法执行->around ->afterRunning->after

如果抛出异常:

  1. before前抛出异常: around->afterThrowing
  2. before抛出异常: around->before(未结束)->afterThrowing
  3. 目标方法抛出异常:around->before->目标方法(未结束)->afterThrowing
  4. 目标方法后,around结束前抛出异常:around->before->目标方法->around(未结束)->afterThrowing

实际在spring中,各个advice的执行流程如下面的伪代码:

try{

  {//around 方法范围
    //around 前半部分逻辑
    {
      {//before 方法作用范围
      }
      {//代理的目标类方法 方法作用范围
      }
    }
    //around 后半部分逻辑
  }
}catch(Throwable t){
  //AfterThrowing 方法
}finally{
  //after方法
}
{
    //AfterRunning方法
}
  
多个相同Advice排序

在spring中如果对同一个切点,如果定义了多个相同类型的advice那么会按照advice的方法名进行字符串比较排序,执行对应的逻辑.

上面的例子,再添加一个before类型的advice.

   @Before(value = "point()")
    public void before2(JoinPoint joinPoint) {
        System.out.println("before2");
    }

    @Before(value = "point()")
    public void before1(JoinPoint joinPoint) {
        System.out.println("before1");
    }

//执行结果:
before1
before2

2.2.3.以JdkDynamicAopProxy探寻spring AOP如何封装

我们再次贴一次Spring Advice实现的伪代码:

try{

  {//around 方法范围
    //around 前半部分逻辑
    {
      {//before 方法作用范围
      }
      {//代理的目标类方法 方法作用范围
      }
    }
    //around 后半部分逻辑
  }
}catch(Throwable t){
  //AfterThrowing 方法
}finally{
  //after方法
}
{
    //AfterRunning方法
}


Spring AOP 的Advice是配置的、可插拔的,每一个Advice只专注自己的处理内容,自己处理完成就继续处理下一层的Advice.

那么这种编程模式,很容易联想到可以责任链模式,spring aop对Advice的织入就是通过责任链模式


public interface XMethodInvocation {


    Object proceed() throws  Throwable;
}


public class XReflectiveMethodInvocation implements XMethodInvocation{
  
  //advice包装后的拦截器
  private List<MethodInterceptor> interceptors;
  //当前执行到第几个拦截器
  private int currentIndex = -1;
  //目标代理类
  private Object target;
  //当前需要执行的目标代理类的method
  private Method method;
   //当前需要执行的目标代理类方法的参数
  private Object[] args;
  
  public Object proceed(){
    while(currentIndex == interceptors.size()-1){
      //通过反射执行目标类方法
    }else{
      return interceptors.get(++currentIndex).invoke(this);
    }
  }

}


public interface MethodInterceptor {


    Object invoke(XMethodInvocation invocation) throws Throwable;
}

public xxAdvice implements  MethodInterceptor{
  
  Object invoke(XMethodInvocation invocation) throws Throwable{
    //插入对应逻辑
    Object result invocation.proceed();
    return result;
  }
}

其中xxAdvice实现伪代码如下:

before:

 @Override
    public Object invoke(XMethodInvocation invocation) throws Throwable {
        //前置增强逻辑
        before();
        Object result=invocation.proceed();
        return result;
    }

around:

 @Override
    public Object invoke(XMethodInvocation invocation) throws Throwable {
        XDefaultProceedingJoinPoint proceedingJoinPoint = new XDefaultProceedingJoinPoint(invocation);
        return invokeAdivceMethod(proceedingJoinPoint);
    }

After:

 @Override
    public Object invoke(XMethodInvocation invocation) throws Throwable {
        try{
            Object result=invocation.proceed();
            return result;
        }finally {
           // after增强逻辑
            after();
        }

    }

afterThrowing:

 @Override
    public Object invoke(XMethodInvocation invocation) throws Throwable {
        try{
            Object result=invocation.proceed();
            return result;
        }catch (Throwable t){
          //afterThrowing增强逻辑
            afterThrowing();
            throw t;
        }
    }

afterReturning:

 @Override
    public Object invoke(XMethodInvocation invocation) throws Throwable {
        Object result=invocation.proceed();
        //afterReturning增强逻辑
        afterReturning();
        return result;
    }

其中around逻辑有点特殊,因为around逻辑里面可能包含以下逻辑:

    @Around(value = "point()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        
        System.out.println("around before");
        Object obj = null;
        obj = proceedingJoinPoint.proceed();
        System.out.println("around after");
        return obj;
    }

先执行一部分around逻辑,接着调用内层逻辑,内层逻辑可以是before增强,也可能是目标类的方法,但是这块代码不能靠框架来完成,所以spring aop中定义了一个特殊的JoinPoint:ProceedingJoinPoint

public interface ProceedingJoinPoint extends JoinPoint {

    void set$AroundClosure(AroundClosure arc);

    public Object proceed() throws Throwable;

 
    public Object proceed(Object[] args) throws Throwable;

}

它主要的作用是触发调用继续下去,调用内部的advice增强逻辑或目标类逻辑.

当然构造ProceedingJoinPoint子类的时候,要把around增强调用时的invocation传入到ProceedingJoinPoint中.

如果你看到这里还是不明白,那么我把上面的代码合并以下:

spring aop在创建代理时,会对advice进行排序,也就是上面伪代码中的interceptors,加入现在用户配置了5中增强,那么interceptors中元素0-4分别为:

AfterReturning(MethodInterceptor实现类)-> AfterThrowing(MethodInterceptor实现类)-> After(MethodInterceptor实现类)-> Around(MethodInterceptor实现类)-> Before(MethodInterceptor实现类),

按照XReflectiveMethodInvocation中proceed中的执行流程,我们来拼接以下代码:

第一次调用:

触发:AfterReturning

currentIndex = 0

invocation.procced()
//织入代码afterReturning相关逻辑
afterReturning()

第二次调用:

触发:AfterThrowing

currentIndex = 1


try{
  invocation.procced()
}catch(Throwable t){
    // 2..织入代码afterThrowing相关逻辑
    afterThrowing();
   throw t;
}
//1.织入代码afterReturning相关逻辑
afterReturning()

第三次调用:

触发:After

currentIndex = 2

try{
  
    try{
            invocation.procced()
        }finally {
            //3.织入代码after相关逻辑
            after();
        }
}catch(Throwable t){
    // 2..织入代码afterThrowing相关逻辑
    afterThrowing();
   throw t;
}
//1.织入代码afterReturning相关逻辑
afterReturning()

第四次调用:

触发:Around

currentIndex = 3

try{
  
    try{
            //4.触发around 增强逻辑
            {
                  System.out.println("around before");
                  Object obj = null;
                  //通过proceedingJoinPoint继续织入
                  obj = proceedingJoinPoint.proceed();
                  System.out.println("around after");
            }
        }finally {
            //3.织入代码after相关逻辑
            after();
        }
}catch(Throwable t){
    // 2..织入代码afterThrowing相关逻辑
    afterThrowing();
   throw t;
}
//1.织入代码afterReturning相关逻辑
afterReturning()

第五次调用:

触发:Before

currentIndex = 4

try{
  
    try{
            //4.触发around 增强逻辑
            {
                  System.out.println("around before");
                  Object obj = null;
                  //通过proceedingJoinPoint继续织入
                    {
                      //5..织入代码after相关逻辑织入before代码
                       before();
                       invocation.procced();
                    }
                  System.out.println("around after");
            }
        }finally {
            //3.织入代码after相关逻辑
            after();
        }
}catch(Throwable t){
    // 2..织入代码afterThrowing相关逻辑
    afterThrowing();
   throw t;
}
//1.织入代码afterReturning相关逻辑
afterReturning()

第六次次调用:

触发目标类方法调用

try{
  
    try{
            //4.触发around 增强逻辑
            {
                  System.out.println("around before");
                  Object obj = null;
                  //通过proceedingJoinPoint继续织入
                    {
                      //5..织入代码after相关逻辑织入before代码
                       before();
                       //6.执行目标类,然后结果一层一层返回
                       method.invoke(target,args);
                    }
                  System.out.println("around after");
            }
        }finally {
            //3.织入代码after相关逻辑
            after();
        }
}catch(Throwable t){
    // 2..织入代码afterThrowing相关逻辑
    afterThrowing();
   throw t;
}
//1.织入代码afterReturning相关逻辑
afterReturning()

到此,Spring AOP的原理就到此结束了,本篇没有详细梳理Spring AOP的源码,只是对其原理做了一下简单介绍.后面会出文章,分析Spring AOP的源码,学习里面的设计思想.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿老徐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值