java 静态代理和动态代理

    在学习java过程中有看到资料,什么 final 类没法动态代理之类的标注,动态代理这个词经常看到,好奇心,顺便研究了下关于java的代理,关于final这个具体在编译和运行期要情况,后面再整理个文章出来,以做收藏。

    代理,顾名思义,跟委托意思差不多,比如你需要将你的网站申请网上ICP经营许可证,但你不懂具体怎么搞,可以找第三方中介机构(因为有中介是专门做这个的,比你自己申请要多要好省),委托他们帮你去做这件事。当然,不管谁去申请ICP,做法都是一样的,相关单位预设了相应接口(如需要准备什么资料、法人信息、公司经营许可、在哪里申请、申请流程等),只要附合这些接口需要提供的东西,就可以申请到许可证。

   上面是一个代理(委托)的过程。

    如有一个客户系统,两个功能,客户跟进和信息修改,看代码:

做法一

package com.blueinfinite;

import lombok.extern.slf4j.Slf4j;

/**
 * 客户业务 例1
 */
@Slf4j
public class Custom1 {
    /**
     * 客户跟进
     */
    public void follow() {
        log.info("通用功能:验证是否有权限跟进");

        log.info("业务A:客户跟进");

        log.info("通用功能:记录日志");
    }

    /**
     * 客户信息修改
     */
    public void edit() {
        log.info("通用功能:验证是否有权限跟进");

        log.info("业务B:客户信息修改");

        log.info("通用功能:记录日志");
    }
}

测试代码:

    @Test
    public void test1() {
        Custom1 c = new Custom1();
        c.follow();
        c.edit();
    }

弊端:两个业务功能跟权限和日志记录混合到一起,原有业务职责不明确、偶合度高且维护难

做法二,简单封装

package com.blueinfinite;

import lombok.extern.slf4j.Slf4j;

/**
 * 客户业务 例2
 */
@Slf4j
public class Custom2 {
    /**
     * 客户跟进
     */
    public void follow() {
        CustomUtils.verifyPermissions();

        log.info("业务A:客户跟进");

        CustomUtils.addlog();
    }

    /**
     * 客户信息修改
     */
    public void edit() {
        CustomUtils.verifyPermissions();

        log.info("业务B:客户信息修改");

        CustomUtils.addlog();
    }
}
package com.blueinfinite;

import lombok.extern.slf4j.Slf4j;

/**
 * 客户功能封装
 */
@Slf4j
public class CustomUtils {
    public static void verifyPermissions() {
        log.info("通用功能:验证是否有权限跟进");
    }

    public static void addlog() {
        log.info("通用功能:记录日志");
    }
}

测试代码:

    @Test
    public void proxy2() {
        Custom2 c = new Custom2();
        c.follow();
        c.edit();
    }

虽然进行了常用功能的封装,但在很多业务场景中出还是有代码冗余(如方法调用)

做法三,静态代理

package com.blueinfinite;

/**
 * 静态代理
 */
public interface Custom3Proxy {
    /**
     * 客户跟进
     */
    public void follow();

    /**
     * 客户信息修改
     */
    public void edit();
}
package com.blueinfinite;

import lombok.extern.slf4j.Slf4j;

/**
 * 客户业务 例3
 */
@Slf4j
public class Custom3 implements Custom3Proxy {
    /**
     * 客户跟进
     */
    public void follow() {
        log.info("业务A:客户跟进");
    }

    /**
     * 客户信息修改
     */
    public void edit() {
        log.info("业务B:客户信息修改");
    }
}
package com.blueinfinite;

import lombok.extern.slf4j.Slf4j;

/**
 * 静态代理实现
 */
@Slf4j
public class Custom3ProxyImpl implements Custom3Proxy {
    private Custom3Proxy proxy;

    public Custom3ProxyImpl(Custom3Proxy proxy){
        this.proxy = proxy;
    }

    /**
     * 客户跟进
     */
    public void follow() {
        CustomUtils.verifyPermissions();

        proxy.follow();

        CustomUtils.addlog();
    }

    /**
     * 客户信息修改
     */
    public void edit() {
        CustomUtils.verifyPermissions();

        proxy.edit();

        CustomUtils.addlog();
    }
}

测试代码:

    @Test
    public void proxy3() {
        Custom3Proxy c = new Custom3ProxyImpl(new Custom3());
        c.follow();
        c.edit();
    }

通过使用静态代理,我们发现客户业务功能(Custom3)相比之前纯洁多了,功能职责不混乱,职责更加单一,清爽。在更好的解偶的情况下,且不修改原有业务代码(Custom3)实现了功能增强。

弊端是实现类和代理类都需要共同的接口,增加代码重复。

做法四,动态代理

package com.blueinfinite;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 动态代理 例4
 */
public class DynamicProxy {

    public static  T getObj(final Object target) {
        return (T)Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //验证是否有权限跟进
                        CustomUtils.verifyPermissions();

                        //具体业务
                        Object result = method.invoke(target, args);

                        //记录日志
                        CustomUtils.addlog();

                        return result;
                    }
                }
        );

    }

}

测试代码:

    @Test
    public void proxy4() {
        Custom3Proxy proxy = DynamicProxy.getObj(new Custom3());
        proxy.follow();
        proxy.edit();
    }

通过jdk动态代理,弥补了静态代理的不足。

java中动态代理主要分两种,除了jdk动态代理,cglib也是可实现动态代理。

jdk动态代理通过反射机制,找到对象的接口和元数据,可强制执行对象的动作。代理主要用到了两个类,java.lang.reflect.InvocationHandler和java.lang.reflect.Proxy,后续将讲解这两个类是怎么实现动态代理、以及cglib的实现原理。

通过代理,我们可以将代码无侵入增强(即无侵入式编程),spring的AOP可以通过动态代理实现无侵入编程。

源码地址:https://github.com/blueinfinite/javaproxy

 

java,动态代理 | 2018-05-31 03:37:25