代理模式:A调用B,由于某些原因(解耦合、保护B、扩展B),B不想让A直接调用,创建一个中间代理者C,由A调用C,由C中调用B的功能和C封装的东西。
优点:
缺点:
静态代理只能指定单一的被代理类,动态代理可以在运行时使用反射机制动态生成代理类
下面举三个例子,分别不使用代理,使用静态代理,使用动态代理来进行比较。
People接口含有一个方法say(),每个实现此接口的类都要输出自己的姓氏,但是有一天需求扩展,还要在执行 say() 之后输出一句代表新增扩展功能的字符串。
代码举例:
公共接口:People
// 人类接口
public interface People {
// 说话方法
public void say();
}
// 不使用代理类,实现人类接口
public class Ling implements People {
public void say() {
System.out.println("I am Ling ... ");
}
}
public class MyTest {
public static void main(String[] args) {
// 1、不使用代理
System.out.println("-- 不使用代理 --");
// 创建基本类对象
People ling = new Ling();
// 新增扩展功能
System.out.println("扩展功能 ... ");
// 执行基本类基本方法
ling.say();
}
}
不使用代理的时候,就只能指定实现类Ling,且执行 say() 方法,再单独执行代表扩展功能的字符串。如果有很多个地方都要执行 ling.say() ,当扩展功能时,那么每个调用 ling.say() 的地方都要添加扩展功能代码,就会显得十分繁琐。
优点: 简单明了
缺点: 扩展复杂,冗余度高
// 被代理类,实现人类接口
public class Wang implements People {
public void say() {
System.out.println("I am Wang ... ");
}
}
// 静态代理类,封装被代理类
public class WangSayProxy implements People {
// 内部创建被代理类
private Wang wang = new Wang();
public void say() {
// 新增扩展功能
System.out.println("扩展功能 ... ");
// 执行被代理类基本功能
wang.say();
}
}
public class MyTest {
public static void main(String[] args) {
// 2、使用静态代理
System.out.println("-- 使用静态代理 --");
// 创建静态代理类对象
People wangSayProxy = new WangSayProxy();
// 执行静态代理类方法
wangSayProxy .say();
}
}
使用静态代理,规定了 WangSayProxy 类为 Wang 类的静态代理类,调用此静态代理类,不仅可以实现调用 wang.say() ,还可以在静态代理类中进行功能增强,最后展示到外层。只要本来调用 wang.say() 的地方,都可以直接调用 wangSayProxy.say() ,如果 wang.say() 有扩展功能,直接在 WangSayProxy 类中就修改即可,减少代码耦合度。
优点: 减耦合,功能扩展方便
缺点: 被代理的对象比较单一,不够灵活
// 被代理类,实现人类接口
public class Zhan implements People {
public void say() {
System.out.println("I am Zhan ... ");
}
}
// 动态代理类,封装被代理类
public class PeopleSayProxyHandler implements InvocationHandler {
// 内部创建Object对象
private Object object;
// 有参构造(指定被代理的动态类型)
public PeopleSayProxyHandler(Object object) {
this.object = object;
}
// 动态生成被代理类
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 执行扩展功能
System.out.println("扩展功能 ... ");
// 执行动态被代理类的方法
method.invoke(object, args);
return null;
}
}
public class MyTest {
public static void main(String[] args) {
// 3、使用动态代理
System.out.println("-- 使用动态代理 --");
// 创建希望被动态代理的类
People zhan = new Zhan();
// 创建动态代理类(此处指定被动态代理对象,此对象为参数,运行时才能确定创建对象)
InvocationHandler peopleSayProxyHandler = new PeopleSayProxyHandler(zhan);
// 通过Proxy类的静态方法newProxyInstance返回一个接口的代理实例
People proxySay = (People) Proxy.newProxyInstance(zhan.getClass().getClassLoader(),
zhan.getClass().getInterfaces(), peopleSayProxyHandler);
// 执行动态代理类方法
proxySay.say();
}
}
使用静态代理,灵活的将被代理类的功能进行扩展。
可以发现,动态代理的关键点就是使用 Proxy 类的静态方法 newProxyInstance ,因此我们可以查看下源码:
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
// 判断对象为不为空
Objects.requireNonNull(h);
// 对象克隆
final Class<?>[] intfs = interfaces.clone();
// 创建安全管理器
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// 此方法调用上述安全管理器对象检查是否存在恶意代码
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
// 生成代理类对象
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 使用指定的调用处理程序获取代理类的构造函数对象
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
// 如果Class作用域为私有,通过 setAccessible 支持访问
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 获取ProxyClass构造函数创建Proxy代理实例
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
更详细的源码解读可以参考此文章:动态代理源码解读