介绍
代理是一种设计模式,提供了对目标对象另外的访问方式,通过代理对象来访问目标对象,这样可以扩展目标对象的功能,对目标对象功能做控制。
类图如下
静态代理
定义一个目标对象和代理对象都需要实现的接口
/**
* 可以唱歌的
*/
public interface Singable {
/**
* 唱歌
*/
void sing();
}
目标对象
/**
* 歌手
*/
public class Singer implements Singable {
@Override
public void sing() {
System.out.println("I am singing...");
}
}
代理对象
/**
* 歌手经纪人
*/
public class SingerAgent implements Singable {
private Singable delegate;
public SingerAgent(Singable delegate) {
this.delegate = delegate;
}
@Override
public void sing() {
System.out.println("before sing...");
delegate.sing();
System.out.println("after sing...");
}
}
客户端调用
public class Client {
public static void main(String[] args) {
Singer singer = new Singer();
SingerAgent singerAgent = new SingerAgent(singer);
singerAgent.sing();
}
}
输出结果
before sing...
I am singing...
after sing...
这个例子就相当于我们想找周杰伦唱歌,必须先找周杰伦经纪人,经纪人负责费用等唱歌前以及唱歌后的工作,周杰伦就负责唱歌。
静态代理的缺点:
代理和目标对象必须实现相同的接口,接口有变化,目标对象和代理都要维护。为了解决这个缺点,我们可以使用动态代理。
动态代理
动态代理其实是可以不实现接口的,但jdk动态代理必须实现,后续的如cglib实现动态代理就不需要了。今天的例子为jdk动态代理。
/**
* 可以唱歌的
*/
public interface Singable {
/**
* 唱歌
*/
void sing();
}
/**
* 歌手
*/
public class Singer implements Singable {
@Override
public void sing() {
System.out.println("I am singing...");
}
}
动态代理处理
public class SingerInvocationHandler implements InvocationHandler {
private Object delegate;
public SingerInvocationHandler(Object delegate) {
this.delegate = delegate;
}
/**
* 动态代理调用方法
*
* @param proxy 生成的代理对象
* @param method 代理的方法
* @param args 方法参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before sing ");
// 目标对象方法调用
Object ret = method.invoke(delegate, args);
System.out.println("after sing ");
return ret;
}
/**
* 创建代理对象
* @return
*/
public Singable newProxyInstance() {
return (Singable) Proxy.newProxyInstance(
delegate.getClass().getClassLoader(),
delegate.getClass().getInterfaces(),
this);
}
}
InvocationHandler 顾名思义为调用处理器,每一个代理对象实例都会有一个调用处理器对象。创建代理对象主要使用的时Proxy的newProxyInstance方法,
方法参数依次为
- 加载代理类的类加载器
- 代理类需要实现的接口
- 调用处理器
public class Client {
public static void main(String[] args) {
//保存创建的代理类
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", Boolean.TRUE.toString());
Singer singer = new Singer();
SingerInvocationHandler singerInvocationHandler = new SingerInvocationHandler(singer);
Singable proxy = singerInvocationHandler.newProxyInstance();
proxy.sing();
}
}
输出结果为
before sing
I am singing...
after sing
生成的代理类为
public final class $Proxy0 extends Proxy implements Singable {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void sing() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
// 初始化方法
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.imooc.sourcecode.java.dynamicproxy.jdk.test2.Singable").getMethod("sing");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
jdk帮我们创建了4个方法,equals,toString,hashCode3个方法都是Object类的,sing方法是目标接口的。
总结
代理模式的本质就是控制对象访问。