设计模式之结构模式(三)

版权声明:欢迎转载评论~哈哈哈哈请标明出处呀 https://blog.csdn.net/legendaryhaha/article/details/89005923

设计模式之结构模式(二)


定义

Proxy(代理),顾名思义,代替客户对象和目标对象打交道,这里可能有人会疑问这跟装饰器模式有啥区别,装饰器也是可以代替客户对象访问目标对象的。

确实如此,但是装饰器强调更多是拓展功能上的设计,而代理强调更多是对 对象的控制,而且这种对象往往是不能直接可得的,而装饰器模式中,只要愿意,还是可以直接访问的,如:

  • 跨服务器的访问
  • 对象状态很难判定,在目标对象工作真正开始工作前并不存在,如:Spring的AOP代理模式,Hibernate的延迟加载
  • 具有访问权限的一些特殊行为的对象,如在并发环境下,出于安全考虑,可能会采用代理来处理业务,而拒接直接的访问

下面从创建的角度,描述Java的三种代理模式:

传统的访问对象方式:

public Client{
	public void method(){
	// 创建一个FileUtil对象
	 	FileUtil obj  =  new FileUtil();
	 	// 调用对象的方法,将括号里的语句写到文件中
	 	obj.wirteToFile("your will,my hands")
	}
}

如果现在需要隐藏FileUtil对象,或者说FileUtil加了安全认证,对于直接访问它的请求,它是拒接的,此时,我们可以采用代理模式进行改进:

静态代理

//定义统一接口
public interface File {
    void writeToFile(String src);
}
// 具体业务
public class FileImpl implements File {
    @Override
    public void writeToFile(String src) {
        System.out.println("将"+src+"保存至文件中");
    }
}
// 代理服务
public class FileProxy implements File {
    private File target;
    public FileProxy(File target){
        this.target = target;
    }
    @Override
    public void writeToFile(String src) {
        System.out.println("开启事务");
        target.writeToFile(src);//执行目标对象的方法
        System.out.println("提交事务");
    }
}
public class MainApp {
    public static void main(String[] args) {
        //构建目标对象
        FileProxy proxy = new FileProxy(new FileImpl());
        proxy.writeToFile("your will, my hands");
    }
}

动态代理

上面程序是一个简单的静态代理处理的程序,它可以通过代理,在不修改目标对象的功能前提下对目标功能扩展,但是,因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类。同时,一旦接口增加方法,目标对象与代理对象都要维护,解决方式是通过借助JDK提供的api接口实现动态代理。

jdk中,在java.lang.reflect包中有Proxy类,它字节实现的方法有4个,而且都是静态的形式:
在这里插入图片描述
创建的时候我们需要用到的方法是newProxyInstance:
在这里插入图片描述
参数:

  • loader - 申明代理对象需要的类加载器(要和加载目标对象的类加载器一致)
  • interfaces - 通过接口指定生成哪个对象的代理对象
  • h -自己编写handler接口的实现来指定生成的代理对象的方法里干什么事

在InvocationHandler中的invoke方法又有三个参数:

  1. proxy:代理类实例
  2. method:Method类的引用,通过它可以找到例子中的writeToFile()方法
  3. args:表示方法的参数
public interface File {
    void writeToFile(String src);
}

public class FileImpl implements File {
    @Override
    public void writeToFile(String src) {
        System.out.println("将"+src+"保存至文件中");
    }
}

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

public class DynamicProxy {

    //维护一个对象
    private Object target;
    public DynamicProxy(Object target){
        this.target = target;
    }

    public Object getProxObject(){

        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),  //目标对象的类加载器
                target.getClass().getInterfaces(), //  目标对象的接口
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("开启事务");
                        //执行事务
                        Object result = method.invoke(target,args);
                        System.out.println("提交事务");
                        return result;
                    }
                }
        );
    }
}

public class MainApp {
    public static void main(String[] args) {
        //构建目标对象
        FileProxy proxy = new FileProxy(new FileImpl());
        proxy.writeToFile("your will, my hands");
    }
}

Cglib(code generation library)代理

CGLIB是一个强大的高性能的代码生成包,它广泛的被许多AOP的框架使用,譬如Spring中拦截器设置。它可以弥补Java自带的Proxy框架的不足。

在上文中,我们通过静态代理和动态代理来实现请求的间接转发,但他们都有一个缺点,就是要求目标对象是实现一个接口的目标对象(FileImpl 要实现File接口),但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用Cglib代理以目标对象子类的方式类实现代理。

说明:Cglib需要我们引入jar包,这里有Maven依赖可直接用,如果有Spring了,也可以直接用pring-core-3.2.5.jar架包。

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

还是以上面说的File类为例子,此时,我们将String保存到文件的类没有实现接口了,而是直接作为单独的一个类存在。

package com.fang.facade;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class File{
    public void writeToFile(String src) {
        System.out.println("将"+src+"保存至文件中");
    }
}

class ProxyFactory implements MethodInterceptor{
    //维护的对象
    private Object target;

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

    //给目标对象创建一个代理对象
    public Object getProxy(){
        //创建工具类
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(target.getClass());
        //设置回调函数
        enhancer.setCallback(this);
        //创建代理对象
        return enhancer.create();
    }
    @Override
    public Object intercept(Object obj, Method method, Object[] args,
                            MethodProxy proxy) throws InvocationTargetException, IllegalAccessException {
        System.out.println("开启事务");
        //执行目标对象的方法
        Object value = method.invoke(target,args);
        System.out.println("提交事务");
        return value;

    }
}

class App {

public static void main(String[] args) {
    //目标对象
    File target = new File();

    //代理对象
    File proxy = (File)new ProxyFactory(target).getProxy();

    //执行代理对象的方法
    proxy.writeToFile("your will my hands");

}
}

小结

通过测试,CGLIB创建的动态代理对象比JDK创建的动态代理对象花费的时间要高的多,就上面的简单例子而言,运行时间相差就已经很明显了。

CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。在Spring的AOP编程中,如果加入容器的目标对象有实现接口,用JDK代理如果目标对象没有实现接口用Cglib代理


代理的分类

在这里插入图片描述

关于代理,其实有很多实际应用的例子,像远程代理中的RMI,remote method invoke,远程方法调用框架,还有RPC,Remote Procedure Call,远程过程调用,都是代理模式的体现。

猜你喜欢

转载自blog.csdn.net/legendaryhaha/article/details/89005923