AOP 技术原理——代理模式全面总结

前言

很是重要的一个设计模式,也很常见,不少框架都有它的影子。定义就很少说了。两点:html

一、为其它对象提供一个代理服务,间接控制对这个对象的访问,联想 Spring 事务机制,在合适的方法上加个 transaction 注解,就分分钟实现了事务。java

二、除了1,代理对象还能充当中介的角色。算法

为何要有代理模式?

若是但愿不给原有对象附加太多的责任(和本对象无关的冗余代码),可是还想能为其实现新功能,那么代理模式就是作这个的。仍是联系 Spring 事务机制,很好的应用场景。数据库

实际生活里,能够联系租房,你们租房通常都会找中介,这个中介就是代理对象的角色,它解耦(分离)了房东的一部分责任,由于房东太忙了,或者房东不屑于作这些事情,故交给代理对象去作。设计模式

一句话:解耦合,提升扩展性数组

静态代理模式

顾名思义,代理类被显示的指定了,即代理在代码里被写死了。实现最简单,也不多用,可是能帮助快速理解思想框架

public interface StaticProxy {
    void dosth();
}
//////////////接下来是很熟悉的作法,实现这个借口
public class RealRole implements StaticProxy {

    @Override
    public void dosth() {
        System.out.println("do sth");
    }
}
///////////而后重要的角色——代理类,联系 Spring 事务机制
public class ProxyRole implements StaticProxy {
    private StaticProxy staticProxy;

    public ProxyRole() {
        this.staticProxy = new RealRole();
    }

    @Override
    public void dosth() {
        // 真正业务逻辑以前的处理,好比加上事务控制
        before();
        this.staticProxy.dosth(); // 真正的业务逻辑处理,好比数据库的 crud
        after(); // 善后处理,好比,事务提交
    }

    private void after() {
        System.out.println("after dosth");
    }

    private void before() {
        System.out.println("before dosth");
    }
}
////////执行
public class ProxyMain {
    public static void main(String[] args) {
        StaticProxy staticProxy = new ProxyRole();
        staticProxy.dosth();
    }
}

打印==============ide

before dosth
do sth
after dosth工具

如上就是最简单的静态代理模式的实现,很直观,就是使用委托的思想,把责任转移到被代理的对象上,代理类实现非业务相关的功能post

缺陷

静态代理很是简单,可是它的缺陷也是显然的,由于静态代理的代理关系在 IDE 编译时就肯定了,若是接口改变了,不只实现类要改变,代理类也要改变,代理类和接口之间的耦合很是严重。

动态代理模式

和静态代理相反,代理类不是写死的,而是动态的建立。又分为两种实现方案:

基于 JDK实现

也很简单,就是利用 Java 的 API 来实现代理类,即咱们不用本身写代理类了,也就是上面例子里的类——ProxyRole。到这里,其实也能猜出来,本质就是利用 Java 的反射机制在程序运行期动态的建立接口的实现类,并生产代理对象而已,如此一来,就能避免实现的接口——StaticProxy 改变了,致使代理类也跟着变的场景发生。下面看实现代码:

首先写好须要实现的接口,和具体实现类

public interface DynamicProxy {
    void dosth();
}
////////////
public class DynamicRealRole implements DynamicProxy {
    @Override
    public void dosth() {
        System.out.println("do sth");
    }
}

而后,要实现 JDK 的一个接口——InvocationHandler,Java 的动态代理机制中,有两个重要的类,一个是 InvocationHandler 接口,一个是 Proxy 类。

注意,DynamicProxyRole 不是代理类,代理类咱们不须要本身写,它是 JDK 动态生成给咱们的(反射机制)

public class DynamicProxyRole implements InvocationHandler {
    private Object object; // 被代理的对象

    public DynamicProxyRole(Object object) { // 构造注入
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object ret = method.invoke(object, args);
        after();

        return ret;
    }

    private void after() {
        System.out.println("after dosth");
    }

    private void before() {
        System.out.println("before dosth");
    }
}

InvocationHandler 接口只有一个方法 —— invoke,参数也很好理解,分别是:

proxy:代理的真实对象,也就是实现类的对象

method:要调用真实对象的某个方法的Method对象,也就是会调用 dosth 的方法的对象

args:调用真实对象某个方法时接受的参数,没有就是空数组

最后写运行类

public class DynamicMain {
    private static DynamicProxy realRole = new DynamicRealRole(); // 被代理的类,也就是实现类
    private static DynamicProxyRole dynamicProxyRole = new DynamicProxyRole(realRole);

    public static void main(String[] args) {
        // 经过JDK动态代理获取被代理对象(实现类的对象)的代理对象,该代理类实现了指定的须要去代理的接口,也就是第2个参数
        DynamicProxy dynamicProxyObj = (DynamicProxy) Proxy.newProxyInstance(
                realRole.getClass().getClassLoader(), // 被代理的类的加载器
                realRole.getClass().getInterfaces(), // 被代理的类须要实现的接口,能够有多个
                dynamicProxyRole // 必须是实现了 InvocationHandler 接口的类,invoke 方法里写业务逻辑和代理方法
        );
        dynamicProxyObj.dosth();
    }
}

Proxy 类的做用是动态建立一个代理对象,也就是代理对象不须要咱们本身写。Proxy 提供了许多的方法,用的最多的是 newProxyInstance,注释里也写了:

其中第一个参数是被代理的类的加载器,传入的目的是告诉 JDK 由哪一个类加载器对生成的代理进行加载。其实就是真实的类(实现类)的对象的加载器。

第二个参数是代理类须要实现的接口,能够多个。其实就是接口 DynamicProxy,很好理解,在静态代理模式中,咱们就须要手动实现这个接口,来实现代理类。

第三个参数就是实现了InvocationHandler接口的类便可,缘由是此类里有 invoke 方法,而经过 Proxy 的 newProxyInstance 方法生成的代理类去调用接口方法(dosth)时,对方法(dosth)的调用会自动委托给 InvocationHandler 接口的 invoke 方法,这样也就实现了代理模式。

综上,代理对象就实现了在程序运行时产生。进一步要知道,全部的 JDK 动态代理都会继承 java.lang.reflect.Proxy,同时还会实现咱们指定的接口(Proxy 的 newProxyInstance 第二个参数里的接口)。

看到这里,也肯定,JDK 动态代理核心就是反射思想的应用,没什么新鲜的东西。

缺陷

JDK 动态代理这种方式只能代理接口,这是其缺陷

基于 CGLib 实现

Java动态代理是基于接口实现的,若是对象没有实现接口,那么能够用 CGLIB 类库实现,它的原理是基于继承实现代理类。代码也不难

首先,写一个类,其没有实现接口,此时前面的 JDK 动态代理就没法使用了。

public class NoInterfaceReal {
    public void dosth() {
        System.out.println("do sth");
    }
}

其次,须要实现CGLIB 类库提供的接口——MethodInterceptor

在这以前,先下载CGLib 包

        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.10</version>
        </dependency>

而后实现其提供的接口——MethodInterceptor,关键方法是 intercept

public class CGLibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        before();
        Object ret = proxy.invokeSuper(obj, args);
        after();

        return ret;
    }

    private void after() {
        System.out.println("after dosth");
    }

    private void before() {
        System.out.println("before dosth");
    }
}

实现了 MethodInterceptor 接口后,后续生成的代理对象对 dosth 方法的调用会被转发到 intercept 方法,天然也就实现了代理模式。

最后,经过 CGLIB 动态代理生成代理对象,就完成了代理模式,很是简单

public class CGLibMain {
    public static void main(String[] args) {
        CGLibProxy cgLibProxy = new CGLibProxy();
        NoInterfaceReal proxy = (NoInterfaceReal) Enhancer.create(NoInterfaceReal.class, cgLibProxy);
        proxy.dosth();
    }
}

经过CGLib 的Enhancer类 create 了一个代理对象,参数传入须要被代理的类(能够不是接口),和实现了 MethodInterceptor 接口的类的对象便可。CGLib 就会为咱们自动生成继承了被代理的类的代理对象,经过代理对象调用 dosth 方法,其调用会被委托给第二个参数里的 intercept 方法。

综上得知:

一、CGLib 底层是利用 asm 字节码框架实现的,该框架能够在 Java 程序运行时对字节码进行修改和动态生成,故它能够代理普通类,具体细节是经过继承和重写须要被代理的类(NoInterfaceReal)来实现。

二、CGLib 能够实现对方法的代理,便可以实现拦截(只代理)某个方法。

三、经过CGLib 的 Enhancer 类来create 代理对象。而对这个对象全部非final方法的调用都会委托给 MethodInterceptor 接口的 intercept,咱们能够在该方法内部写拦截代码,最后在经过调用MethodProxy 对象的 invokeSuper() 方法,把调用转发给真实对象

缺陷

没法对 final 类、或者 final 方法进行代理

代理模式的性能对比

直接搬运结论:CGLib 底层基于asm 框架实现,比 Java 反射性能好,可是比 JDK 动态代理稍微慢一些

代理模式的缺点

主要是性能问题,什么增长系统复杂度等都不是事儿。同等条件,用代理,确定比不用代理要慢一些。

代理模式和装饰器模式对比

实现方式上很像,可是目标不同,后者是为了给类(对象)增长新的功能,不改变API,前者除了这些做用,目标主要是为了使用中间人(代理角色)给本类(对象)减小负担。

参见:对复合(协做)算法/策略的封装方法——装饰模式总结

代理模式的应用

很是常见了,AOP很是典型,还有各类框架的拦截器机制,数据库切换等工具。。。

相关文章
相关标签/搜索