1. 代理模式
代理模式通过为某个对象提供一个代理来控制对于该对象的访问。代理类主要负责为委托类(真实对象)预处理消息、过滤消息、传递消息给委托类。代理类本身不负责具体的实现,而是利用委托类来完成具体的业务实现,并将执行结果进行封装处理。
代理类为委托类进行消息预处理、消息的过滤等操作后,将消息传递给被代理类(委托类),之后还可以进行消息的后处理操作。代理类和委托类通常会存在关联关系,代理类本身不实现服务,而是通过调用委托类中的方法来提供服务。
代理模式可以分为如下几类:
- 远程代理
- 保护代理
- 缓存代理
- 虚拟和智能代理
代理模式中包含有几项要素:
- Subject(共同接口):客户端使用的现有接口
- RealSubject(真实对象):真实对象的类
- ProxySubject(代理对象):代理类
下面我们通过例子来理解一下代理模式。公司都有一个公关部门来对接媒体,当外面的媒体想要采访公司的董事的时候,并不是直接进行采访,而是通过公关部门来进行对接。如果公司有成就,公关部门负责大吹特吹;如果媒体有问题想要找公司询问,公关部门负责说一些推诿搪塞的话,例如:这件事我们已经上报给相关的部门了,请回去等消息(这件事我们已经忘了,你继续傻等吧),或者说这件事不归我们管,你要去找xxx(别来找我们,找我们也不会解决)……
上面的例子中的公司就是委托对象,公关部门就是代理对象,媒体就是客户端。
2. 静态代理
所谓静态,指的是接口、代理类和委托类在程序编译期就已经被确定下来。静态代理中的委托类和代理类都需要实现定义的接口,而代理类中持有一个委托类对象的引用,而后在代理类的方法中调用该对象的方法。
假设现在有一个Person接口,接口中有一个方法say()
public interface Person {
void say(String name);
}
下面定义Star类实现Person接口,并重写say()
:
public class Star implements Person{
public Star() {
}
@Override
public void say(String name) {
System.out.println("Hello " + name);
}
}
明星通常需要经纪人来对接外部的事务,因此,下面创建Agent类,它同样需要实现Person接口并重写say()
。Agent类中有一个Star的对象,表示他所负责的明星,而且类中的say()
只负责传递消息,真正使用到消息的是Star中的say()
public class Agent implements Person{
Star star;
public Agent(Star star) {
this.star = star;
}
@Override
public void say(String name) {
star.say(name);
}
}
最后通过测试类来看一下如何使用:
public class Demo {
public static void main(String[] args) {
String name = "Forlogen";
Star star = new Star();
Agent agent = new Agent(star);
agent.say(name);
}
}
Hello Forlogen
首先创建Star类对象star,然后创建Agent对象并将star传入。最后使用agent的say()
来传递输入的字符串,Agent类中的star的say()
来使用消息,输出结果。
总结:
- 优点:在不改变委托类对象的前提下,可以选择性的对委托类对象的功能进行扩展,例子中没有添加扩展的相关逻辑
- 缺点:代理类由于和委托类都实现了接口,因此,代理类实现了委托类要实现的所有方法。一旦接口中增加方法,两者都需要进行改变,这样不仅违背了开闭原则,而且增加了维护成本
3. 动态代理
动态相对于静态来说,它指的是代理类并不是在编译期就被确定,而是在运行期才被确定下来。在运行期中,根据程序中的指示来动态的生成代理类。动态代理相较于静态代理的优势在于,它可以很方便的对代理类的函数进行统一管理,而不用修改每个代理类中的方法。
首先来看一下如何使用动态代理,然后再分析它的原理。我们依然使用静态代理中使用的例子,Person接口和Star实现类并不改变,Agent中体现动态代理:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Agent implements InvocationHandler {
Object obj;
public Agent(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before invoke..." + method.getName());
method.invoke(obj, args);
System.out.println("after invoke..." + method.getName());
return null;
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
}
}
在使用时,首先创建Star和Agent类对象,然后通过Agent的类对象调用getProxyInstance()
来获取代理类对象,最后使用对象来执行say()
。
public class Demo {
public static void main(String[] args) {
System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Star star = new Star();
Agent agent = new Agent(star);
Person proxyInstance = (Person)agent.getProxyInstance();
proxyInstance.say("Forlogen");
}
}
before invoke...say
Hello Forlogen
after invoke...say
现在我们再来具体看一下Agent类的实现。现在Agent并没有选择去实现Person接口,而是实现InvocationHandler接口并重写其中的invoke()
。通过代理对象来调用委托类中的方法时,最后都是委托给invoke()
来执行,invoke()
中可以对委托类进行一系列的前置增强和后置增强操作。最后使用的是Proxy类中的静态方法static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler invocationHandler );
来获取代理对象,通过代理对象来执行Person接口中定义的方法,也就是Star类中想要执行的方法。
package java.lang.reflect;
public class Proxy implements java.io.Serializable {
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
// ...
}
}
参数分析:
- ClassLoader loader:指定一个动态加载代理类的类加载器
- Class<?>[] interfaces::指明委托类实现的接口,之后通过拼接字节码生成的类才能知道调用哪些方法。
- InvocationHandler h:这是一个方法委托类,通过代理调用被代理类的方法时,就可以将方法名和方法参数都委托给这个委托类
动态代理使用的过程中,我们只能看到代理类,委托类和代理类之间通过InvocationHandler来实现代理的过程。
总结,动态代理的具体步骤如下:
- 通过实现InvocationHandler接口创建自己的调用处理器(Handler)
- 通过为Proxy类指定类加载器对象和一组Interface来创建代理类
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器的接口类型
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数传入
4. 源码分析
体现动态代理的类要实现InvocationHandler接口并重写invoke()
,而代理类对象的获取首先要通过Proxy.newProxyInstance()
实现,下面看一下它的源码实现:
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
// 检查传入的Handler是否为空
Objects.requireNonNull(h);
// 拷贝代理类代理的接口
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
// 生成代理类对象
Class<?> cl = getProxyClass0(loader, intfs);
//使用指定的Handler来获取代理类的构造函数
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 通过反射机制获得指定代理类的构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
// 检查访问权限是否为public
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
// 设置访问权限
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// 调用newInstance()创建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);
}
}
从newProxyInstance()
的源码实现可以看出,它所执行的主要操作有:
- 调用
getProxyClass0()
来获取代理类对象 - 调用
getConstructor()
通过反射获取代理类对象的构造函数 - 设置访问权限为public
- 通过反射中的
newInstance()
来创建Proxy代理实例
上面使用到的三个主要的方法,getConstructor
和newInstance()
是java.lang.reflect包下的函数,熟悉反射机制的应该清楚。我们重点看下getProxyClass0()
的源码实现:
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
// 接口数量不应大于65535,否则抛异常
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 如果指定接口的代理类在缓存中已存在,则直接读取
// 否则通过ProxyClassFactory工厂来获取代理对象
return proxyClassCache.get(loader, interfaces);
}
proxyClassCache的实现仍然是通过代理模式:
/**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
代理对象需要使用工厂ProxyClassFactory获取,它的的源码实现如下:
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// 代理类的名字的前缀统一为“$Proxy”
private static final String proxyClassNamePrefix = "$Proxy";
// 每个代理类前缀后面都会跟着一个唯一的编号,如$Proxy0、$Proxy1、$Proxy2
// 例如例子中只代理了一个接口,应该字节码文件反编译后只有$Proxy0
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) {
// 验证类加载器加载接口得到对象是否与由apply函数参数传入的对象相同
Class<?> interfaceClass = null;
try {
// 获取接口的Class类对象
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
//验证interfaceClass是不是接口,因为动态代理只能代理接口
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
// 将interfaceClass存到Map集合中
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
// 记录不是被public修饰的代理接口的包名,用来帮助在相同的包下定义代理类
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
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 from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
// 选择一个要生成的代理类的名字
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 生成代理类
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
上面主要执行的是通过工厂来获取指定类加载器和接口的代理类,其中主要使用的是generateProxyClass()
来实现代理类的获取,它的源码实现为:
/*
var0:代理类的名字
var1:要代理的具体接口
*/
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
// 生成代理类的字节码文件
final byte[] var4 = var3.generateClassFile();
// 保存代理类的字节码文件
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
generateProxyClass()
方法的返回类型是一个字节数组,表示代理类的字节码文件信息。而字节码文件的生成使用了generateClassFile()
,它的源码实现为:
private byte[] generateClassFile() {
//下面一系列的addProxyMethod方法是将接口中的方法和Object中的方法添加到代理方法中(proxyMethod)
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
//获得接口中所有方法并添加到代理方法中
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}
Iterator var11 = this.proxyMethods.values().iterator();
List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
checkReturnTypes(var12);
}
Iterator var15;
try {
//生成代理类的构造函数
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
this.methods.add(var16.generateMethod());
}
}
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
this.cp.setReadOnly();
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}
字节码生成后,它是作为工厂方法中调用defineClass0()
的参数,最终是使用defineClass0()
来解析字节码,生成了Proxy的Class对象。defineClass0()
是一个本地方法,这里就无法看它的具体实现了。
private static native Class<?> defineClass0(ClassLoader loader, String name,
byte[] b, int off, int len);
至于JVM如何通过类加载子系统和运行时数据区的操作来从字节码文件中创建对象,可以查看JVM原理的相关内容。有关对象创建的内容可阅读对象的实例化、内存布局和访问定位了解。更多JVM的内容可查阅:
最后看一下Person接口的代理类对应的字节码文件反编译后的结果。首选,在动态代理的使用程序中添加
System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
来获取代理类的.class文件,文件在自己工程的com.sun.proxy包下。最后反编译后得到:
package com.sun.proxy;
import Proxy.DynamicProxy.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Person {
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 say(String var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} 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);
} 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("Proxy.DynamicProxy.Person").getMethod("say", Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
$Proxy0继承了Proxy并实现了要代理的接口Person,这也就说明了动态代理只能代理接口而不能代理类,因为Java中的继承只能是单继承。$Proxy0中的方法包含了Object类中的方法和接口中要实现的方法,静态代码块中通过Class.forName()
来获取指定全限定类名对应类的的Class类对象,然后使用getMethod()
来获取指定名字的方法,这些工作都是通过反射机制实现,例如Person接口中的say()
对应得到的是m3。下面把$Proxy0中say()
的实现单独抽出来看一下:
public final void say(String var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
发现方法的实现仍然使用的是反射中的invoke()
,方法的参数为代理类、方法名和传入的String类型的参数。
通过对动态代理实现过程的源码分析,我们现在可以明白为什么通过Proxy.newProxyInstance()
可以按指定的参数来获取接口的代理类,为什么参数中要传入InvocationHandler接口的实现类,为什么说最后方法都委托给实现类的invoke()
,这一切的工作都依赖于反射机制和Java虚拟机。
5. Cglib代理
静态代理和动态代理都要求目标对象实现某个接口,但有时目标对象只是一个单独的对象,并没有实现任何接口,如果使用目标对象自雷来实现代理的方式就成为Cglib代理,也称为为子类代理。它是在内存中构建一个子类对象从而实现目标对象功能扩展。
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口,使用它需要现在相关的Jar包,然后将其引入到项目的路径中。
通常来说,如果目标对象需要实现接口,那么使用上面的动态代理;如果目标对象不需要实现接口,那么使用Cglib代理。
依然使用前面的例子来看一下Cglib代理如何使用,现在Person接口不再使用,Star不需要实现任何接口:
public class Star {
public void say(String name) {
System.out.println("Hello " + name);
}
}
Agent的实现:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Agent implements MethodInterceptor {
private Object obj;
public Agent(Object obj) {
this.obj = obj;
}
public Object getProxyInstance(){
// 创建工具类
Enhancer enhancer = new Enhancer();
// 设置父类,即委托类
enhancer.setSuperclass(obj.getClass());
// 设置回调函数
enhancer.setCallback(this);
//创建子类对象,即代理对象
return enhancer.create();
}
// 重写intercept方法,调用目标对象的方法
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("before cglib proxy...");
method.invoke(obj, args);
System.out.println("after cglib proxy...");
return null;
}
}
这里只做了简单的介绍,详细的原理等深入学习了再做整理。