# 什么是动态代理

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

# SpringAOP中使用的动态代理技术-JDK动态代理和CGLIB动态代理

什么情况使用jdk哪种情况使用CGLIB

  • 如果要代理的对象有接口,则使用jdk动态代理--创建接口实现类代理对象,增强类的方法
  • 如果要代理的对象没有接口,则使用CGLIB--创建子类的代理对象。增强类的方法

# jdk动态代理示例

准备一个接口

public interface UserDao {
    public int add(int a,int b);
}

接口的实现类

public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
        System.out.println(a);
        return a + b;
    }
}

代理类

public class MyProxy implements InvocationHandler {
    //要代理的对象
    private Object object;

    public Object getObject() {
        return object;
    }

    public void setObject(Object object) {
        this.object = object;
    }

    //通过Proxy获取动态代理对象
    public Object getProxyInstance(){
        /**
         * 获取类加载器
         * 获取对应接口
         * this就是invocationHandler
         */
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理前");
        Object invoke = method.invoke(object, args);
        System.out.println("代理后");
        return invoke;

    }
}

测试

public class Test {
    public static void main(String[] args) {
        UserDaoImpl userDao=new UserDaoImpl();
        MyProxy myProxy=new MyProxy();

        myProxy.setObject(userDao);
        UserDao proxyInstance = (UserDao) myProxy.getProxyInstance();
        System.out.println(proxyInstance.add(1,2));


    }
}

# JDK动态代理详解

JDK的动态代理是基于Proxy类底层通过反射实现的。JDK动态代理是有JK提供的工具类Proxy实现的。动态代理类是在运行时生成指定接口的代理类,每个代理实例都有一个关联的调用处理程序的对象,此对象实现了invocationHandler,最终的业务逻辑是是在invocationHandler实现类的invoke方法.
Proxy类中核心的方法就是 通过newProxyInstance来生成动态代理对象

# 使用步骤

使用 JDK 动态代理的 5 大步骤如下

  1. 为动态代理类实现 InvocationHandler 接口并重写 invoke 方法,即为动态代理类关联一个自定义的 handler
  2. 通过 Proxy.getProxyClass 获得动态代理类。
  3. 通过反射机制获得代理类的构造方法,方法签名为 getConstructor(InvocationHandler.class)
  4. 通过构造函数获得代理对象,并将自定义的 InvocationHandler 实例对象作为参数传入。
  5. 通过代理对象调用目标方法。

Java 的 Proxy 类提供了一个 Proxy.newProxyInstance() 方法,封装了上面的第 2~4 步的工作(详情见下 Proxy 类的源码注释),故步骤可精简为 3 步

  1. 为动态代理类实现 InvocationHandler 接口并重写 invoke 方法,即为动态代理类关联一个自定义的 handler
  2. 使用 Proxy.newProxyInstance() 方法获得代理对象
  3. 通过代理对象调用目标方法。

通过源码可以看到newProxyInstance需要三个参数 类加载器 接口 和 InvocationHandler

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);
    // 这里就是获取处接口的所有方法 复制出来了一份等待处理
    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     */
    //得到了代理类的Class
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        //传入构造对象拿到代理类的构造器
        //从这里我们可以猜出这个代理类一个有一个构造方法是传入InvocationHandler进行初始化
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        //通过反射传入之前我们定义的那个HelloInvocationHandle,进行构造器实例化代理对象
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

InvocationHandler是一个接口,里面只有一个invoke方法。这个方法主要就是用来做业务上的增强的

//proxy-被代理的实例 method-要增强的方法 args-要增强的方法所需要的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

# Spring AOP和AspectJ是什么关系

  • 首先AspectJ是什么? AspectJ是一个java实现的AOP框架,它能够对java代码进行AOP编译(一般在编译期进行),让java代码具有AspectJ的AOP功能(当然需要特殊的编译器)

动态织入静态织入

  • 动态织入的方式是在运行时动态将要增强的代码织入到目标类中,这样往往是通过动态代理技术完成,如Java JDK的动态代理(Proxy,底层通过反射实现)或者CGLIB的动态代理(底层通过继承实现),SpringAOP采用的就是基于运行时增强的代理技术
  • AspectJ采用的就是静态织入的方式。AspectJ只要采用的是编译器织入,这个期间使用AspectJ的acj编译器(类似javac)把aspect类编译成class字节码后,在Java目标类编译时织入,即先编译aspect类在编译目标类

# Spring AOP and AspectJ AOP 有什么区别?AOP 有哪些实现方式?

AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以SpringAOP为代表

  • AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。
  • SpringAOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特点的切点做了增强处理,并回调原对象的方法