java三种代理方式总结

最近在学习java代理,总结输出如下。

java代理分为三种实现方式JDK静态代理,JDK动态代理和CGLIB代理,三种代理的特点及比较如下表

代理方式 实现 优点 缺点 其他
JDK静态代理 代理类与委托类实现同一接口,并且在代理类中需要硬编码接口 实现简单,容易理解 代理类需要硬编码接口,在实际应用中可能会导致重复编码,浪费存储空间并且效率很低 实现简单
JDK动态代理 代理类与委托类实现同一接口,主要是通过代理类实现InvocationHandler并重写invoke方法来进行动态代理的,在invoke方法中将对方法进行增强处理 不需要硬编码接口,代码复用率高 只能够代理实现了接口的委托类 底层使用反射机制进行方法的调用
CGLIB动态代理 代理类将委托类作为自己的父类并为其中的非final委托方法创建两个方法,一个是与委托方法签名相同的方法,它在方法中会通过super调用委托方法;另一个是代理类独有的方法。在代理方法中,它会判断是否存在实现了MethodInterceptor接口的对象,若存在则将调用intercept方法对委托方法进行代理 可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口 不能对final类以及final方法进行代理 底层将方法全部存入一个数组中,通过数组索引直接进行方法调用
  • JDK静态代理实现

如上表,静态代理需要代理类与目标类实现同一接口。
统一接口类Person,Man类实现Person接口;静态代理类StaticManProxy对Man类进行增强;即代理。

Person.java

public interface Person{
    String eat(String food);    
    String sleep(String where); 
}

Man.java

public class Man implements Person{
    public String sleep(String name){
        System.out.println(name+"睡觉了");
        return "";
    }

    public String eat(String name){
        System.out.println(name+"吃饭了");
        return "";
    }
}

静态代理类需要与目标类的接口,即StaticManProxy同样需要实现Person

StaticManProxy.java

public class StaticManProxy implements Person{

    private Person target;

    public StaticManProxy(Person target){
        this.target=target;
    }

    public String eat(String name) {
        System.out.println("静态代理 do something~");
        target.eat(name);
        return null;
    }

    public String sleep(String name) {
        return null;
    }
}

测试代码

public class ProxyTest {

    public static void main(String[] args){     
        //1、静态代理
        Person person=new Man();
        StaticManProxy manProxy=new StaticManProxy(person);
        manProxy.eat("静态代理");   
    }
}

结果

静态代理 do something~
静态代理吃饭了

如上StaticManProxy 即为Man的代理类,同时为其他实现Person接口的代理类;

  • JDK动态代理实现

JDK动态代理通过反射实现,直接使用JDK的java.lang.reflect.Proxy.newProxyInstance方法生成代理;

实现代码如下

public class ProxyFactory {

    // 代理目标
    private Object target;

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

    public Object getProxy() {

        return Proxy.newProxyInstance(ProxyFactory.class.getClassLoader(),
                target.getClass().getInterfaces(),
                /**
                 * InvocationHandler接口只定义了一个invoke方法,因此对于这样的接口,
                 * 我们不用单独去定义一个类来实现该接口, 而是直接使用一个匿名内部类来实现该接口,new
                 * InvocationHandler() {}就是针对InvocationHandler接口的匿名实现类
                 */
                /**
                 * 在invoke方法编码指定返回的代理对象干的工作 proxy : 把代理对象自己传递进来 method:
                 * 把代理对象当前调用的方法传递进来 args: 把方法参数传递进来
                 * 
                 * 当调用代理对象的方法时,
                 * 实际上执行的都是invoke方法里面的代码,
                 * 因此我们可以在invoke方法中使用method.getName()就可以知道当前调用的是代理对象的哪个方法
                 */
                new InvocationHandler() {

                    public Object invoke(Object proxy, Method method,
                            Object[] args) throws Throwable {
                        System.out.println("动态代理do something");

                        return method.invoke(target, args);

                    }
                });
    }

    public Object getProxyByLambda() {

        return Proxy.newProxyInstance(ProxyFactory.class.getClassLoader(),
                target.getClass().getInterfaces(), (proxy, method, args) -> {

                    System.out.println("Lambda动态代理do something");
                    return method.invoke(target, args);

                });
    }

}

测试代码段:

ProxyFactory proxy=new ProxyFactory(man);

        //2、匿名函数 模式
        Person p1=(Person)proxy.getProxy();
        System.out.println(p1.eat("匿名函数"));

        //3、Lambda 模式
        Person p=(Person)proxy.getProxyByLambda();

        System.out.println(p.eat("Lambda模式"));

结果

动态代理do something
匿名函数吃饭了

Lambda动态代理do something
Lambda模式吃饭了

如上,JDK动态代理通过Proxy.newProxyInstance直接生成代理对象,如需通过代理实现事物处理或者日志添加,则可通过以上代理类直接在代理Factory中增加对应需要处理的逻辑即可,代码调用时只需要将目标类传入则实现了对应的增强功能。针对所有接口实现类通用。

  • CGLIB动态代理

Spring中继承了CGLIB代理相关的代码,所以实现CGLIB代理只需要导入spring-core的jar包即可。

CGLIB代理的目标类不需要实现任何接口;

public class CgLibMan{

    public String eat(String name){
        System.out.println(name+"吃饭了");
        return "";
    }

    public String sleep(String name){
        System.out.println(name+"睡觉了");
        return "";
    }   
}

CGLIB代理实现类需要实现MethodInterceptor接口。如下:

public class CgLibProxyFactory implements MethodInterceptor{

    //维护目标对象
    private Object target;

    public CgLibProxyFactory(Object target){
        this.target=target;
    }

    //给目标对象创建一个代理对象
    public Object getCgLibProxy(){
        //1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {

        System.out.println("开始事务...");

        //通过反射 执行目标对象的方法
        Object returnValue = method.invoke(target, args);
        proxy.invokeSuper(o, args);

        System.out.println("提交事务...");

        return returnValue;

    }

}

测试代码段:

        //4、Cglib实现代理
        //目标对象
        CgLibMan ldhCg=new CgLibMan();
        //代理对象
        CgLibMan proxyCg=(CgLibMan) new CgLibProxyFactory(ldhCg).getCgLibProxy();
        proxyCg.eat("Cglib");

你可能感兴趣的:(Java)