一个典型的代理模式通常有三个角色,这里称之为代理三要素:共同接口、真实对象、代理对象。
ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。
RealSubject:真实主题角色,是实现抽象主题接口的类。
Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
实现动态代理的关键技术是反射。
静态代理
代理模式有几种,虚拟代理,计数代理,远程代理,动态代理。主要分为两类,静态代理和动态代理。静态代理比较简单,是由程序员编写的代理类,并在程序运行前就编译好的,而不是由程序动态产生代理类,这就是所谓的静态。
考虑这样的场景,管理员在网站上执行操作,在生成操作结果的同时需要记录操作日志,这是很常见的。此时就可以使用代理模式,代理模式可以通过聚合和继承两种方式实现:
/**方式一:聚合式静态代理
* @author Goser (mailto:goskalrie@163.com)
* @Since 2016年9月7日
*/
//1.抽象主题接口
public interface Manager {
void doSomething();
}
//2.真实主题类
public class Admin implements Manager {
public void doSomething() {
System.out.println("Admin do something.");
}
}
//3.以聚合方式实现的代理主题
public class AdminPoly implements Manager{
private Admin admin;
public AdminPoly(Admin admin) {
super();
this.admin = admin;
}
public void doSomething() {
System.out.println("Log:admin操作开始");
admin.doSomething();
System.out.println("Log:admin操作结束");
}
}
//4.测试代码
Admin admin = new Admin();
Manager m = new AdminPoly(admin);
m.doSomething();
//方式二:继承式静态代理
//与上面的方式仅代理类和测试代码不同
//1.代理类
public class AdminProxy extends Admin {
@Override
public void doSomething() {
System.out.println("Log:admin操作开始");
super.doSomething();
System.out.println("Log:admin操作开始");
}
}
//2.测试代码
AdminProxy proxy = new AdminProxy();
proxy.doSomething();
聚合实现方式中代理类聚合了被代理类,且代理类及被代理类都实现了同一个接口,可实现灵活多变。继承式的实现方式则不够灵活。
动态代理
一般来说,对代理模式而言,一个主题类与一个代理类一一对应,这也是静态代理模式的特点。
但是,也存在这样的情况,有n各主题类,但是代理类中的“前处理、后处理”都是一样的,仅调用主题不同。也就是说,多个主题类对应一个代理类,共享“前处理,后处理”功能,动态调用所需主题,大大减小了程序规模,这就是动态代理模式的特点。
//1. 抽象主题
public interface Moveable {
void move() throws Exception;
}
//2. 真实主题
public class Car implements Moveable {
public void move() throws Exception {
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽车行驶中…");
}
}
//3.事务处理器
public class TimeHandler implements InvocationHandler {
private Object target;
public TimeHandler(Object target) {
super();
this.target = target;
}
/**
* 参数:
*proxy 被代理的对象
*method 被代理对象的方法
*args 方法的参数
* 返回:
*Object 方法返回值
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long startTime = System.currentTimeMillis();
System.out.println("汽车开始行驶…");
method.invoke(target, args);
long stopTime = System.currentTimeMillis();
System.out.println("汽车结束行驶…汽车行驶时间:" + (stopTime - startTime) + "毫秒!");
return null;
}
}
//测试类
public class Test {
public static void main(String[] args) throws Exception{
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();
}
/* 方法二 public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetExc eption, InstantiationException {
//生成$Proxy0的class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//获取动态代理类
Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(),IHello.class);
//获得代理类的构造函数,并传入参数类型InvocationHandler.class
Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
//通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入
IHello iHello = (IHello) constructor.newInstance(new HWInvocationHandler(new Hello()));
//通过代理对象调用目标方法
iHello.sayHello();
}
*/
}
代码讲解:
在测试代码中,Proxy.newProxyInstance()方法需要3个参数:类加载器(要进行代理的类)、被代理类实现的接口,事务处理器。所以先实例化Car,实例化InvocationHandler的子类TimeHandler,将各参数传入Proxy的静态方法newProxyInstance()即可获得Car的代理类,前面的静态代理,代理类是我们编写好的,而动态代理则不需要我们去编写代理类,是在程序中动态生成的。
JDK动态代理步骤
1. 创建一个实现InvocationHandler接口的类,它必须实现invoke()方法
在动态代理中InvocationHandler是核心,每个代理实例都具有一个关联的调用处理程序(InvocationHandler)。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序(InvocationHandler)的 invoke
方法。所以对代理方法的调用都是通InvocationHadler的invoke来实现中,而invoke方法根据传入的代理对象,方法和参数来决定调用代理的哪个方法
invoke方法签名:invoke(Object Proxy,Method method,Object[] args)
2. 创建被代理的类及接口
3. 调用Proxy的静态方法,创建一个代理类
4. 通过代理调用方法
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException{
if (h == null) {//如果h为空直接抛出异常,所以InvocationHandler实例对象是必须的
throw new NullPointerException();
}
final Class<?>[] intfs = interfaces.clone();
//一些安全的权限检查
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*查找或生成指定的代理类*/
Class<?> cl = getProxyClass0(loader, intfs);
/*用指定的调用处理程序调用它的构造函数.*/
try {
//获得类的构造函数
//获取代理类的构造函数对象
//参数constructorParames为常量值:private static final Class<?>[] constructorParams = { InvocationHandler.class };
final Constructor<?> cons =cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
//当需要代理的类实现了一个非public的接口时,因为这样的接口需要特殊的权限,因此调用doPrivilege(native 修饰的方法)创建代理实例。
return AccessController.doPrivileged(newPrivilegedAction<Object>() {
public Object run() {
//根据代理类的构造函数对象来创建代理类对象
return newInstance(cons,ih);
}
});
} else {
//根据代理类的构造函数对象来创建代理类对象
return newInstance(cons,ih);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}
可以看到,获得代理类的代码是
Class<?>cl = getProxyClass0(loader,intfs);
并由此获得代理类的构造函数,生成代理类的实例返回给该方法的调用者。
继续跟进getProxyClass0()方法:
/** 生成代理类。调用该方法前必须使用checkproxyaccess方法执行权限检查。*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
//检查实现的接口数,65535这个数字好特殊,端口数好像也是这个,这个数字是由虚拟机所决定的,2^16-1个
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 如果代理类已经通过实现给定接口的类加载器创建了,则返回缓存中的该类的副本;否则将通过ProxyClassFactory创建代理类
return proxyClassCache.get(loader, interfaces);
}
还是没有看到代理类是怎么生成的,只知道代理类是从proxyClassCache中取得的,这个变量是与缓存相关的一个对象,查看该变量的声明与初始化:
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
可以发现proxyClassCache是个用来缓存代理类的类变量,大家知道类变量的特点是与类一一对应,在一个虚拟机中类只有一个,对应着在一个虚拟机中类变量也只有一个,且在此处,在Proxy类被加载的时候就赋值了。在赋值操作的参数中有ProxyClassFactory()这么一个构造函数,这个是动态代理中的关键:生成代理类的类文件字节码。继续跟进去,找到代理类的生成之处了:
/** 根据给定的类加载器和接口数组生成代理类的工厂类*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader,Class<?>[], Class<?>>
{
// 所有代理类名称的前缀
private static final String proxyClassNamePrefix = "$Proxy";
//使用唯一的编号给作为代理类名的一部分,如$Proxy0,$Proxy1等
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader,Class<?>[] interfaces) {
Map<Class<?>, Boolean>interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/* 验证类加载器将此接口的名称解析为实际对象的名称。*/
//验证指定的类加载器(loader)加载接口所得到的Class对象(interfaceClass)是否与intf对象相同
Class<?> interfaceClass =null;
try {
interfaceClass = Class.forName(intf.getName(),false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from classloader");
}
/* 验证类对象确实是一个接口。*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*确保接口唯一*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // 代理类的包名
/*记录非公开代理接口的包,以便将代理类定义在同一个包中。确认所有非公共代理接口都在同一个包中。*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
String name =intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces fromdifferent packages");
}
}
}
if (proxyPkg == null) {
// 如果没有非公开的代理接口,使用com.sun.proxy作为包名
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/* 生成代理类名的序号*/
long num = nextUniqueNumber.getAndIncrement();
//生成全类名
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*生成代理类字节码 */
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,interfaces);
try {
return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
在ProxyClassFactory中,可以看到产生代理类的具体逻辑,大致上是,根据传递的被代理类及其实现的接口生成代理类的字节码加载到缓存中,但是加载到缓存中只是一个.java文件也不能用,所以底层还有编译等操作。到这里,可以大致的看清JDK中动态代理的面孔了,实现的步骤为:
1. 创建代理类的源码;
2. 对源码进行编译成字节码;
3. 将字节码加载到内存;
4. 实例化代理类对象并返回给调用者;
$Proxy0.class
来看看例1(MyProxy)的代理类是怎样的?
public final class $Proxy0 extends Proxy implements IHello { //继承了Proxy类和实现IHello接口
//变量,都是private static Method XXX
private static Method m3;
private static Method m1;
private static Method m0;
private static Method m2;
//代理类的构造函数,其参数正是是InvocationHandler实例,Proxy.newInstance方法就是通过通过这个构造函数来创建代理实例的
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
//接口代理方法
public final void sayHello() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
//以下Object中的三个方法
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
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);
}
}
//对变量进行一些初始化工作
static {
try {
m3 = Class.forName("com.mobin.proxy.IHello").getMethod("sayHello", new Class[0]);
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}