代理模式
[TOC]
1 静态代理和动态代理
代理的概念:为某个对象提供一个代理,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。
白话文:简单的说,就是类似于买东西,厂商负责生产,货物交给商店来代理出售。客人跟商店来交互,但是其实买的是厂商的东西。
静态代理
package com.proxy.inter;
/**
* 定义Demo接口
*/
public interface Demo {
public void save();
}
package com.proxy.impl;
import com.proxy.inter.Demo;
/**
* DemoImpl实现Demo接口并覆写save()方法
* 真实主题,执行具体业务
*/
public class DemoImpl implements Demo {
public void save() {
System.out.println("调用save()方法");
}
}
package com.proxy.impl;
import com.proxy.inter.Demo;
/**
* DemoImplProxy 也实现了Demo接口,并覆写了save()方法,增加了自己的业务
* 代理主题,负责其他业务的处理
*/
public class DemoImplProxy implements Demo {
Demo demoImpl = new DemoImpl();
public void save() {
System.out.println("开始记录日志");
demoImpl.save();
System.out.println("开始结束日志");
}
}
package com.proxy.impl;
import com.proxy.inter.Demo;
/**
* 开始记录日志
* 调用save()方法
* 开始结束日志
*/
public class Test {
public static void main(String[] args) {
Demo demoImplProxy = new DemoImplProxy();
demoImplProxy.save();
}
}
静态代理有一个缺点,每个代理类只能为一个接口服务,这样程序开发中必然会产生许多的代理类
所以我们就会想办法可以通过一个代理类完成全部的代理功能,那么我们就需要用动态代理
在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler 接口和 java.lang.reflect.Proxy 类的支持。
动态代理
java.lang.reflect.InvocationHandler接口的定义如下:
/**
*
*Object proxy:被代理的对象
*Method method:要调用的方法
*Object[] args:方法调用时所需要参数
*/
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
java.lang.reflect.Proxy类的定义如下:
/**
*CLassLoader loader:类的加载器
*Class<?> interfaces:得到全部的接口
*InvocationHandler h:得到InvocationHandler接口的子类的实例
*/
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
具体实现
package com.proxy.inter;
/**
* 定义DemoFirst接口
*/
public interface DemoFirst {
public void saveFirst();
}
package com.proxy.impl;
import com.proxy.inter.DemoFirst;
/**
* DemoFirstImpl实现DemoFirst接口,覆写saveFirst()方法
* 真实主题,负责执行具体业务
*/
public class DemoFirstImpl implements DemoFirst {
@Override
public void saveFirst() {
System.out.println("调用saveFirst()方法");
}
}
package com.proxy.impl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* InvocationHandlerImple实现InvocationHandler接口,覆写invoke()方法
* 代理主题的业务写在invoke()方法中
*/
public class InvocationHandlerImpl implements InvocationHandler {
private Object target;
public InvocationHandlerImpl(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("target : " + target.getClass().getName());
System.out.println("proxy : " + proxy.getClass().getName());
System.out.println("method : " + method.getName());
System.out.println("args : " + args);
System.out.println("开始记录日志");
Object obj = method.invoke(target, args);
System.out.println("结束记录日志");
/*
* System.out.println("obj : " + obj.getClass().getName());
* 本例中saveXXX方法没有返回值所以obj会报空指针异常
*/
return obj;
}
}
package com.proxy.impl;
import java.lang.reflect.Proxy;
import com.proxy.inter.DemoFirst;
/*import com.proxy.inter.DemoSecond;*/
public class Test {
public static void main(String[] args) {
DemoFirst first = new DemoFirstImpl();
/*DemoSecond second = new DemoSecondImpl();*/
//取得代理对象
DemoFirst firstProxy = (DemoFirst) Proxy.newProxyInstance(first
.getClass().getClassLoader(), first.getClass().getInterfaces(),
new InvocationHandlerImpl(first));
//通过动态代理调用方法
firstProxy.saveFirst();
/*DemoSecond secondProxy = (DemoSecond) Proxy.newProxyInstance(second
.getClass().getClassLoader(), second.getClass().getInterfaces(),
new InvocationHandlerImpl(second));
secondProxy.saveSecond();*/
}
}
优缺点
缺点:
- 如果是代理类,那么调用getclass等方法的时候,会无法得到预期的结果。
代理模式的应用
代理模式是非常好用的,在很多的地方都有着用途。比如spring的AOP,
代理模式有多种应用场合,如下所述:
- 远程代理,也就是为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实。比如说 WebService,当我们在应用程序的项目中加入一个 Web 引用,引用一个 WebService,此时会在项目中声称一个 WebReference 的文件夹和一些文件,这个就是起代理作用的,这样可以让那个客户端程序调用代理解决远程访问的问题;
- 虚拟代理,是根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象。这样就可以达到性能的最优化,比如打开一个网页,这个网页里面包含了大量的文字和图片,但我们可以很快看到文字,但是图片却是一张一张地下载后才能看到,那些未打开的图片框,就是通过虚拟代里来替换了真实的图片,此时代理存储了真实图片的路径和尺寸;
- 安全代理,也可以是权限代理。用来控制真实对象访问时的权限。一般用于对象应该有不同的访问权限的时候;
- 延迟加载,用代理模式实现延迟加载的一个经典应用就在 Hibernate 框架里面。当 Hibernate 加载实体 bean 时,并不会一次性将数据库所有的数据都装载。默认情况下,它会采取延迟加载的机制,以提高系统的性能。Hibernate 中的延迟加载主要分为属性的延迟加载和关联表的延时加载两类。实现原理是使用代理拦截原有的 getter 方法,在真正使用对象数据时才去数据库或者其他第三方组件加载实际的数据,从而提升系统性能。
网友评论