欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java动态代理

程序员文章站 2022-06-09 21:38:45
...

代理简介

代理是基本的设计模式之一,它是你为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象。但是对于调用者,对此毫无察觉,就好像调用的方法是用传统方式实现的一般。

静态代理

静态代理的依赖关系在编译期就已经确定了。
静态代理类图如下:
Java动态代理

一个静态代理的例子:

public interface  TestInterface{
    void doSomething();
}
public class TestOne implements TestInterface{
    @Override
    public void doSomething() {
        System.out.println("TestOne on call");
    }
}
public class ProxyDemo implements TestInterface{
    private TestOne one;
    public ProxyDemo(TestOne one){
        this.one=one;
    }
    @Override
    public void doSomething() {
        System.out.println("Proxy on call");
        one.doSomething();
    }
}
public class Client {
    public static void func(TestInterface ti){
        ti.doSomething();
    }
    public static void main(String[] args){
        func(new TestOne());
        func(new ProxyDemo(one));
    }
}

打印结果如下:
Java动态代理
从代码中可以看出,project.func()方法执行之前,其实代理和被代理类的关系就已经确定了。

静态代理理解:

给我的感觉是代理类就像包裹在了被代理类外面一样。然后因为代理类和被代理类都实现了同一个接口,根据面向接口编程的特性(以及注入),不需要重写原来的方法就能实现代理类中添加的操作。

这里有几个值得注意的点:

  1. “实际”类和代理类都实现了同一个接口
  2. 调用的方法传递的参数是面向接口的
  3. 在代理类中,添加了一些“实际”类额外的操作,而且实现这些额外的操作不会对func方法本身的实现产生影响

静态代理本身存在以下缺点:

  1. 一旦接口增加方法,那么所有实现了该接口的类都要进行修改
  2. 一个代理类只能服务一种类型,如果要服务于多种类型,会创建过多的代理类

下面动态代理的例子会对上述的缺点进行进一步的阐述。

动态代理

动态代理不同于静态代理,代理和被代理的关系是直到运行时才能够确定。
引用秒懂Java代理与动态代理模式的例子:

public interface ILawSuit {
    void submit(String proof);//提起诉讼
    void defend();//法庭辩护
}
public class CuiHuaNiu implements ILawSuit {
    @Override
    public void submit(String proof) {
        System.out.println(String.format("老板欠薪跑路,证据如下:%s",proof));
    }
    @Override
    public void defend() {
        System.out.println(String.format("铁证如山,%s还牛翠花血汗钱","马旭"));
    }
}
public class DynProxyLawyer implements InvocationHandler {
    private Object target;//被代理的对象
    public DynProxyLawyer(Object obj){
        this.target=obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("案件进展:"+method.getName());
        Object result=method.invoke(target,args);
        return result;
    }
}
public class Client {
    public static void main(String[] args) {
        ILawSuit target=new CuiHuaNiu();
        InvocationHandler handler = new DynProxyLawyer(target);
        ILawSuit proxy = (ILawSuit) Proxy.
            newProxyInstance(target.getClass().getClassLoader(), 
                                target.getClass().getInterfaces(), 
                                handler);
        proxy.submit("工资流水在此");
        proxy.defend();
    }
}

打印结果:

案件进展:submit
老板欠薪跑路,证据如下:工资流水在此
案件进展:defend
铁证如山,马旭还牛翠花血汗钱

试想,如果采用静态代理的方式,那么代理类中要实现被代理类中所有的方法,如果方法很多的话,那么会非常冗余,尤其是如果这些方法需要的额外操作还一样的话(此处是打印案件进展)。而且,如果存在另一个继承自BLawSuit的被代理人张小花,就算律师处理流程相同,那么也需要编写一个代理律师的类。

动态代理理解:

我的理解是动态代理是在保持当前方法不变的情况下,为需要添加相同额外操作的方法提供的统一的“接口”。这个实现了InvocationHandler的类相当于是一类操作的抽象,所有需要这个类中invoke方法中额外操作的类(前提是这个类实现了某个接口)在生成代理的时候,把这个抽象传递就去就好了(Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler))。

动态代理我认为存在如下值得注意的点:

  1. 需要有一个实现了接口的被代理类
  2. 需要有一个实现了InvocationHandler接口的类
  3. 在调用时使用Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler)获取实例

对动态代理中需要的静态类和函数解释的比较清楚的看这篇博文:Java的动态代理机制详解

静态代理比动态代理更符合OOP原则,在日常开发中使用也较多。动态代理在开发框架时使用较多,例如的Spring和MyBatis。

CGLIB代理

cglib特点:

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。
  • CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。
  • CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它需要你对JVM内部结构包括class文件的格式和指令集都很熟悉。

引用秒懂Java代理与动态代理模式的例子:

public class Frank {
   public void submit(String proof) {
       System.out.println(String.format("老板欠薪跑路,证据如下:%s",proof));
   }
   public void defend() {
       System.out.println(String.format("铁证如山,%s还Frank血汗钱","马旭"));
   }
}
public class cgLibDynProxyLawyer implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
        if (method.getName().equals("submit"))
            System.out.println("案件提交成功,证据如下:"+ Arrays.asList(params));
        Object result = methodProxy.invokeSuper(o, params);
        return result;
    }
}
public class ProxyFactory {
    public static Object getGcLibDynProxy(Object target){
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new cgLibDynProxyLawyer());
        Object targetProxy= enhancer.create();
        return targetProxy;
    }
}
  public static void main(String[] args) {
        Frank cProxy= (Frank) ProxyFactory.getGcLibDynProxy(new Frank());
        cProxy.submit("工资流水在此");
        cProxy.defend();
    }

打印如下:

案件提交成功,证据如下:[工资流水在此]
老板欠薪跑路,证据如下:工资流水在此
铁证如山,马旭还Frank血汗钱

在spring的AOP实现中,同时使用了jdk提供的动态代理和cglib动态代理。

Java三种代理模式:静态代理、动态代理和cglib代理的例子和上面不同,可以参考下

参考链接: