一、代理的概念
代理(Proxy)是一种设计模式,提供了目标对象另外的访问方式,即通过代理对象访问目标对象。该设计模式,可以在目标对象已实现功能的基础上,增加额外的功能,即扩展目标对象。
二、静态代理
静态代理是对目标对象进行封装,对代理类的调用实际上会调用目标类。使用时,需要定义接口或者父类,代理类和目标类需要实现相同的接口或者继承相同的父类。
案例:
首先,我们有一个Star的接口,接口里面有sing和dance两个方法:
package com.code.proxy;
/**
* 明星接口
*/
public interface Star {
public void sing();
public void dance();
}
刘德华是一个大明星,它会sing、dance,下面是他的类:
package com.code.proxy;
/**
* 明星刘德华
*/
public class LiuDehua implements Star {
@Override
public void sing() {
System.out.println("I'm LiuDehua. Now follow me singing");
}
@Override
public void dance() {
System.out.println("I'm LiuDehua. Now follow me dancing");
}
}
我们知道,刘德华是一个名气很大的明星,每天都有很多导演、机构会找他演出。这些导演如果想约刘德华演戏,得先找刘德华的代理人,演出完了演出费用也是交给代理人:
package com.code.proxy;
/**
* 刘德华的代理人
*/
public class LiuDehuaProxy implements Star {
private Star star;
public LiuDehuaProxy(Star star) {
super();
this.star = star;
}
@Override
public void sing() {
System.out.println("唱前先跟我谈");
star.sing();
System.out.println("唱完了费用交给我");
}
@Override
public void dance() {
System.out.println("跳前先跟我谈");
star.dance();
System.out.println("跳完了费用交给我");
}
}
测试类:
package com.code.proxy;
/**
* 静态代理测试类
*/
public class TestStaticProxy {
public static void main(String[] args) throws Exception {
Star proxy = new LiuDehuaProxy(new LiuDehua());
proxy.sing();
proxy.dance();
}
}
静态代理类优缺点:
优点:
可以在不修改目标对象的前提下,扩展目标对象的功能
缺点:
1.每一个目标类都需要增加一个对应的代理类,如果需要代理的目标类比较多,就需要添加大量的代理类,代码重复很多
2.如果接口类新增一个方法,需要同步修改代理类
3.一个静态代理类只能代理一个目标对象,不能代理多个目标类
三、动态代理
动态代理的代理类的源码是在程序运行期间根据反射动态生成,然后通过类加载器加载到jvm的,不存在代理类的字节码文件。代理类和目标类的关系是在程序运行期间确定的。
与动态代理紧密相连的java api:
1.)java.lang.reflect.Proxy
这是java动态代理机制生成的所有代理类的父类,它提供了一组静态方法来为一组接口动态的生成代理类及其对象。
/**该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象*/
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces);
/**该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例 */
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
/**该方法用于判断指定类是否是一个动态代理类*/
public static boolean isProxyClass(Class<?> cl);
/**该方法用于获取代理对象所关联的调用处理器*/
public static InvocationHandler getInvocationHandler(Object proxy);
2.)java.lang.reflect.InvocationHandler
这是代理类指定的调用处理接口,它有一个invoke方法,用于集中处理在动态代理对象上的方法调用,通常在这个方法里实现对委托类的代理访问。每次动态生成代理对象时,都要指定一个对应的调用处理器对象。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
3.)java.lang.ClassLoader
这是类装载器,负责将类的字节码装载到jvm并为其定义类对象,然后该类才能被使用。Proxy生成动态代理类时同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由jvm在运行时动态生成的而非预存于任何一个class文件中。每次生成动态代理类时都需要指定类加载器。
案例:
上面的接口Star和刘德华的类LiuDehua不变,下面我们用动态代理的方法来实现上面的功能。
需要定义一个调用处理器,里面定义一个目标对象target:
package com.code.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 明星的代理类
*/
public class StarProxyHandler implements InvocationHandler {
//代理目标对象
private Object target;
//绑定目标对象
public void bind(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("签约前先跟我谈");
Object rs = method.invoke(target, args);
System.out.println("演出完了费用交给我");
return rs;
}
}
下面看测试代码:
package com.code.proxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* 动态代理测试类
*/
public class TestDymaticProxy {
public static void main(String[] args) throws Exception {
//目标对象刘德华
Star liuDehua = new LiuDehua();
//调用处理对象
StarProxyHandler handler = new StarProxyHandler();
//调用处理对象绑定目标对象刘德华
handler.bind(liuDehua);
//创建代理对象
Star proxy = (Star)Proxy.newProxyInstance(liuDehua.getClass().getClassLoader(), liuDehua.getClass().getInterfaces(), handler);
//调用代理对象
proxy.sing();
}
}
总结起来,动态代理的实现就包括下面两步:
//第一步:创建调用处理对象,实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(..);
//第二部:通过Proxy直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance(classLoader, new Class[] { Interface.class }, handler );
动态代理的源码解读见Java动态代理(二)。