动态代理模式

动态代理模式

  • Jdk动态代理
  • Cglib动态代理

动态代理就是在实现阶段不用关心代理谁,而在运行期才指定代理哪一个对象

Jdk动态代理

游戏大家可能都玩过,但是一些游戏升级就很难或者说是很耗费时间,所以就有好多人去找游戏代练进行升级。

public interface IGamePlayer {
    void play();
}
public class GamePlayer implements IGamePlayer {
    @Override
    public void play() {
        System.out.println("玩游戏,升级...");
    }
}

好了我们已经定义好了自己玩游戏的接口和类了,现在我们再去找个代理帮我们玩

public class JdkProxy implements InvocationHandler {

    //目标对象
    private Object target;

    /**
     * 建立代理对象和真实对象的代理关系,并返回代理对象
     * @param object    真实对象
     * @return      代理对象
     *
     * 其中newProxyInstance方法包含了3个参数:
     *  (1)第一个是类加载器,采用了target本身的类加载器
     *  (2)第二个是把生成的动态代理对象挂在哪些接口下,这个就是挂在target实现的接口下
     *  (3)第三个定义实现方法逻辑的代理类,this表示当前对象
     */
    public Object getProxy(Object object){
        this.target = object;
        Object proxy = Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
        return proxy;
    }

    /**
     * @param proxy     代理对象
     * @param method    当前调度方法
     * @param args      当前方法参数
     * @return          代理结果返回
     * @throws Throwable        异常
     *
     * invoke方法可以实现代理逻辑,invoke方法3个参数含义如下:
     * (1)proxy:代理对象,就是bind生成的对象
     * (2)method:当前调度的方法。
     * (3)args:调度方法的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(target,args);
    }
}

我们可以看到上述动态代理类,在代码中并不用向我们的静态代理一样,需要把GamePlayer类指定进行,我们在运行过程中,确定了要代理GamePlayer类时,再指定就可以啦

下面我们就在场景类中测试一下

public class Client {
    public static void main(String[] args) {
        IGamePlayer gamePlayer = new GamePlayer();
        JdkProxy jdkProxy = new JdkProxy();
        //获取代理对象,因为挂在接口IGamePlayer下,所以声明代理对象IGamePlayer
        IGamePlayer proxy = (IGamePlayer) jdkProxy.getProxy(gamePlayer);
        proxy.play();
    }
}

打印结果

玩游戏,升级...

那动态代理有什么好处了,我们可以看看我们之前 静态代理模式 中只指定代理了一个接口,万一我们想要代理两个怎么办?
要么我们写两个类分别继承两个接口

public interface class1 implements interface1{
}
public interface class2 implements interface2{
}

目标对象这样,那么我们的代理对象肯定要要分为两个类,分别代理这两个对象,这样类就太多了,那么我们就只能使用继承多个接口的方法

public interface class1 implements interface1, interface1{
	@Override
	public void method1(){
	}
    @Override
	public void method2(){
	}
}

这样但是万一我们在重写接口方法 (method1, method2) 中invoke目标方法时在调用前、调用后分别进行一些处理,那就会造成代码冗余。

那我们的动态代理可以解决吗?
比如说游戏代练除了帮人代练游戏,还帮人代卖的游戏账号

public interface ISellAccount {
    void sell();
}

那我们的游戏者再多实现一个ISellAction的接口

public class GamePlayer implements IGamePlayer, ISellAccount {
    @Override
    public void play() {
        System.out.println("玩游戏,升级...");
    }

    @Override
    public void sell() {
        System.out.println("出售游戏账号...");
    }
}

至于我们的动态代理类都不需要变动,只要在场景类中指定我们想要代理的接口就好

public class Client {
    public static void main(String[] args) {
        IGamePlayer gamePlayer = new GamePlayer();
        JdkProxy jdkProxy = new JdkProxy();
        //如想要出售游戏账号
        ISellAccount proxy1 = (ISellAccount) jdkProxy.getProxy(gamePlayer);
        proxy1.sell();
        //如想要找代练升级
        IGamePlayer proxy2 = (IGamePlayer) jdkProxy.getProxy(gamePlayer);
        proxy2.play();;
    }
}

如上我们也应该清楚了,在我们的Jdk动态代理中,被代理的目标对象必须要有接口,因为我们Proxy.newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)的第二个参数需要传入目标对象实现的接口,用于我们将生成的代理对象挂在哪一个接口之下(可多个接口,使用方法如上)。

Cglib动态代理

jdk动态代理我们发现被代理的目标对象必须要有接口,但是有的类没有接口怎么办呢,就不能使用代理了么,可以,那就需要使用到我们的Cglib动态代理啦

要是我们不是在Spring环境下,使用Cglib需要引入相应的jar包,依赖如下,也可以下载相应的 .jar文件导入项目中

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.12</version>
</dependency>

cglib动态代理类如下

public class CglibProxy implements MethodInterceptor {

    public Object getProxy(Class clazz){
        //CGLIB enhancer增强类对象
        Enhancer enhancer = new Enhancer();
        //设置增强类型(父类)
        enhancer.setSuperclass(clazz);
        //定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法
        enhancer.setCallback(this);
        //生成并返回代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //proxy是代理后的子类
        return methodProxy.invokeSuper(proxy, args);
    }
}

在Client类中,用cglib动态代理为GamePlayer生成一个代理对象,并调用其方法

public class Client {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        GamePlayer proxy = (GamePlayer) cglibProxy.getProxy(GamePlayer.class);
        proxy.play();
    }
}
玩游戏,升级...

我们在上述过程中就会发现,cglib动态代理就没有像jdk动态代理上代理的目标对象有着接口的要求。

你可能感兴趣的:(设计模式)