前言
在Java中,我们如果需要提供一个模块给其他人或模块调用,往往是将所有的功能抽取为一个接口,然后其他人通过接口去访问服务,形式为接口->实现类,但是如果我们想在每次使用接口提供的服务之前,进行一些判断,就需要在中间实现一个代理,在代理中进行判断,形式为 接口->代理->实现类,在Java中代理又分为静态代理以及动态代理。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。动态代理类不存在class文件,程序在执行时动态生成字节码并加载。
静态代理
首先定义一个接口
/**
* 需要使用的接口
*/
public interface ProxyInterface {
void doSomething();
}
然后是具体的实现类
/**
* 功能的真正实现位置
*/
public class ProxyInterfaceImpl implements ProxyInterface {
@Override
public void doSomething() {
System.out.println("doSomething success");
}
}
然后是代理类
/**
* 静态代理类
*/
public class StaticProxyClass implements ProxyInterface {
private ProxyInterface mProxyInterface;
public StaticProxyClass(ProxyInterface proxyInterface) {
mProxyInterface = proxyInterface;
}
@Override
public void doSomething() {
System.out.println("before doSomething");
mProxyInterface.doSomething();
System.out.println("after doSomething");
}
}
使用静态代理
StaticProxyClass staticProxyClass = new StaticProxyClass(new ProxyInterfaceImpl());
staticProxyClass.doSomething();
我们可以看到,代理类同样实现了代理接口,但是其实将所有的逻辑请求全部委托给了真正的实现类ProxyInterfaceImpl,对于调用者来说,还是只需要使用接口,在调用之前的判断以及处理结果我们就可以在代理类中就可以进行处理。
动态代理
通过上面的静态代理模式,我们可以看到,代理类需要实现被代理的接口,然后将所有的请求委托给真实的实现类,如果接口里面的方法数很多,那么我们手动编写代理类无疑是很繁琐的一件事,所以这个时候我们可以引入动态代理模式。
首先我们还是得有一个代理接口以及实现类,这里和上面贴出的代码一致,然后编写动态代理类
/**
* 动态代理
*/
public class DynamicProxyClass implements InvocationHandler {
private Object mTarget;
public Object bindProxy(Object target) {
mTarget = target;
return Proxy.newProxyInstance(mTarget.getClass().getClassLoader()
, mTarget.getClass().getInterfaces()
, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before doSomething");
Object invokeResult = method.invoke(mTarget, args);
System.out.println("after doSomething");
return invokeResult;
}
}
使用动态代理
DynamicProxyClass dynamicProxyClass = new DynamicProxyClass();
ProxyInterface proxyInterface = (ProxyInterface) dynamicProxyClass.bindProxy(new ProxyInterfaceImpl());
proxyInterface.doSomething();
我们可以看到,动态代理实现了InvocationHandler接口,然后我们通过Proxy.newProxyInstance方法可以获取到代理类的实例,动态生成的代理类如下。(说明:动态生成的代理类没有.class文件,由程序直接生成字节码然后加载,代码只存在内存中)
当然,我们可以通过ProxyGenerator来将生成的class文件保存到本地
String className = "TestClass";
byte[] bytes = ProxyGenerator.generateProxyClass("com.test." + className, new Class[]{ProxyInterface.class});
FileOutputStream out = new FileOutputStream(new File("./" + className + ".class"));
out.write(bytes);
out.close();
对应的动态代理class文件如下
public final class TestClass extends Proxy implements ProxyInterface {
private static Method m1;
private static Method m2;
private static Method m0;
public TestClass(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 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");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
通过上面的代码我们可以知道,动态代理类$Proxy1同样实现了ProxyInterface接口,然后将接口的的方法通过反射去调用实现。具体的实现逻辑在InvocationHandler的invoke函数中。
动态代理的好处
Proxy类的代码量被固定下来,不会因为业务的逐渐庞大而庞大;
可以实现AOP编程,实际上静态代理也可以实现,总的来说,AOP可以算作是代理模式的一个典型应用;
解耦,通过参数就可以判断真实类,不需要事先实例化,更加灵活多变。