Java笔记··By/蜜汁炒酸奶

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

前言

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

代理

代理是基本的设计模式之一。 是为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象。 将额外的操作从“实际”对象中分离到不同的地方,特别是希望能够容易的做出修改,从没有使用额外操作转为使用这些操作,或者反过来时,代理就显得很有用。 设计模式的关键就是封装修改-因此需要修改事务以证明这种模式的正确性。如,当希望跟踪Dog中的方法调用,或度量这些调用的开销,这些代码肯定不希望将其合并到应用中的代码,此时代理可以很容易的添加或移除他们。 通俗说,代理就是对象需要一些附加或不同的操作时,使用第三方对象操作原对象以及处理这些操作,从而达到不直接修改原对象的目的的一种设计模式。 静态代理是在编译时就将接口、实现类、代理类全部写出来,但若是需要很多代理,就需要每一次都这样写一遍,这就可能导致时间与资源的浪费。 此时可以使用动态代理来代替静态代理。

静态代理的实现

接口类 Animal

public interface Animal {
    void doBark();
    void somethingElse(String arg);
}
1
2
3
4

具体实现类 Dog

public class Dog implements Animal {
    public void doBark() {
        PrintUtill.println("doBark wow");
    }

    public void somethingElse(String arg) {
        PrintUtill.println("somethingElse " + arg);
    }
}
1
2
3
4
5
6
7
8
9

代理类 SimpleProxy

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);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

测试代理类 SimpleProxyDemo

/**
 * 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()));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

测试结果:

doBark wow
somethingElse WindCoder.com
SimpleProxy doSomething
doBark wow
SimpleProxy somethingElse WindCoder.com
somethingElse WindCoder.com
1
2
3
4
5
6

动态代理

动态代理可以动态地创建代理并动态地处理对所代理方法的调用。 在动态代理上所做的所有调用都会被重定向到单一的调用处理器上。 该调用处理器的工作是揭示调用的类型并确定相应的对策。

JDK动态代理实现

**InvocationHandler接口的实现类DynamicProxyHandler **

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);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

动态代理测试类SimpleDynamicProxy

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);

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

测试结果:

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
1
2
3
4
5
6
7

关键代码解析

动态代理测试类SimpleDynamicProxy.java中

Animal prox = (Animal) Proxy.newProxyInstance(
                Animal.class.getClassLoader(),
                new Class[]{Animal.class},
                new DynamicProxyHandler(dog)
        );
1
2
3
4
5

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

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

动态代理可以将所有调用重定向到调用处理器。 通常会向调用处理器的构造器传递一个“实际”对象的引用,从而使得调用处理器在执行其中介任务时,可以将请求转发。 InvocationHandler接口的实现类DynamicProxyHandler.java中:

method.invoke(proxied, args);
1

亦既:

public Object invoke(Object obj, Object... args)
1

invoke()方法中传递进来了代理对象,以防使用时需要区分请求的来源,但在许多情况下并不关心这一点。 在invoke()内部,在代理调用方法时,对接口的调用将被重定向为对代理的调用,因此要格外当心。 通常,执行被代理的操作,然后使用Method.invoke()将请求转发给被代对象,并传入必需的参数。 初看起来可能有些受限,就像只能执行泛化操作一样。 但可以通过传递其他的参数,来过滤某些方法的调用。

CGLIB动态代理(CGLibDemo)

JDK动态代理: 只能代理实现了接口的类,没有实现接口的类不能实现JDK动态代理。 Cglib代理: 针对类来实现代理,对指定目标产生一个子类 通过方法拦截技术拦截所有父类方法的调用。 在实现内部,CGLIB库使用了ASM这一个轻量但高性能的字节码操作框架来转化字节码,产生新类。

CGLIB实现方式

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

public class DogCGlib {
    public void doBark() {
        PrintUtill.println("doBark wow CGlib");
    }

    public void somethingElse(String arg) {
        PrintUtill.println("somethingElse CGlib" + arg);
    }
}
1
2
3
4
5
6
7
8
9

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

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;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

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

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("  你好");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

参考资料

《Java编程思想 第4版》 CGLIB动态代理介绍

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

Cglib动态代理模式实现

预览
Loading comments...
1 条评论
  • W

    […] 详细代码实例请见:Java代理1 代理和动态代理的基础与使用 中的静态代理部分。 […]

example
预览