代理类的两个优点:
- 可以隐藏委托类的实现
- 可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。
1静态代理
UML图如下:
ServiceInterface: 服务接口类
ServiceImpl:服务具体实现类,也就是被代理类,即委托类
Proxy:代理类。
ServiceImpl和Proxy都继承自同一个接口,proxy类通过包含一个ServiceImpl的引用从而对客户提供服务,Proxy类自身也可以扩展doSomething()的实现,从而达到代理的意义
/**
* 代理类
*/
public class Proxy implements ServiceInterface {
private ServiceImpl server;
public BusinessAgent(ServiceImpl server){
this.server=server;
}
public void doSomething {
System,out.println("say hello");
System,out.println("smile");
this.server.doSomething();//被代理对象的方法
System,out.println("say goob bye");
}
}
静态代理的局限在于:必须在运行前就编写好代理类
2.动态代理
2.1 动态代理的使用
假设存在这样一个需求:存在一个ServiceImpl类,它包含了上百个方法,我们需要在每次调用ServiceImpl中的任何一个方法之前打印一条日志。
如果还是用静代理的方式实现的话,那么我们就要重新实现这上百个方法,并且还包含了大量冗余。因此动态代理排上用处了
动态代理其实就是通过Java的反射机制,对委托类的调用进行一个封装扩展。
class ServiceHandler implements InvocationHandler {
//委托类对象;
private ServiceImpl server;
public ServiceHandler(ServiceImpl server) {
this.server = server;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info(method.getName()) //我们在这实现打印日志的功能
//还可添加其他方法,会在被代理方法执行前执行
Object result = method.invoke(server, args); //通过持有的serviceimpl 引用来反射调用被代理方法
//还可添加其他方法,会在被代理方法执行后执行
return result;
}
}
如上所示,ServiceHandler中包含了一个委托类的引用,客户端通过动态代理对象来调用方法时,最终其实是调用到invoke()方法,invoke()方法中通过反射来调用委托类的具体方法,动态代理的所有扩展操作也都是在invoke()方法中,我们需要做的也就是把我们想要做的操作封装到handler的invoke方法中。
如何获取动态代理对象呢?
ServiceInterface serverProxy = (ServiceInterface) Proxy.newProxyInstance(ServiceInterface.class.getClassLoader(), new Class[]{ServiceInterface.class}, new ServiceHandler(new ServiceImpl()));
serverProxy.doSomething();
获得到的这个serverProxy对象中也就持有了上面的serviceHandler对象的引用。
java.lang.reflect.Proxy: 这是生成代理类的主类
//创建代理对象
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
需要三个参数:
- 用来加载代理类的classloader
- 代理类需要实现的interface
- handler对象(初始化时需要传入委托类的实例)
总结一下动态代理:
- 代理对象是在程序运行时产生的,而不是编译期
- 对代理对象的所有接口方法调用都会转发到InvocationHandler.invoke()方法,在invoke()方法里我们可以加入任何逻辑
- 优点:Java动态代理可以避免静态代理带来的代码冗余的问题。
- 缺点:Java动态代理只能针对接口创建代理,不能针对类创建代理 (因为每个动态代理类都要继承Proxy类,且Java是单继承,因此没法对针对类创建代理了)。这是JDK的动态代理实现,如果还需其他功能可以考虑CGLIb
2.2 JDK动态代理的原理
动态代理类是在运行时生成的,并不是编译时生成。
在调用newProxyInstance生成动态代理对象之前,加入下面一段代码可以将生成的动态代理class文件保存下来
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
生成的动态代理class文件如下:
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 me.zm.ServiceInterface;
public final class $Proxy0 extends Proxy implements ServiceInterface {
private static Method m1; //equals()的Method对象
private static Method m3;//ServiceInterface中doSomething对应的Method对象
private static Method m2;//toStrin()的Method对象
private static Method m0;hashCode()的Method对象
public $Proxy0(InvocationHandler var1) throws {
super(var1);//可以看到需要吧Handler的引用给传递进来
}
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 void doSomething() throws {
try {
//通过调用handler的invoke方法来调用ServiceInterface接口中的方法。可以看到吧m3传进去了
super.h.invoke(this, m3, (Object[])null);
} 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);
}
}
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"));
m3 = Class.forName("me.zm.ServiceInterface").getMethod("doSomething");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到生成的动态代理类继承自proxy类并且实现了ServiceInterface接口。而doSomething方法的实现其实是调用了handler的invoke方法。
除此之外,还可以看到动态代理对象的equals、hashCode、toString这三个方法也都是直接调用了handler的实现,这个也是要记住的哦,在某些情景下要重新Handler中的上述方法才对的。
参考:
http://www.importnew.com/27772.html
https://blog.youkuaiyun.com/kisimple/article/details/43709505