对于java的使用,真的已经有好几年了。可以说,java的方方面面差不多都有涉猎。但是,也有几个知识点,一直没有怎么涉及。比如:

  • 动态代理
  • 注解
  • GUI

可能是当时学习这些的时候,带给我太多的恐惧,导致我一直不敢再碰这些东西。
不过现在回过头来,再次拾起当时让我恐惧的这些内容,没有当时那么恐惧了;相反,有一种很愉悦的感觉。

顺便说一句:java中应该没有所谓的”静态代理”。因为我看到网上的各种博客/教程/问答,对于静态代理的讲解,其实完全是对装饰者模式的讲解与使用而已。或者是是对聚合模式的使用而已。

闲话不多说,下面我也简单讲解一下我对动态代理的理解与使用。


DynamicProxy

Java 动态代理| jdk & cglib 两种方式实现

所谓动态代理,首先要确立一个目标就是要生成一个目标类的代理对象。

明确了这一目标,后续的实现就有了一个明确的目的性了。

  • 无论是 jdk的原生支持,还是第三方库,比如cglib,目的都是一样的,就是生成一个目标类的代理对象。
  • 拿到代理对象只后,就可以像使用目标对象一样去使用里面的方法。
  • 不过既然是使用代理,就必然希望通过代理,去对目标对象对方法做一些改变,比如增强实现。(加入日志插入等)。这时候,就可以在代理实现等回调方法里面去做一些增强了。

大体上的套路就是这些。至于怎么去使用,往往要看实际应用场景了。

  • jdk对动态代理的支持程度是:被代理方法,必须是接口中的方法。
  • cglib对动态代理对支持程度是:被代理对方法,不能是被final修饰对方法。

好吧:下面分别给出两种实现的简单示例:


  • 基于jdk的实现:

模拟场景:有一个Car类,实现类Moveable接口。然后,通过动态代理去调用Car#move()方法,并在方法执行的前后打印日志。

// interface movaable
interface Moveable {
    boolean move(String road);
}
// class car
class Car implements Moveable {
    @Override
    public boolean move(String road) {

        System.out.println("I am running in road: " + road);
        try {
            Thread.sleep(1000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return new Random().nextInt() % 2 == 0;
    }
}

// test main
public class TestClient {

    public static void main(String[] args) {
        Moveable m = new Car();

        Class<? extends Moveable> clazz = m.getClass();

        Moveable proxyInstance = (Moveable) Proxy.newProxyInstance(clazz.getClassLoader(),
                clazz.getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        System.out.println("do before >>>>");

                        long st = System.currentTimeMillis();
                        Object invoke = method.invoke(m, args);
                        long et = System.currentTimeMillis();
                        System.err.println("invoke = " + invoke + " , " + method + " , " + Arrays.toString(args));
                        System.out.println("do after <<<<<");

                        System.out.println(method.getName() + " 耗时: " + (et - st));
                        return invoke;
                    }
                });

        proxyInstance.move("Tokyo");

    }
}

jdk输出如下:

do before >>>>
I am running in road: Tokyo
invoke = false , public abstract boolean com.cat.proxy.dynamic.Moveable.move(java.lang.String) , [Tokyo]
do after <<<<<
move 耗时: 1001


  • 基于cglib的实现:

模拟场景:有一个 Train类,也有一个 move方法,通过动态代理,在 move方法执行前后,打印日志。
// class train 
class Train {

    public boolean move(String road) {

        System.out.println("I am running in road: " + road);
        try {
            Thread.sleep(1000);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return new Random().nextInt() % 2 == 0;
    }
}
// test main
public class TestMain {
    public static void main(String[] args) throws InterruptedException {
        Train train = new Train();
        proxy3(train);
    }
    private static void proxy3(Train train) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(train.getClass());
        enhancer.setCallback((MethodInterceptor) (target, method, objects, methodProxy) -> {
            System.out.println("** do before >>> .");
//            Object invokeSuper = methodProxy.invokeSuper(target, objects);
            Object invokeSuper = method.invoke(train, objects); // 使用 method 和 methodProxy 都可以。
            System.err.println((target instanceof Train) + " , " + method + " , " + Arrays.toString(objects) + " , " + methodProxy);
            System.out.println("** do after <<<< $ ");
            return invokeSuper;
        });
        Train trainProxy = (Train) enhancer.create();
        trainProxy.move("Paris");
    }
 }

cglib输出如下

** do before >>> .
I am running in road: Paris
** do after <<<< $ 
true , public boolean com.cat.proxy.dynamic.cglib.Train.move(java.lang.String) , [Paris] , net.sf.cglib.proxy.MethodProxy@78e03bb5

最后,给出完整示例代码。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐