JDK 动静代理
为了引出动静代理,咱们看看一个案列!
广东广州,早上 9:00,一位靓仔衣着人字拖、提着鸟笼,走进了早茶店。没错,这就是广州典型的包租公!名下几栋楼,只收租为生,没工作,这人身真是无趣至极!
这里就得出一个问题:收租不算工作?好吧,其实正真的包租公 不会本人去收租,都是委托给中介去做。为什么呢?这其中能够说牵扯到平安、隐衷等等。想一下,如果包租公本人收租,当下租客很多,其余包租公就不爽了,罗唆找人去捣鬼,比方只问不租,节约包租公工夫。当然不仅仅是这样…
简略应用
好的,租房中介就进去了,租客看房、谈价、签合同、交付钥匙等等都让中介(代理)去做,房东就坐等收钱就行了。
代码中应用输入语句代替真正的业务!
首先,咱们定义一个 房东接口 ,和一个 房东实现类。(为什么须要接口?前面代理类是依据接口失去的,而且房东类不应该裸露进来的)
public interface ILandlordService {void deliver();
}
public class LandlordServiceImpl implements ILandlordService {public void deliver() {
try {System.out.println("告知房东出租胜利,房东收钱");
} catch (Exception e) {e.printStackTrace();
System.out.println("出现异常,终止");
}
}
}
接下来创立一个类,实现 java.lang.reflect.InvocationHandler 接口,该类用来对原来的办法加强。(留神包,被导错)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TransactionInvocationHandler implements InvocationHandler {
// 须要被代理的对象(房东)private Object target;
public Object getTarget() {return target;}
public void setTarget(Object target) {this.target = target;}
/*
实现接口须要重写的办法
proxy: 代理对象
method: 房东接口中被调用的办法
args: 房东接口中被调用的办法须要的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object retVal = null;
try {System.out.println("交付前与中介的交谈");
// 房东收钱
retVal = method.invoke(target, args);
System.out.println("租房胜利");
System.out.println("给钥匙");
} catch (Exception e) {e.printStackTrace();
System.out.println("出现异常,终止交付");
}
return retVal;
}
}
最初写测试类(租客)
import org.junit.Test;
import java.lang.reflect.Proxy;
public class TenantTest {
@Test
public void test() {
// 这两个创建对象和设置值应在 spring 中配置,让 spring 创立、设值
// 创立一个房东对象
ILandlordService landlordService = new LandlordServiceImpl();
// 创立加强类对象
TransactionInvocationHandler transactionInvocationHandler = new TransactionInvocationHandler();
// 把房东对象设置到加强类对象中应用
transactionInvocationHandler.setTarget(landlordService);
/*
应用强转失去房东类代理对象(中介)newProxyInstance 办法须要三个参数
ClassLoader loader:类加载器,间接应用本类的类加载器(零碎类加载器)Class<?>[] interfaces:接口数组
InvocationHandler h:加强类
*/
ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance(this.getClass().getClassLoader(),
// 这里 landlordService 对象临时在测试类中创立,// 应用 spring 后,注入到 TransactionInvocationHandler(加强类)的字段(target)中,// 间接到加强类中获取,这样测试类(租客)就不晓得实现类了(房东)landlordService.getClass().getInterfaces(),
transactionInvocationHandler
);
// 调用代理对象的 deliver(),会执行加强类的 invoke 办法,参数 method 为代理对象调用的办法
proxy.deliver();}
}
执行后果:交付前与中介的交谈
告知房东出租胜利,房东收钱
租房胜利
给钥匙
此时很多人就纳闷了,怎么创立代理类的?又怎么会调用加强类的 invoke 办法的?那就接下去看看原理吧!倡议截图看,代码有点多!
原理
测试类中的 newProxyInstance 办法,点进去
ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance(this.getClass().getClassLoader(),
landlordService.getClass().getInterfaces(),
transactionInvocationHandler
);
到 proxy 类的 newProxyInstance 办法,省去不必要的代码
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
/*
* Look up or generate the designated proxy class and its constructor.
* 查找或生成 指定的代理类和它的构造函数
*/
/*
获取生成类的结构器
loader:类加载器
interfaces:接口数组
*/
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
return newProxyInstance(caller, cons, h);
}
往 newProxyInstance 办法中走,省去不必要的代码,try-catch 也省去了
private static Object newProxyInstance(Class<?> caller,
Constructor<?> cons,
InvocationHandler h) {
// caller 无关平安的,这里疏忽
checkNewProxyPermission(caller, cons.getDeclaringClass());
/*
反射调用结构器创立这个类的对象
cons:代理类结构器
h:加强类
*/
return cons.newInstance(new Object[]{h});
}
应用以下代码获取代理类字节码文件
ProxyGenerator 在 JDK 11 曾经不是私有的了,我这里的源码是 JKD 11
上面获取代理类字节码用的是 JDK 8
import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
public class DynamicProxyClassGenerator {public static void main(String[] args) throws Exception {
// 实现类的 class 对象
generateClassFile(LandlordServiceImpl.class, "LandlordServiceProxy");
}
public static void generateClassFile(Class<?> targetClass, String proxyName) throws Exception {
// 依据类信息和提供的代理类名称,生成字节码
byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, targetClass.getInterfaces());
String path = targetClass.getResource(".").getPath();
System.out.println(path);
FileOutputStream out = null;
// 保留到硬盘中
out = new FileOutputStream(path + proxyName + ".class");
out.write(classFile);
out.close();}
}
代理类生成的 class 文件是这样的,deliver()办法是依据咱们定义的类得来的,省去几个 Object 类失去的办法内容
调用有参结构器创建对象
import com.hao.service.ILandlordService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class LandlordServiceProxy extends Proxy implements ILandlordService {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
// 创建对象时调用的办法
public LandlordServiceProxy(InvocationHandler var1) throws {
// 调用父类(proxy 类)结构器
super(var1);
}
public final boolean equals(Object var1) throws { }
public final String toString() throws {}
public final void deliver() throws {
try {super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {}
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("com.hao.service.ILandlordService").getMethod("deliver");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());
}
}
}
protected Proxy(InvocationHandler h) {Objects.requireNonNull(h);
// 把加强类设置到 proxy 类的字段上
this.h = h;
}
protected InvocationHandler h;
最终 Proxy.newProxyInstance()返回的是代理类(中介)
ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance(
);
proxy.deliver();
而后咱们调用 deliver()办法,进入的是代理类的 deliver()办法
public final void deliver() throws {
try {// 调用父类的 h 字段(加强类)中的 invoke 办法,m3 在本类 static{}中获取
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);
}
}
m3 在 static{}中
static {
try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
// 通过反射失去 deliver 办法
m3 = Class.forName("com.hao.service.ILandlordService").getMethod("deliver");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());
}
}
最初
能够看出,调用代理类最终会调用加强类的 invoke 办法。感激你看到这里,文章有什么有余还请斧正,感觉文章对你有帮忙的话记得给我点个赞,每天都会分享 java 相干技术文章或行业资讯,欢送大家关注和转发文章!