通用技术 [设计模式] 动态代理模式学习

bauul · 2017年08月23日 · 最后由 bauul 回复于 2017年08月24日 · 1240 次阅读

jdk 动态代理

如果你的程序需要频繁、反复地创建代理对象,则 JDK 动态代理在性能上更占优。

缺点

目标对象一定要实现接口,否则不能用动态代理

接口

package com.carl.proxy;

public interface IFly {
    boolean isCanFly();

    String flying();
}

实例类

package com.carl.proxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Bird implements IFly {

    private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);

    @Override
    public boolean isCanFly() {
        LOGGER.info("isCanFly");
        return true;
    }

    @Override
    public String flying() {
        LOGGER.info("flying");
        return "flying";
    }

}

动态代理类

package com.carl.proxy;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ProxyFactory {

    private static final Logger LOGGER = LoggerFactory.getLogger(ProxyFactory.class);

    private Object target;
    public ProxyFactory(Object target) {
        this.target = target;
    }

    public void test() {
        LOGGER.info("testing");
    }

    public Object getProxyInstance() {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler(){

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        LOGGER.info("invoke start");
                        test();
                        Object obj = method.invoke(target, args);
                        LOGGER.info("invoke end");
                        return obj;
                    }

                });
    }

}

测试类

package com.carl.proxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {

//  private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);

    public static void main(String[] args) {
        IFly f = new Bird();

        IFly proxy = (IFly) new ProxyFactory(f).getProxyInstance();

        proxy.isCanFly();
    }

}

结果

22:22:10.984 [main] INFO com.carl.proxy.ProxyFactory - invoke start
22:22:10.988 [main] INFO com.carl.proxy.ProxyFactory - testing
22:22:10.989 [main] INFO com.carl.proxy.Main - isCanFly
22:22:10.989 [main] INFO com.carl.proxy.ProxyFactory - invoke end

我的理解

  1. 似乎有点反射调用的意思,看得我还是有点懵的,参考中网上别人的例子中没有加 test 这个方法调用,这是我自己加的, 因为如果不加我感觉没必要这么玩,明明可以直接调 f.isCanFly() 方法的……。
  2. 然后 proxy 与 args 都没有用到,在
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

关于远程对象服务目前最优工具

HPROSE 是 High Performance Remote Object Service Engine 的缩写,翻译成中文就是 “高性能远程对象服务引擎”。

cglib 代理

在运行期扩展 Java 类与实现 Java 接口,通俗说 cglib 可以在运行时动态生成字节码

pom 依赖

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.5</version>
</dependency>

Bird

package com.carl.cglib;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Bird {

    private static final Logger LOGGER = LoggerFactory.getLogger(Bird.class);

    public boolean isCanFly() {
        LOGGER.info("isCanFly");
        return true;
    }

    public String flying() {
        LOGGER.info("flying");
        return "flying";
    }

}

DynamicProxy

package com.carl.cglib;

import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class DynamicProxy implements MethodInterceptor {
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicProxy.class);

    Object target;

    public Object getProxyObject(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setCallback(this);
        enhancer.setSuperclass(target.getClass());
        return enhancer.create();
    }

    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        LOGGER.info("invoke start");
        LOGGER.info(methodProxy.getSuperName());
        Object result = methodProxy.invoke(target, args);
        LOGGER.info("invoke end");
        return result;
    }

}

测试类

package com.carl.cglib;

public class Main {

    public static void main(String[] args) {
        Bird bird = (Bird) new DynamicProxy().getProxyObject(new Bird());
        bird.flying();
        bird.isCanFly();
    }

}

调试截图

结果

21:35:39.214 [main] INFO com.carl.cglib.DynamicProxy - invoke start
21:35:39.217 [main] INFO com.carl.cglib.DynamicProxy - CGLIB$flying$0
21:35:39.229 [main] INFO com.carl.cglib.Bird - flying
21:35:39.229 [main] INFO com.carl.cglib.DynamicProxy - invoke end
21:35:39.229 [main] INFO com.carl.cglib.DynamicProxy - invoke start
21:35:39.229 [main] INFO com.carl.cglib.DynamicProxy - CGLIB$isCanFly$1
21:35:39.229 [main] INFO com.carl.cglib.Bird - isCanFly
21:35:39.229 [main] INFO com.carl.cglib.DynamicProxy - invoke end

我的理解

cglib 在执行时动态的生成了一个新的 Bird 类并重写了被代理的方法
如果使用 intellij 可能是可以看到源码的,eclipse 不行呢

参考

  1. http://www.cnblogs.com/best/p/5679656.htmljdk 动态代理到 cglib 动态代理到 spring(从静态代理到 AOP)
  2. https://www.zhihu.com/question/20794107/answer/23330381
  3. http://www.cnblogs.com/hongwz/p/5764917.htmlspring( AOP 原理简介)

欢迎批评交流

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 4 条回复 时间 点赞

就是反射,像 jmockit 这种单测 mock 用的核心也是反射机制

—— 来自 TesterHome 官方 安卓客户端

你首先要想一下为什么要使用代理模式,它的使用场景是什么。通过这种代理,你可以在 invoke 方法中除了调用原方法外,可以再插入其它的操作,比如记录时间、日志,在方法调用前后检查端口、数据库等等,是一种面向切面的思想,类似 python 中的装饰器。当很多个方法有这些共有的操作的时候,你可以将方法抽象出来,然后将这些操作都放入 invoke 方法中,通过代理可以让多个被代理的方法都有同样的操作。

CC 回复

谢谢

增加 cglib 动态代理

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册