Java AOP实现原理

AOP是什么

AOP(Aspect Oriented Programming)是面向切面编程,OOP是面向对象编程,AOP是在OOP基础之上的一种更高级的设计思想。

OOP侧重于对象的提取和封装。----封装对象

AOP侧重于方面组件,方面组件可以理解成封装了通用功能的组件,方面组件可以通过配置方式,灵活地切入到某一批目标对象方法上。----封装功能

AOP用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等。

AOP代理

静态代理

AOP实现的关键,在于AOP框架自动创建的AOP代理类,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表。
所谓静态代理也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。因此也称为编译时增强!

静态代理优缺点:

优点:静态代理的优点是代理模式的共同优点,即与业务解耦,避免代码侵入,业务类只关注自己的业务逻辑,代理类实现额外的功能。

缺点:不够灵活也不够方便

具体来讲:

  1. 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
  2. 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
  3. 代理类必须事先存在,一个真实类必须对应一个代理类,在实际企业级开发中,如果大量使用会导致类的急剧膨胀。

静态代理的这些缺点都可以通过动态代理来解决,动态代理可以可以代理实现任何接口的对象的任何方法,下面我们将详细讲解。

动态代理

Spring AOP使用的就是动态代理。所谓的动态代理,就是说AOP框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法

Spring AOP中的动态代理,主要有两种方式:JDK动态代理和CGLIB动态代理。

JDK动态代理通过“反射”来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。
CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态地生成某个类的子类。注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

JDK动态代理

使用JDK动态代理的两个重要步骤:
1.通过实现 InvocationHandler 接口创建自己的调用处理器

2.通过为Proxy类的newProxyInstance方法指定代理类的ClassLoader 对象和代理要实现的interface以及调用处理器InvocationHandler对象 来创建动态代理类的对象

JDK动态代理例子如下:

public interface StockTrading {
    void buy();
    default void buy(int param) {
    }
    void sell();
}
public class Person implements StockTrading {
    @Override
    public void buy(int param) {
        System.out.println(param);
    }
    @Override
    public void buy() {
        System.out.println("person buy stock");
    }
    @Override
    public void sell() {
        System.out.println("person sell stock");
    }
}
/**
handler必须实现InvocationHandler
*/
public class StockTradingInvocationHandler implements InvocationHandler {
    private Object delegate;
    public StockTradingInvocationHandler(Object delegate) {
        this.delegate = delegate;
    }
    /**
     * @param proxy 代理类的实例
     * @param method 被调用的方法对象
     * @param args 调用method的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("method start----------");
        Long start = System.currentTimeMillis();
        Object result = method.invoke(delegate, args);
        FeeUtil.fee();
        System.out.println("method end----------");
        return null;
    }
}

public class DynamicMain {
    public static void main(String[] args) {
        StockTrading delegate = new Person();
        InvocationHandler handler = new StockTradingInvocationHandler(delegate);
        //为指定类装载器、一组接口及调用处理器生成动态代理类实例
        StockTrading proxy = (StockTrading) Proxy.newProxyInstance(
                delegate.getClass().getClassLoader(),//代理类的ClassLoader加载器
                delegate.getClass().getInterfaces(),//代理类要实现的接口
                handler//表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
        );
        System.out.println(proxy.getClass().getName());
        proxy.sell();
    }
}
//执行结果 
com.sun.proxy.$Proxy0
method start----------
person sell stock
Charge a fee
method end----------

上面代码,客户端需要自己去实现代理类,显得比较繁琐,,我们可以将代理对象的创建封装到代理协调器的实现中。做如下优化:

public class StockTradingInvocationHandler2 implements InvocationHandler {
    private Object delegate;
    public Object bind(Object delegate) {
        this.delegate = delegate;
        return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), 
                delegate.getClass().getInterfaces(), 
                this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(proxy.getClass().getName());
        System.out.println(method.getName() + " method start----------");
        Long start = System.currentTimeMillis();
        Object result = method.invoke(delegate, args);
        FeeUtil.fee();
        System.out.println(method.getName() + " method end----------");
        return null;
    }

}
public class DynamicMain2 {
    public static void main(String[] args) {
        StockTrading delegate = new Person();
        StockTrading proxy = (StockTrading) new StockTradingInvocationHandler2().bind(delegate);
        proxy.buy();
        proxy.sell();

    }
}
//执行结果:
com.sun.proxy.$Proxy0
buy method start----------
person buy stock
Charge a fee
buy method end----------
com.sun.proxy.$Proxy0
sell method start----------
person sell stock
Charge a fee
sell method end----------

到目前为止,我们只列出了实际例子说明JDK动态代理该怎么使用,至于JDK是怎么实现的,我们还是不清楚,主要有两个疑惑:

(1) InvocationHandler的invoke方法是由谁来调用的。
(2) Proxy.newProxyInstance到底做了什么,代理类是怎么生成的;下面我们来弄明白这两个问题。

源码分析

进入ProxyClassFactory

    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        ......
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);//这里就是动态生成代理对象的最关键的地方
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);//根据代理类的字节码生成代理类的实例  
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

 ProxyGenerator.generateProxyClass是真正生成代理类字节码的地方。 到目前为止,我们知道了代理类是通过生成字节码反射得到的,这就解决了第(2)个问题。对于第(1)个问题,我们通过反编译看一下$Proxy0的内容:

public final class $Proxy0 extends Proxy implements StockTrading {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m0;
    public $Proxy0(InvocationHandler var1) throws  {//构造参数是InvocationHandler的实例
        super(var1);
    }
......
    public final void buy() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);//这里实际是调用StockTradingInvocationHandler2的invoke方法
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    public final void sell() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);//这里实际是调用StockTradingInvocationHandler2的invoke方法
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("com.prist.demo.proxy.StockTrading").getMethod("buy", new Class[0]);
            m4 = Class.forName("com.prist.demo.proxy.StockTrading").getMethod("sell", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

代理类调用了InvocationHandler的invoke方法。

这里我们看到JDK动态代理生成的代理类$Proxy0本身已经extends了Proxy,而java不允许多重继承,另一方面,被代理类必须要实现接口才能使用JDK的动态代理,对于没有实现接口的代理类,或者为了更好的性能(JDK动态代理毕竟采用反射,会有一定代价),CGLIB是一个很好的选择。

CGLIB

实现原理

CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比反射效率要高。但是需要主要注意的,CGLib不能对声明为final的方法进行代理。因为CGLib原理是动态的生成被代理类的子类。ASM能够通过改造既有类,直接生成需要的代码。增强的代码是硬编码在新生成的类文件内部的,没有反射带来性能上的付出。它是一个普通的 Java 类而不是 Proxy类(JDK的动态代理类是一个Proxy类),甚至可以在应用程序的类框架中拥有自己的位置,派生自己的子类。

我们还是用上面的case来举例

public class CglibDynamicProxy implements MethodInterceptor {
    private Object target;
    /**
     * 创建代理对象
     * @param target 被代理的对象
     * @return
     */
    public Object getProxyInstance(Object target){
        this.target = target;
        // 声明增强类实例
        Enhancer enhancer = new Enhancer();
        // 设置被代理类字节码,CGLIB根据字节码生成被代理类的子类
        enhancer.setSuperclass(this.target.getClass());
        // 设置要代理的拦截器,回调函数,即一个方法拦截   new MethodInterceptor()
        enhancer.setCallback(this);
        // 创建代理对象 实例
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy)
 throws Throwable {
        System.out.println("代理方法执行前操作----");
        methodProxy.invokeSuper(o, args);
        System.out.println("代理方法执行后操作----");
        return null;
    }
}
public class CglibClient {
    public static void main(String[] args) {
        CglibDynamicProxy cglib = new CglibDynamicProxy();
        Person realSubject = (Person) cglib.getProxyInstance(new Person());
        realSubject.buy();
    }
}
//执行结果
代理方法执行前操作----
person buy stock
代理方法执行后操作----</code>
0 0 投票数
Article Rating
订阅评论
提醒
guest
0 评论
最旧
最新 最多投票
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x