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

七、代理模式(Proxy)

代理模式是指“为其他对象提供一种代理以控制这个对象的访问”。
这里写图片描述
抽象主题角色(Subject):声明了代理对象和目标对象的公共接口,是任何需要目标对象的地方都使用代理对象。
代理主题角色(ProxySubject):含有目标对象的引用,从而可以在任何时候操作目标对象。
真实主题角色(RealSubject):定义了代理角色所代表的具体对象。
有两种代理模式:静态代理、动态代理

1.静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.

//抽象主题
public interface Subject {
    //操作
    public void operation();
}

//目标对象角色
public class RealObject implements Subject {
    @Override
    public void operation() {
        //一些操作
        System.out.println("一些操作");
    }
}

//代理对象角色
public class ProxyObject implements Subject {
    RealObject realObject = new RealObject();
    @Override
    public void operation() {
        //调用目标对象之前可以做相关操作
        System.out.println("before");        
        realObject.operation();        
        //调用目标对象之后可以做相关操作
        System.out.println("after");
    }
}

2.动态代理

静态代理在运行之前就要写好代理类,这就造成大量的重复,而动态代理在运行时才回生成代理类。
动态代理有两种实现方式,一种是jdk方式,一种是cglib

2.1 jdk实现方式

在jdk的动态代理实现中,有两个非常重要的类和接口Proxy(Class)、InvocationHandler(Interface)。
jdk实现代理的方式其实是代理对象和目标对象实现同一个接口,因此目标对象必须要实现某个接口。
比如我有个人接口,有个findLove方法。

public interface Person {
    public String findLove();
}

有一个目标类实现了这个接口

public class Lina implements Person {
    @Override
    public String findLove() {
        System.out.println("高富帅");
        System.out.println("正直");
        return "完成";
    }
}

需要一个生成代理类的类

public class ProxyMeipo implements InvocationHandler {
    // 被代理对象
    private Object target;

    /**
     * 获取代理对象
     * @param target
     * @return
     * @throws Exception
     */
    public Object getInstance(Object target) throws Exception {
        this.target = target; // 将被代理对象保存
        Class<?> clazz = target.getClass();
        // 创建代理对象
        Object proxy = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
        return proxy;
    }

    /**
     * 利用反射调用真实对象的方法
     * @param proxy:代理对象
     * @param method:被代理对象的某个方法
     * @param args:参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().startsWith("find")) {
            System.out.println("开始物色");
            Object obj = method.invoke(target, args);
            System.out.println("相亲");
            return obj;
        }
        return null;
    }
}

测试代理

public class ProxyTest {

    public static void main(String[] args) throws Exception {
        ProxyMeipo meipo = new ProxyMeipo();
        Person person = (Person) meipo.getInstance(new Lina());
        person.findLove();
        System.out.println(person.getClass());
    }
}
2.2 cglib方式

cglib其实是通过继承目标类实现的代理,所以它不用目标对象一定实现某个接口
任意一个目标类

public class Barry {
    public String findLove() {
        System.out.println("高富帅");
        System.out.println("正直");
        return "完成";
    }
}

动态生成代理类,需要实现MethodInterceptor,去看源码会发现这个接口只定义了一个方法

public class ProxyMeipo implements MethodInterceptor {

    private Object target;

    public Object getInstance(Object target) {
        this.target = target;
        // 工具类
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass()); // 设置父类
        enhancer.setCallback(this); // 设置回调函数
        return enhancer.create();
    }

    /**
     *
     * @param o:代理类对象
     * @param method:目标对象方法
     * @param objects:参数
     * @param methodProxy:代理对象方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开始物色");
        // 两种调用目标方法的方式
        Object obj = methodProxy.invokeSuper(o, objects);   // 调用父类的目标方法,也就是目标方法
//        Object obj = method.invoke(target, objects);
        System.out.println("相亲");
        return obj;
    }
}

测试

public class ProxyTest {
    public static void main(String[] args) {
        ProxyMeipo meipo = new ProxyMeipo();
        Barry barry = (Barry) meipo.getInstance(new Barry());
        System.out.println(barry.findLove());
        System.out.println(barry.getClass().getSuperclass());
    }
}
2.3 自己动手实现jdk方式的动态代理

看到上面2.1 jdk动态生成代理类的方法(Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this))需要Proxy、ClassLoader和反射调用方法的接口InvocationHandler。
首先我们要一个GPInvocationHandler

public interface GPInvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
}

其次,我们需要一个将代理类加载到jvm中的GPClassLoader(ClassLoader是一个abstract的类,具体怎么加载以后会再做说明)

public class GPClassLoader extends ClassLoader {

    public GPClassLoader() {
    }

    // 执行父加载器的loadClass方法 如果向上委托父加载器没有加载成功,则通过findClass(String)查找。
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String className = GPClassLoader.class.getPackage().getName() + "." + name;

        File classFile = new File(GPClassLoader.class.getResource("").getPath().replaceAll("\\.", "/") + name + ".class");
        FileInputStream in = null;
        ByteArrayOutputStream os = null;
        try {
            if (classFile.exists()) {
                in = new FileInputStream(classFile);
                os = new ByteArrayOutputStream();

                byte [] buff = new byte[1024];
                int len;
                while ((len = in.read(buff)) != -1){
                   os.write(buff,0,len);
                }
                //  加载到jvm
                return defineClass(className, os.toByteArray(), 0, os.size());
            }
       } catch (IOException e) {
            e.printStackTrace();
       } finally {
           if(null != in){
               try {
                   in.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
           if(os != null){
               try {
                   os.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
       }
        return null;
    }
}

最后我们需要一个生成代理类并返回代理对象的GPProxy

public class GPProxy {
    public static final String ln = "\r\n"; // 换行

    public static Object newProxyInstance(GPClassLoader classLoader, Class<?>[] interfaces, GPInvocationHandler h) {
        FileWriter writer = null;
        try {
            // 1.生成源码
            String src = generateSrc(interfaces);

            // 2.写入文件
            File f = new File(GPProxy.class.getResource("").getPath() + "$Proxy0.java");
            writer = new FileWriter(f);
            writer.write(src);
            writer.flush();
            writer.close();

            // 3.编译成class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
            Iterable iterable = manager.getJavaFileObjects(f);
            JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
            task.call();
            manager.close();

            //4、编译生成的.class文件加载到JVM中来
            Class proxyClass = classLoader.findClass("$Proxy0");
            Constructor c = proxyClass.getConstructor(GPInvocationHandler.class);
            f.delete();

            //5、返回字节码重组以后的新的代理对象
            Object obj = c.newInstance(h);
            return obj;

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private static String generateSrc(Class<?>[] interfaces) {
        StringBuffer sb = new StringBuffer();
        sb.append("package com.crystal.pattern.proxy.custom;" + ln);
        sb.append("import " + interfaces[0].getName() + ";" + ln);
        sb.append("import java.lang.reflect.Method;" + ln);
        sb.append("public class $Proxy0 implements " + interfaces[0].getSimpleName() + "{" + ln);
        sb.append("GPInvocationHandler h;" + ln);
        sb.append("public $Proxy0(GPInvocationHandler h) { " + ln);
        sb.append("this.h = h;");
        sb.append("}" + ln);

        for (Method m : interfaces[0].getMethods()){
            sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "() {" + ln);
            sb.append(m.getReturnType().getName() + " obj = null;" + ln);
            sb.append("try{" + ln);
            sb.append("Method m = " + interfaces[0].getSimpleName() + ".class.getMethod(\"" + m.getName() + "\",new Class[]{});" + ln);
            sb.append("obj = (" + m.getReturnType().getName() + ") this.h.invoke(this,m,null);" + ln);
            sb.append("}catch(Throwable e){" + ln);
            sb.append("e.printStackTrace();" + ln);
            sb.append("}" + ln);
            sb.append("return obj;" + ln);
            sb.append("}");
        }
        sb.append("}" + ln);
        return sb.toString();
    }
}

测试

public class GPTest {

    public static void main(String[] args) {
        GPProxyMeipo meipo = new GPProxyMeipo();
        Person person = (Person) meipo.getInstance(new Lina());
        person.findLove();
        System.out.println(person.getClass());
    }
}

这样我们就实现了动态生成代理对象。

上面讲述了适配器模式,代理模式和装饰模式,感觉好像有点类似,下面我们来看看它们之间有什么区别:

代理模式与适配器模式

模式名代理模式适配器模式
是否替一个对象提供间接性质的访问
接口实现和目标对象相同的接口主要用来处理接口间不匹配的问题,它往往替所适配的对象提供一个不同的接口


代理模式与装饰模式

模式名代理模式装饰模式
是否需要为转调其他对象的前后执行一定的功能
目的主要目的是控制对对象的访问动态的为某个类型添加新的职责,也就是说为了动态的添加功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值