Java基础-静态代理以及动态代理

动态代理:

在了解动态代理之前,先对代理有一个认识.

代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。

打个比方:你买火车票的时候,并不直接花钱购买, 而是将钱预先垫付到抢票软件上, 使抢票软件为你购买, 你要做的行为就是买票,抢票软件就是你的代理

代理对象控制对被代理对象的访问:

这是代理的通用模型图

  • Subject:定义了被代理角色和代理角色的共同接口或者抽象类,也就是subject中定义了共同接口opration();

  • Realsubject:实现或者继承抽象主题角色,定义实现具体业务逻辑的实现。

  • Proxy:也就是代理人,porxy持有Realsubject的引用控制和实现. 并且有自己的处理逻辑.

代理分为静态和动态两种,先了解静态代理,知道其缺点后,再了解动态代理,会豁然开朗.

静态代理的作用:

静态代理通常用于对原有业务逻辑的扩充。比如持有二方包的某个类,并调用了其中的某些方法。然后出于某种原因,比如记录日志、打印方法执行时间,但是又不好将这些逻辑写入二方包的方法里。所以可以创建一个代理类实现和二方方法相同的方法,通过让代理类持有真实对象,然后在原代码中调用代理类方法,来达到添加我们需要业务逻辑的目的。

静态代理的实现:

这里方便理解 引入的Proxy是自己定义的,在下面动态代理的时候会使用java.reflect中提供的Proxy

package proxy;  
//将业务层抽象出来的接口.
public interface Subject {  
    void doSomething();
}


package proxy;

//真实对象,也就是被代理者,理解为你自己就可以了,要进行买票行为.
public class RealObject implements Subject{
    
    @Override
    public void doSomething() {

        System.out.println("这里是真实对象");

    }
}


package proxy;

//代理者,进行代理事务的人,理解成抢票软件,要代替你进行买票行为.
public class Proxy implements  Subject{

    private Subject realObject;

     Proxy(Subject realObject){
        this.realObject=realObject;
        //从这里知道需要代理的人是谁
    }
    
    @Override
    public void doSomething() {
        System.out.println("这里是代理对象");
        //可以在调用真实对象前增加操作
        //例如在开抢前告诉你,抢票要开始了.
        realObject.doSomething();//在这里调用真实的代理对象
        //可以在调用真实对象后增加操作.
        //在抢票结束后,告诉你成功或者失败.
    }
    
}

执行:

package proxy;

public class ProxyTest {


    public static void main(String[] args){

        RealObject realObject =new RealObject();
        realObject.doSomething();//被代理对象做的事,或者说委托人想要做的事

        System.out.println("-----------------");
        Proxy proxy =new Proxy(new RealObject());//告知代理对象被代理对象是谁,或者说告诉执行者委托人要做什么.
        proxy.doSomething();//通过代理对象调用被代理对象,实现代理.

        /*
        代理和被代理是不是有点晕,换一种说法来说.
        以DOTA为例:
        大哥本来要自己拉野,忽然发现一大波线袭来,于是就和酱油说,帮我拉两波野,一会带你喝汤! 大哥就是被代理者
        酱油说: 是大哥! 酱油在去拉野的路上,吃了赏金符,拉完野之后配合队友杀了一波人. 酱油就是代理者
        吃赏金符就是预处理.
        杀人就是结果处理.
         */
    }

}

大家有没有发现,这样虽然形成了代理行为,但是写法恨死,耦合度很高,现在是一个火车票,如果有飞机票,演唱会票等等一系列的需求和一系列的雇主,会导致代理类的代码量越来越大,并且会有重名的危险,这也就是静态代理的缺点.

为了解决这个问题,大佬们发明出了动态代理的方法.
JAVA为我们提供了动态代理的接口,使用起来非常方便,下边来了解一下.

需要将要扩展的功能写在一个InvocationHandler 实现类里:

这个Handler中的invoke方法中实现了代理类要扩展的公共功能。

静态代理的缺点:
有十个不同的RealObject,同时我们要去代理的方法是不同的,比要代理方法:doSomething、doAnotherThing、doTwoAnotherThing.

方法1:为每个代理创建代理类,这样会造成数个类
方法2: 在一个代理类中创建数个代理方法,当代理方法越来越多的时候容易造成代理类中的代码量越来越大 并且有重名的风险.

为此动态代理诞生了:

动态代理的使用:

先看一下动态代理的接口:

  1. 实现InvocationHandler接口

我们来看一下接口的结构:

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable; 
//这个接口只有一个方法 就是invoke方法,也就是说我们只要实现invoke方法就可以了
}

虽然这个参数不用我们传入,但是还是需要了解一下,第一个就是代理类的实例,第二个是方法, 第三个是对象的参数数组.

2.通过newProxyInstance方法创建代理实例:

newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 

通过这个方法返回我们代理类的实例, 也就是RealObject的Proxy

返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序

从参数上看 第一个参数是类加载器, 第二个方法是个Class对象数组接口,第三个参数是我们上边实现的InvocationHandler

在这里不贴源码了,有兴趣的可以自己追踪或者查看下方我的参考资料.

动态代理实例:

沿用上边静态代理的类,下面只写测试类和处理类.

处理类:

import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DynamicProxyHandler implements InvocationHandler {

    private Object realObject;

    Object bind(Object realObject){

        this.realObject=realObject;//给真实对象赋值


        return    Proxy.newProxyInstance(realObject.getClass().getClassLoader(),
                                       realObject.getClass().getInterfaces(),
                                        this);
        //通过Class对象得到该对象的构造器,接口方法数组,传入自己构造后的实例.
        //java.reflect.Proxy方法最后返回的是一个代理实例.
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在这里拓展代理逻辑

        System.out.println("rua!");

        Object result= method.invoke(realObject,args);//方法的调用.
        //这里的invoke调用是java进行调用,proxy对象,方法,和参数数组已经是我们之前传入的被代理类的了.

        return result;
    }
}

测试类:

public class DynamicProxy {

    public static void main(String[] args){


        DynamicProxyHandler pHandler=new DynamicProxyHandler();//创建我们动态代理处理类的对象
        Subject proxy=(Subject)pHandler.bind(new RealObject());//对其进行动态代理的参数绑定然后返回一个代理实例
        proxy.doSomething();//调用    
    }
}

我第一次看的时候也是云里雾里,不明白为什么这样就动态了, 这里的方法调用,都与方法对象无关,不直接进行调用.
不管你在Subject中定义了什么接口,都与调用无关,依靠反射,实时获取你的方法,参数,类的数据,再通过反射进行调用.

有十个不同的RealObject,同时我们要去代理的方法是不同的,比要代理方法:doSomething、doAnotherThing、doTwoAnotherThing.
以这个例子来说, 我传入10个不同的对象,对象中自然有自己的实现,我通过反射拿到实现后构建新的对象,不会对原有对象造成影响,再对新的对象进行方法调用操作,来100个 1000个对象也同样可以.

上面一切的条件都是建立在反射的基础上,如果反射了解的不太清楚,请返回去看反射,我的博客也有写,也可以通过网络自行了解.

参考资料:

1.https://blog.csdn.net/wangqyoho/article/details/77584832
2.https://www.zhihu.com/question/20794107/answer/23330381
3.Thinking in java page 592-598

猜你喜欢

转载自www.cnblogs.com/Curry-Rice/p/8990904.html
今日推荐