设计模式——Proxy代理模式
Proxy代理模式
为其他对象提供一种代理,以控制这个对象的访问。代理对象起到中介作用,可以去掉功能服务或增加额外的服务。
几种常见的代理模式:
远程代理:为不同地理的对象提供局域网代表对象。
虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候进行创建。
保护代理:控制用户的访问权限。
智能引用代理:提供对目标对象额外服务。
静态代理
代理和被代理对象在代理之前是确定的,他们都实现相同的接口或者继承相同的抽象类。代理类可以继承或聚合被代理类,以便于扩充和删减功能。(有点像装饰器模式)
举栗:
Moveable.java
public interface Moveable {
void move();
}
Car.java
接口的实现类。
public class Car implementsMoveable {
@Override
public void move() {
try {
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽车行驶中...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
CarLogProxy.java
记录日志代理,实现Moveable接口
public class CarLogProxy implements Moveable {
public CarLogProxy(Moveable m) {
super();
this.m = m;
}
private Moveable m;
@Override
public void move() {
System.out.println("日志开始....");
m.move();
System.out.println("日志结束....");
}
}
CarTimeProxy.java
记录行驶时间代理,实现Moveable接口。
public class CarTimeProxy implements Moveable {
public CarTimeProxy(Moveable m) {
super();
this.m = m;
}
private Moveable m;
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶....");
m.move();
long endTime = System.currentTimeMillis();
System.out.println("汽车结束行驶.... 汽车行驶时间:"
+ (endTime - startTime) + "毫秒!");
}
}
Test.java
使用聚合的方式、实现同样的接口来实现功能的扩充。
输出:
汽车开始行驶....
日志开始....
汽车行驶中...
日志结束....
汽车结束行驶.... 汽车行驶时间:147毫秒!
public class Test {
public static void main(String[] args) {
Car car = new Car();
CarLogProxy clp = new CarLogProxy(car);
CarTimeProxy ctp = new CarTimeProxy(clp);
ctp.move();
}
}
动态代理
Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
1. Interface InvocationHandler:该接口仅定义了一个方法
public object invoke(Object obj, Method method, Object[] args)在代理实例上处理方法调用并返回结果。第一个参数obj一般是指代理类,method是被代理的方法,args为该方法的参数数组。这个抽象方法在代理类中动态实现。
2. Proxy:该类即为动态代理类
static ObjectnewProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在接口中声明过的方法)。
动态代理实现步骤:
1. 创建一个实现接口InvocationHandler的类,必须实现invoke方法
2. 创建被代理的类以及接口
3. 调用Proxy的静态方法newProxyInstance创建一个代理类
4. 通过代理调用方法。
上面的栗子稍作修改:
Moveable.java
这里再多声明一个方法fun,用来测试
public interface Moveable {
void move();
void fun(String s);
}
Car.java
Moveable接口的一个实现类
public class Car implementsMoveable {
@Override
public void move() {
try {
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽车行驶中...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void fun(String s) {
System.out.println("fun方法+"+s);
}
}
TimeHandler.java
实现InvocationHandler接口,实现invoke方法。该方法在代理实例上处理方法调用并返回结果,被代理类的任何一个方法的调用都会被该代理处理,下面使用接口中的两个方法进行测试。
public class TimeHandler implements InvocationHandler {
public TimeHandler(Object target) {
super();
this.target = target;
}
private Object target;
/*
* 参数:
* proxy 代理对象
* method 被代理对象的方法
* args 方法的参数
*
* 返回值:
* Object 方法的返回值
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long starttime = System.currentTimeMillis();
System.out.println("汽车开始行驶....");
if(args!=null)
System.out.println(method.getName()+args.length);
else
System.out.println(method.getName());
method.invoke(target,args);
long endtime = System.currentTimeMillis();
System.out.println("汽车结束行驶.... 汽车行驶时间:"
+ (endtime - starttime) + "毫秒!");
return null;
}
}
Test.java
测试类,输出:
汽车开始行驶....
move
汽车行驶中...
汽车结束行驶.... 汽车行驶时间:18毫秒!
汽车开始行驶....
fun1
fun...123
汽车结束行驶.... 汽车行驶时间:0毫秒!
从上面的输出可以看出代理类可以实现并修改被代理类的一些功能,并且每个方法都是通过invoke来通过反射机制执行。方法调用的对象,方法自身的Method对象,方法的参数,这些都可以获得并进行处理。
public class Test {
/**
* JDK动态代理测试类
*/
public static void main(String[] args) {
Car car = new Car();
InvocationHandler h = new TimeHandler(car);
Class<?> cls = car.getClass();
/**
* loader 类加载器
* interfaces 实现接口
* h InvocationHandler
*/
Moveable m = (Moveable)Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(), h);
m.move();
m.fun("123");
}
}
若是添加多层代理,可以在测试类中继续编写,当然这里要写一个LogHandler的实现类,这里就不贴代码了,和TimeHandler类似。
//多添加一层代理
InvocationHandlerlogHandler = new LogHandler(m);
Moveable m2=(Moveable) Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(), logHandler);
m2.move();
也可以使用cglib实现动态代理,与JDK动态代理区别:
JDK动态代理只能代理实现了接口的类,而cglib是针对类来实现代理的,对指定目标类产生一个子类,通过方法拦截技术拦截所有父类方法的调用(不能对final修饰的类进行代理)。
总结
简单来说,动态代理实现过程就是定义接口,定义代理类,拿到代理类的实例,通过代理类的实例调用被代理类的一些功能。这其中涉及到根据反射机制生成java代码,编译成字节码,加载到内存,调用等。大概理解一下整体思路,对应上面的例子加深理解,必要时可以阅读相关源码,查看实现细节。