前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java代理1 代理和动态代理的基础与使用

Java代理1 代理和动态代理的基础与使用

作者头像
WindCoder
发布2018-09-19 15:53:21
3010
发布2018-09-19 15:53:21
举报
文章被收录于专栏:WindCoderWindCoder

前言

预计分两篇写,第一篇是基础和一般用法,第二篇详细写下动态代理。本篇基础主要来自《Think in java》阅读笔记。

代理

代理是基本的设计模式之一。

是为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象。

将额外的操作从“实际”对象中分离到不同的地方,特别是希望能够容易的做出修改,从没有使用额外操作转为使用这些操作,或者反过来时,代理就显得很有用。

设计模式的关键就是封装修改-因此需要修改事务以证明这种模式的正确性。如,当希望跟踪Dog中的方法调用,或度量这些调用的开销,这些代码肯定不希望将其合并到应用中的代码,此时代理可以很容易的添加或移除他们。

通俗说,代理就是对象需要一些附加或不同的操作时,使用第三方对象操作原对象以及处理这些操作,从而达到不直接修改原对象的目的的一种设计模式。

静态代理是在编译时就将接口、实现类、代理类全部写出来,但若是需要很多代理,就需要每一次都这样写一遍,这就可能导致时间与资源的浪费。 此时可以使用动态代理来代替静态代理。

静态代理的实现

接口类 Animal

代码语言:javascript
复制
public interface Animal {
    void doBark();
    void somethingElse(String arg);
}

具体实现类 Dog

代码语言:javascript
复制
public class Dog implements Animal {
    public void doBark() {
        PrintUtill.println("doBark wow");
    }

    public void somethingElse(String arg) {
        PrintUtill.println("somethingElse " + arg);
    }
}

代理类 SimpleProxy

代码语言:javascript
复制
public class SimpleProxy implements Animal {

    private Animal proxied;

    public SimpleProxy(Animal proxied) {
        this.proxied = proxied;
    }

    public void doBark() {
        PrintUtill.println("SimpleProxy doSomething");
        proxied.doBark();
    }

    public void somethingElse(String arg) {
        PrintUtill.println("SimpleProxy somethingElse " + arg);
        proxied.somethingElse(arg);
    }
}

测试代理类 SimpleProxyDemo

代码语言:javascript
复制
/**
 * consumer 接收的Animal,所以它无法知道正则获得的到底是Dog还是SimpleProxy,因为这两者都实现了Animal。
 *
 * 但是SimpleProxy已经被插入到consumer与Dog之间,因此它会执行操作,然后调用Dog上相同的方法。
 */
public class SimpleProxyDemo {
    public static void  consumer(Animal iface){
        iface.doBark();
        iface.somethingElse("WindCoder.com");
    }

    public static void main(String[] args) {
        consumer(new Dog());
        consumer(new SimpleProxy(new Dog()));
    }
}

测试结果:

代码语言:javascript
复制
doBark wow
somethingElse WindCoder.com
SimpleProxy doSomething
doBark wow
SimpleProxy somethingElse WindCoder.com
somethingElse WindCoder.com

动态代理

动态代理可以动态地创建代理并动态地处理对所代理方法的调用。

在动态代理上所做的所有调用都会被重定向到单一的调用处理器上。

该调用处理器的工作是揭示调用的类型并确定相应的对策。

JDK动态代理实现

InvocationHandler接口的实现类DynamicProxyHandler 

代码语言:javascript
复制
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DynamicProxyHandler implements InvocationHandler {
    private Object proxied;

    public DynamicProxyHandler(Object proxied) {
        this.proxied = proxied;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("*** proxy: " + proxy.getClass() + " , method: " + method + " , args: " + args);
        if (args != null){
            for (Object arg: args){
                PrintUtill.println(" " + arg);
            }
        }
        return method.invoke(proxied, args);
    }
}

动态代理测试类SimpleDynamicProxy

代码语言:javascript
复制
public class SimpleDynamicProxy {
    public static void consumer(Animal iface){
        iface.doBark();
        iface.somethingElse("windCoder.com DynamicProxy");
    }

    public static void main(String[] args) {
        Dog dog = new Dog();
        consumer(dog);
        // Insert a proxy and call again
        Animal prox = (Animal) Proxy.newProxyInstance(
                Animal.class.getClassLoader(),
                new Class[]{Animal.class},
                new DynamicProxyHandler(dog)
        );
        consumer(prox);

    }
}
测试结果:
代码语言:javascript
复制
doBark wow
somethingElse windCoder.com DynamicProxy
*** proxy: class com.sun.proxy.$Proxy0 , method: public abstract void Others.base.SimpleProxy.Animal.doBark() , args: null
doBark wow
*** proxy: class com.sun.proxy.$Proxy0 , method: public abstract void Others.base.SimpleProxy.Animal.somethingElse(java.lang.String) , args: [Ljava.lang.Object;@4b67cf4d
 windCoder.com DynamicProxy
somethingElse windCoder.com DynamicProxy
关键代码解析

动态代理测试类SimpleDynamicProxy.java中

代码语言:javascript
复制
        Animal prox = (Animal) Proxy.newProxyInstance(
                Animal.class.getClassLoader(),
                new Class[]{Animal.class},
                new DynamicProxyHandler(dog)
        );

通过调用静态方法Proxy.newProxyInstance()可以创建动态代理,这个方法需要得到:

  • 1. 一个类加载器(你通常可以从已经被加载的对象获取其类加载器,然后传递给它,如此处的Animal.class.getClassLoader())
  • 2. 一个你希望该代理类实现的接口列表(不是类或抽象类),如 new Class[]{Animal.class}
  • 3. InvocationHandler接口的一个实现,如DynamicProxyHandler

动态代理可以将所有调用重定向到调用处理器。

通常会向调用处理器的构造器传递一个“实际”对象的引用,从而使得调用处理器在执行其中介任务时,可以将请求转发。

InvocationHandler接口的实现类DynamicProxyHandler.java中:

代码语言:javascript
复制
method.invoke(proxied, args);

亦既:

代码语言:javascript
复制
    public Object invoke(Object obj, Object... args)

invoke()方法中传递进来了代理对象,以防使用时需要区分请求的来源,但在许多情况下并不关心这一点。

在invoke()内部,在代理调用方法时,对接口的调用将被重定向为对代理的调用,因此要格外当心。

通常,执行被代理的操作,然后使用Method.invoke()将请求转发给被代对象,并传入必需的参数。

初看起来可能有些受限,就像只能执行泛化操作一样。

但可以通过传递其他的参数,来过滤某些方法的调用。

CGLIB动态代理(CGLibDemo)

JDK动态代理: 只能代理实现了接口的类,没有实现接口的类不能实现JDK动态代理。

Cglib代理: 针对类来实现代理,对指定目标产生一个子类 通过方法拦截技术拦截所有父类方法的调用。

在实现内部,CGLIB库使用了ASM这一个轻量但高性能的字节码操作框架来转化字节码,产生新类。

CGLIB实现方式

1.首先定义业务类,无需实现接口(当然,实现接口也可以,不影响的)

代码语言:javascript
复制
public class DogCGlib {
    public void doBark() {
        PrintUtill.println("doBark wow CGlib");
    }

    public void somethingElse(String arg) {
        PrintUtill.println("somethingElse CGlib" + arg);
    }
}

2.实现 MethodInterceptor方法代理接口,创建代理类

代码语言:javascript
复制
public class CGlibProxy implements MethodInterceptor {

    public Object getInstance(Class  target){
        //创建加强器,用来创建动态代理类
        Enhancer enhancer = new Enhancer();
        //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
        enhancer.setSuperclass(target);
        //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
        enhancer.setCallback(this);
        // 创建动态代理类对象并返回
        return enhancer.create();
    }
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("windcoder.com日志开始...");
        //代理类调用父类的方法
        proxy.invokeSuper(obj, args);
        System.out.println("windcoder.com日志结束...");
        return null;
    }
}

3.创建业务类和代理类对象,然后通过 代理类对象.getInstance(业务类对象) 返回一个动态代理类对象(它是业务类的子类,可以用业务类引用指向它)。最后通过动态代理类对象进行方法调用。

代码语言:javascript
复制
public class CGLIBDemo {
    public static void main(String[] args) {
        // 设置输出目录,方便之后查看CGLIB生成的class---非必选项
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./tmp/");
        // 正式执行
        CGlibProxy proxy = new CGlibProxy();
        DogCGlib dog = (DogCGlib)proxy.getInstance(DogCGlib.class);
        dog.doBark();
        PrintUtill.printlnRule();
        dog.somethingElse("  你好");
    }
}

参考资料

《Java编程思想 第4版》

CGLIB动态代理介绍

Java动态代理之JDK实现和CGlib实现(简单易懂)

Cglib动态代理模式实现

相关下载

点击下载

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 代理
    • 静态代理的实现
    • 动态代理
      • JDK动态代理实现
        • 测试结果:
        • 关键代码解析
      • CGLIB动态代理(CGLibDemo)
        • CGLIB实现方式
    • 参考资料
    • 相关下载
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档