代理模式
简述
代理模式,就是为一个真实的对象提供代理,从而能够控制这个对象的访问。代理对象和被代理的对象实现相同接口,在代理对象中,增加特定的控制逻辑。代理对象分为两种,静态代理和动态代理。
首先,我们有如下对象。
UserDao.java
package reflectionSample;
public interface UserDao {
public void queryName();
}
UserDaoImpl.java
package reflectionSample;
public class UserDaoImpl implements UserDao{
@Override
public void queryName() {
System.out.println("getUserName");
}
}
如果我们需要在queryName之前做一些其它操作,比如记录由哪个用户发起的这个query操作,又或者是限制在某个特定条件下才可以执行query操作,显然直接修改UserDaoImpl
会给后期维护带来困难,这个时候代理模式就能够起作用了。
静态代理模式
在静态代理模式之下,我们显示地创建代理类,代理类持有被代理类的对象,实现与被代理类的相同接口,并且增添相应的业务逻辑,以达到代理的效果。
StaticProxy.java
package reflectionSample;
public class StaticProxy implements UserDao{
private UserDaoImpl userDaoImpl = new UserDaoImpl();
@Override
public void queryName() {
System.out.println("proxy control");
userDaoImpl.queryName();
}
}
ProxyMain.java
package reflectionSample;
import java.lang.reflect.Proxy;
public class ProxyMain {
public static void main(String args[]) {
UserDao staticProxy = new StaticProxy();
staticProxy.queryName();
//out put: proxy control
// getUserName
}
}
如上所示,我们完成了对queryName()
这个接口的代理。
在静态代理的情况下,每代理一个对象,我们必须在编译时就生成好对应的类,而且需要对接口中所有方法进行代理,会造成不必要的代码冗余,但是这样做的好处就是,代理的调用时直接的方法调用,节省了时间。
动态代理模式
在动态代理的情况下,我们不再为每个类生成一个类来完成代理,而是通过Proxy.newProxyInstance(loader, interfaces, h)
来生成代理对象。其三个参数的作用如下表所示
参数名 | 作用 |
---|---|
loader | 代理的class所属的classLoader(关于classLoader,可以参考这篇博客),代理和被代理的class必须在同一个classLoader之下才能够工作 |
interfaces | 被代理的接口。这个时候我们可以指定哪些接口是需要被代理的,传入的是接口的类的数组 |
h | handler,最终的回调方法,必须是一个实现了InvocationHandler 的类的实例 |
而InvocationHandler
接口中的方法则为public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
,其参数作用如下表所示
参数名 | 作用 |
---|---|
proxy | 生成的代理对象 |
method | 被调用的方法对象 |
args | 调用方法时传入的参数 |
在invoker
方法中,method
就是我们的代理方法,我们可以通过method.invoke(object, args)
来直接调用具体对象的方法,由于这个函数的第一个参数是proxy
对象,因此我们还需要让handler
持有一个真正调用方法的对象,从而能够达到真正的代理效果。
DynamicProxyHandler.java
package reflectionSample;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxyHandler implements InvocationHandler{
private Object realObj;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy control");
return method.invoke(realObj, args); //通过检查后,调用真正实体的对应方法
}
public Object bind(Object realObj) {
this.realObj = realObj;
return Proxy.newProxyInstance(realObj.getClass().getClassLoader(), realObj.getClass().getInterfaces(), this);
}
}
ProxyMain.java
package reflectionSample;
import java.lang.reflect.Proxy;
public class ProxyMain {
public static void main(String args[]) {
UserDao staticProxy = new StaticProxy();
staticProxy.queryName();
//输出: proxy control
// getUserName
DynamicProxyHandler handler = new DynamicProxyHandler();
UserDao dymicDao = (UserDao)handler.bind(new UserDaoImpl());
dymicDao.queryName();
//输出 : proxy control
// getUserName
}
}
以上,我们就完成了一次Java原生的动态代理。在动态代理的方法里面,我们可以任意添加业务逻辑之外的控制逻辑,既不会让代码变的难以维护,又达到了效果,一举两得。
Java原生动态代理剖析
Proxy.newInstance源代码分析
既然原生代理有着这样强大的功能,那么它的具体实现又是什么呢?
最好的方法当然是直接查看源代码了。首先我们研究一下Proxy.newProxyInstance
这个静态方法做了哪些事情
Proxy.java
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); //检查是否拥有对这个类的访问权限,java安全相关,会在后面文章补充
}
//检查内存中是否存在已经生成好的类,如果没有,就生成一个并返回
Class<?> cl = getProxyClass0(loader, intfs);
//调用生成的代理类的构造方法创建新的类,并返回
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
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);
}
}
从上面的代码,我们能够发现,其实动态代理也会新建一个类,同时也会实例化这个新类的对象,然后返回。那么具体,这个新的类,又是怎么被构建出来的呢?
在getProxyClass0
这个方法中,首先会从内部的proxyClassCache
中去取缓存的类,如果没有的话,就会通过ProxyClassFactory
生产一个。
proxyClassCache
的声明为private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
接下来我们继续研究ProxyFactory
中的方法,我们只需要重点关注ProxyGenerator.generateProxyClass(
因为这个静态方法直接以字节码的形式生成新的类。
proxyName, interfaces, accessFlags);
public static byte[] generateProxyClass(final String name,
Class<?>[] interfaces,
int accessFlags)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
final byte[] classFile = gen.generateClassFile();
.....
...
return classFile;
}
ProxyGenerator.java
private byte[] generateClassFile() {
//第一步,生成最终类所需要的所有方法
//首先,为这个新的类生成hashCode, equals, toString这三个java.lang.Object中带有的默认方法
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
//遍历提供的接口,并且增加所有接口中的方法
for (Class<?> intf : interfaces) {
for (Method m : intf.getMethods()) {
addProxyMethod(m, intf);
}
}
//检查已经实现的方法,是否有可能出现冲突
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
checkReturnTypes(sigmethods);
}
//第二步,生成构造函数,并且为刚刚生成的每一个方法生成一个私有静态变量。
try {
methods.add(generateConstructor());
for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
for (ProxyMethod pm : sigmethods) {
// add static field for method's Method object
fields.add(new FieldInfo(pm.methodFieldName,
"Ljava/lang/reflect/Method;",
ACC_PRIVATE | ACC_STATIC));
// generate code for proxy method and add it
methods.add(pm.generateMethod());
}
}
methods.add(generateStaticInitializer());
} catch (IOException e) {
throw new InternalError("unexpected I/O Exception", e);
}
//判断方法/字段的数量是否超过java限制
if (methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
}
if (fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
}
//第三步,将生成好的方法,字段,以二进制的形式输出(之前生成的方法,字段,都是在内存中的变量,并没有输出到文件)
cp.getClass(dotToSlash(className));
cp.getClass(superclassName);
for (Class<?> intf: interfaces) {
cp.getClass(dotToSlash(intf.getName()));
}
/*
* Disallow new constant pool additions beyond this point, since
* we are about to write the final constant pool table.
*/
cp.setReadOnly();
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
...
return bout.toByteArray();
}
由上面我们可以知道,其实动态代理,就是根据传入的参数(class名称,需要代理的接口数组,访问控制符)来动态生成一个新的类,这个类会被JVM动态加载到内存中,然后实例化,最终完成代理的功能。
在ProxyGenerator.java
中省略部分主要包含的是对.class二进制文件的输出,如果想要进一步了解.class文件的结构以及如何输出一个.class文件,可以参阅这个系列的博客
生成的新类代码分析
既然动态代理的原理是生成一个新的java class,那么它的结构是什么呢?
通过在Main函数中增加如下声明,可以让生成的class文件保存到本地
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
得到本地文件之后,我们可以通过一些class 二进制文件反编译器,如Java Decompiler,来反编译从而得到可读性更好的java文件。
下面附上生成的proxy类
$Proxy0.java
package com.sun.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import reflectionSample.UserDao;
public final class $Proxy0
extends Proxy
implements UserDao //实现了指定的UserDao接口
{
//每个方法,都维护了一个私有静态变量
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
//保存了对最后方法调用类的引用
public $Proxy0(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
{
try
{
//调用方法时,都是通过方法调用类的invoke,而不是调用实际的方法。
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void queryName()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
//在类初始化的时候,就对私有静态变量进行赋值,以获得自己需要实现的接口的方法
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("reflectionSample.UserDao").getMethod("queryName", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
以上,就是Java原生代理的所有学习笔记了。从上面的分析我们不难得出几个结论。
- 代理也是基于类的,静态代理通过编译/手动在编译时写入代理类,以及相应的代理方法,从而完成代理。动态代理在运行时通过指定需要实现的接口,动态拼接生成新的类,并且指定所有方法的回调处理类,从而达到实现一个接口,代理所有方法的效果。
- 动态代理需要通过
method
进行反射调用。
那么,Java原生动态代理就到这里结束了,后面的文章会介绍另外一种动态代理,基于ASM的cglib
。