注:部分代码来自于享学课堂,有自己的理解修改
目录
我们再看看super.h.invoke(this, m3, (Object[])null);
1、JDK动态代理demo
还是以我们熟悉的奥特曼举例,赛文奥特曼腿被怪兽打折了,只能有雷欧代替赛文出战。
首先是奥特曼接口
public interface AoTeMan {
//打怪兽方法
public void playMaster();
}
接着是赛文奥特曼
public class SaiWen implements AoTeMan {
@Override
public void playMaster() {
System.out.println("我是赛文,我腿受伤了,大不了怪兽");
}
}
雷欧奥特曼,不是来自M78星云,不用实现AoTeMan接口
public class LeiO implements InvocationHandler {
private AoTeMan saiWen;
public LeiO(SaiWen saiWen) {
this.saiWen = saiWen;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("雷欧来到地球了");
method.invoke(saiWen,args);
System.out.println("雷欧开始打怪兽");
return null;
}
}
怪兽来了,奥特曼出动打怪兽
public class Test {
public static void main(String[] args) {
SaiWen saiWen = new SaiWen();
AoTeMan leiO = (AoTeMan) Proxy.newProxyInstance(Test.class.getClassLoader(), new Class<?>[]{AoTeMan.class}, new LeiO(saiWen));
leiO.playMaster();
}
}
执行结果
雷欧来到地球了
我是赛文,我腿受伤了,大不了怪兽
雷欧开始打怪兽
2、执行原理图解
思考,既然是动态生成的对象,我们能不能拿到这个动态生成的对象呢?答案是可以,我们通过二进制了把动态生成的对象保存到本地文件,然后查看源码就能一目了然。
拉取动态生成对象
修改Test方法
public class Test {
public static void main(String[] args) {
SaiWen saiWen = new SaiWen();
AoTeMan leiO = (AoTeMan) Proxy.newProxyInstance(Test.class.getClassLoader(), new Class<?>[]{AoTeMan.class}, new LeiO(saiWen));
leiO.playMaster();
createProxyClassFile();
}
public static void createProxyClassFile() {
byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{AoTeMan.class});
try {
FileOutputStream fileOutputStream = new FileOutputStream("$Proxy0.class");
fileOutputStream.write($Proxy0s);
fileOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
执行之后得到文件
内容如下:
public final class $Proxy0 extends Proxy implements AoTeMan {
private static Method m1;
private static Method m3;
private static Method m2;
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 void playMaster() throws {
try {
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("com.xiangxue.jack.test.AoTeMan").getMethod("playMaster");
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());
}
}
}
最主要的就是这个方法
public final void playMaster() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
我们再看看super.h.invoke(this, m3, (Object[])null);
super.h:protected InvocationHandler h;就是实现了InvocationHandler的LeiO奥特曼类h.invoke(this, m3, (Object[])null):就是LeiO中重写的invoke方法
我们把Proxy0.class类修改,其实就是一个普通的反射调用
public class DongTaiClass implements AoTeMan {
LeiO h;
public DongTaiClass(LeiO h) {
this.h = h;
}
public final void playMaster() {
try {
//其实就是一个普通的反射的调用
Method md = AoTeMan.class.getMethod("playMaster");
this.h.invoke(this, md, null);
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
public static void main(String[] args) {
new DongTaiClass(new LeiO(new SaiWen())).playMaster();
}
}
这样看来事情就比较明朗了
问题:
1、被代理类必须要实现接口AoTeMan,才能够被代理(cglib不需要实现接口)
3、使用cglib实现动态代理
引入maven
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.5</version>
</dependency>
产生代理的工厂
public class CglibInstanceFactory {
public static Object getInstance() {
//拿到字节码增强器
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Original.class);
// 这里的0和1对应callbacks中数组的下标
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
if ("add".equals(method.getName())) {
return 0;
} else{
return 1;
}
}
});
//AddInterceptor对应0,DelInterceptor对应1
Callback[] callbacks = {new AddInterceptor(), new DelInterceptor()};
enhancer.setCallbacks(callbacks);
return enhancer.create();
}
}
原始类:注意没有实现任何接口
public class Original {
public void add(){
System.out.println("add");
}
public void del(){
System.out.println("del");
}
}
新增和删除代理
public class AddInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("新增之前");
methodProxy.invokeSuper(o,objects);
System.out.println("新增之后");
return null;
}
}
public class DelInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("删除之前");
methodProxy.invokeSuper(o,objects);
System.out.println("删除之后");
return null;
}
}
执行类
public class Test {
public static void main(String[] args) {
Original instance = (Original)CglibInstanceFactory.getInstance();
instance.add();
}
}
执行结果
新增之前
add
新增之后
4、自定义动态代理的实现
这个用到了很多开发中基本不会使用到的类,所以是直接把享学jack老师的代码放在这里,给大家做个参考,也方便自己以后查阅
首先原始代码对象
还是奥特曼、雷欧、赛文三个实体,在1的demo中有的,就不再贴代码了
自定义ClassLoader
public class MyClassLoader extends ClassLoader {
private File dir;
MyClassLoader(String path) {
dir = new File(path);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (dir != null) {
File clazzFile = new File(dir, name + ".class");
if (clazzFile.exists()) {
try {
FileInputStream inputStream = new FileInputStream(clazzFile);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, len);
}
//返回反射对象
return defineClass("com.zhuguang.jack.myProxy." + name, byteArrayOutputStream.toByteArray(), 0,
byteArrayOutputStream.size());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return super.findClass(name);
}
}
自定义Handler接口
public interface MyInvocationHandler {
public Object invoke(Object proxy, Method method, Object args)
throws Throwable;
}
自定义代理对象MyProxy
public class MyProxy {
private static String rt = "\r\n";
//项目所在的目录
private static String path = "E:\\idea\\public\\lesson20190305-aop\\src\\main\\java\\com\\zhuguang\\jack\\myProxy";
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
MyInvocationHandler h) {
//1、以字符串的形式拼凑出一个代理类
String javaStr = getJavaStr(interfaces);
//2、以流的方式写到java文件里面
createFile(javaStr);
//3、java文件动态编译成.class文件
compiler();
//4、自定义类加载器把磁盘里面的.class文件加载到内存
//5、实例化这个内存里面的类然后把把实例返回
MyClassLoader myClassLoader = new MyClassLoader(path);
try {
Class<?> $Proxy0 = myClassLoader.findClass("$Proxy0");
Constructor<?> constructor = $Proxy0.getConstructor(MyInvocationHandler.class);
Object o = constructor.newInstance(h);
return o;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
private static void createFile(String javaStr) {
File file = new File(path + "\\$Proxy0.java");
try {
FileWriter fw = new FileWriter(file);
fw.write(javaStr);
fw.flush();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* 运行时编译.java文件
* */
private static void compiler() {
try {
JavaCompiler systemJavaCompiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardFileManager = systemJavaCompiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> javaFileObjects = standardFileManager.getJavaFileObjects(path + "\\$Proxy0.java");
JavaCompiler.CompilationTask task = systemJavaCompiler.getTask(null, standardFileManager, null, null, null, javaFileObjects);
task.call();
standardFileManager.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static String getJavaStr(Class<?>[] interfaces) {
Method[] methods = interfaces[0].getMethods();
String proxyClassStr = "package com.zhuguang.jack.myProxy;" + rt
+ "import java.lang.reflect.Method;" + rt
+ "public class $Proxy0 implements " + interfaces[0].getName()
+ "{" + rt + "MyInvocationHandler h;" + rt
+ "public $Proxy0(MyInvocationHandler h) {" + rt
+ "this.h=h;" + rt + "}"
+ getMethodString(methods, interfaces[0]) + rt + "}";
return proxyClassStr;
}
private static String getMethodString(Method[] methods, Class intf) {
String proxyMe = "";
for (Method method : methods) {
proxyMe += "public void " + method.getName()
+ "() throws Throwable {" + rt + "Method md = "
+ intf.getName() + ".class.getMethod(\"" + method.getName()
+ "\",new Class[]{});" + rt
+ "this.h.invoke(this,md,null);" + rt + "}" + rt;
}
return proxyMe;
}
}
测试
public class Test {
public static void main(String[] args) {
People o = (People)MyProxy.newProxyInstance(Test.class.getClassLoader(),
new Class<?>[]{People.class}, new Parent(new Xiaoming()));
try {
o.zhaoduixiang();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
理解
就是通过解析被代理的类,生成了一个新的java类字符串,然后输出流把java类字符串转换成本地的文件,然后类加载器加载这个java文件,变异成为class文件,租后通过反射拿到Class,然后调用代理方法。
简单来说就是生成了一个全新的类,在我们看不见的地方。