1、代理特点
执行者、被代理人
对于被代理人来说,这件事情一定要做,但是又不想去做或者没时间去做
需要获取被代理人的个人资料
2、jdk动态代理
这里以一个买票的实例进行深入
买票服务
public interface Person {
//买票
void butTicket();
String getBegin();
String getName();
String getDestination();
String getDate();
}
具体买票对象
public class Zhangsan implements Person {
private String begin="贵阳";
private String name="张三";
private String destination="广州";
private String date="2018-12-12";
public void butTicket() {
System.out.println("我叫"+name);
System.out.println("我要买一张"+date+begin+"开往"+destination+"的票");
}
//..setter..getter
}
黄牛:
public class HuangNiu implements InvocationHandler {
private Person target;
//获取被代理人的个人资料
public Object getInstance(Person target) throws Exception{
this.target=target;
Class<? extends Person> clazz = target.getClass();
System.out.println("被代理对象的class是:"+clazz);
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("姓名:"+this.target.getName());
System.out.println("起始地点:"+this.target.getBegin());
System.out.println("出发日期:"+this.target.getDate());
System.out.println("目的地:"+this.target.getDestination());
System.out.println("-------------------------------------");
this.target.butTicket();
System.out.println("-------------------------------------");
System.out.println("价格合适的话,请付尾款吧!!!");
return null;
}
}
测试类:
public class TestBuyTicket {
public static void main(String[] args) {
try {
Person instance = (Person) new HuangNiu().getInstance(new Zhangsan());
System.out.println(instance.getClass());
instance.butTicket();
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果
原理:
(1)拿到被代理对象的引用,获取其接口
(2)JDK代理重新生成一个类,同时实现其代理对象所实现的接口
(3)把被代理对象的引用也拿到了
(4)重新动态生成一个class字节码
(5)编译执行
证明:
在上述的测试类中作如下修改:
public class TestBuyTicket {
public static void main(String[] args) {
try {
Person instance = (Person) new HuangNiu().getInstance(new Zhangsan());
System.out.println(instance.getClass());
instance.butTicket();
/**
* 原理:
*
* (1)拿到被代理对象的引用,获取其接口
*
* (2)JDK代理重新生成一个类,同时实现其代理对象所实现的接口
*
* (3)把被代理对象的引用也拿到了
*
* (4)重新动态生成一个class字节码
*
* (5)编译执行
*/
//获取字节码内容
byte[] data = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{instance.getClass()});
FileOutputStream os = new FileOutputStream("./$Proxy0.class");
os.write(data);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
反编译工具打开生成的代理类:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import com.itboy.jdk.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Person {
private static Method m1;
private static Method m5;
private static Method m3;
private static Method m2;
private static Method m6;
private static Method m4;
private static Method m7;
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 String getBegin() throws {
try {
return (String)super.h.invoke(this, m5, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String getName() throws {
try {
return (String)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 String getDestination() throws {
try {
return (String)super.h.invoke(this, m6, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String getDate() throws {
try {
return (String)super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void butTicket() throws {
try {
super.h.invoke(this, m7, (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"));
m5 = Class.forName("com.itboy.jdk.Person").getMethod("getBegin");
m3 = Class.forName("com.itboy.jdk.Person").getMethod("getName");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m6 = Class.forName("com.itboy.jdk.Person").getMethod("getDestination");
m4 = Class.forName("com.itboy.jdk.Person").getMethod("getDate");
m7 = Class.forName("com.itboy.jdk.Person").getMethod("butTicket");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到其实现了Person接口,并且也实现了接口的方法(这里实现了接口所有的方法,但是调用哪个方法他就代理哪个方法,这里要区分好)。到这里应该就比较清晰了!!接下来只要将这个类加载到jvm不就可以执行了吗。。不过接下来这一步也来实践操作一番。。。
(1)搞一个invocationHandler,最后的方法调用肯定需要这个嘛
public interface MyInvocationHandler {
//调用
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
(2)Proxy中的返回实例也是必须的
//生成代理对象的代码
public class MyProxy {
private static String ln="\r\n";
public static Object newProxyInstance(MyClassLoader loader,
Class<?>[] interfaces,
MyInvocationHandler h)
throws IllegalArgumentException, IOException {
return null;
}
(3)类加载器
//加载字节码到JVM,编译执行
public class MyClassLoader extends ClassLoader{
}
(4)黄牛代理,注意其中的替换
public class MyHuangNiu implements MyInvocationHandler{
private Person target;
//获取被代理人的个人资料
public Object getInstance(Person target) throws Exception{
this.target=target;
Class<? extends Person> clazz = target.getClass();
System.out.println("被代理对象的class是:"+clazz);
return MyProxy.newProxyInstance(new MyClassLoader(),clazz.getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("姓名:"+this.target.getName());
System.out.println("起始地点:"+this.target.getBegin());
System.out.println("出发日期:"+this.target.getDate());
System.out.println("目的地:"+this.target.getDestination());
System.out.println("-------------------------------------");
this.target.butTicket();
System.out.println("-------------------------------------");
System.out.println("价格合适的话,请付尾款吧!!!");
return null;
}
}
(5)接下来肯定就是要动态生成.java文件嘛。动态代理不就是动态生成类然后编译执行吗?那么这个在哪生成的呢?毫无疑问在Proxy的new..方法中,因为实例是从这里返回的。。
//生成代理对象的代码
public class MyProxy {
private static String ln="\r\n";
public static Object newProxyInstance(MyClassLoader loader,
Class<?>[] interfaces,
MyInvocationHandler h)
throws IllegalArgumentException, IOException {
//1、生成源代码
String proxySrc=generateSource(interfaces);
//2、将生成的源代码输出到磁盘,保存为.java文件
String filePath = MyProxy.class.getResource("").getPath();
File file = new File(filePath+"$Proxy0.java");
FileWriter fw=new FileWriter(file);
fw.write(proxySrc);
fw.flush();
fw.close();
//3、编译源代码,并且生成.class文件
//4、将class文件中的内容,动态加载到JVM
//5、返回被代理后的代理对象
return null;
}
//生成源代码,这里为了简单仅一个接口作为例子
private static String generateSource(Class<?>[] interfaces){
StringBuffer sb = new StringBuffer();
sb.append("package com.itboy.custom;"+ln);
sb.append("import java.lang.reflect.Method;"+ln);
sb.append("import java.lang.reflect.UndeclaredThrowableException;"+ln);
sb.append("public class $Proxy0 implements "+interfaces[0].getName()+"{"+ln);
sb.append("MyInvocationHandler h;"+ln);
sb.append("public $Proxy0(MyInvocationHandler h) {"+ln);
sb.append("this.h=h;"+ln);
sb.append("}"+ln);
for (Method m:interfaces[0].getMethods()){
sb.append("public "+m.getReturnType().getName()+" "+m.getName()+"() {"+ln);
sb.append("try{" + ln);
sb.append("Method m="+interfaces[0].getName()+".class.getMethod(\""+m.getName()+"\",new Class[]{});"+ln);
//需要返回类型的
if (!m.getReturnType().getName().equals("void"))
sb.append("return ("+m.getReturnType().getName()+")");
sb.append("this.h.invoke(this,m,null);"+ln);
sb.append("} catch (RuntimeException | Error var2) {"+ln);
sb.append("throw var2;"+ln);
sb.append("} catch (Throwable var3) {"+ln);
sb.append("throw new UndeclaredThrowableException(var3);"+ln);
sb.append("}"+ln);
sb.append("}"+ln);
}
sb.append("}");
return sb.toString();
}
}
测试类:
new MyHuangNiu().getInstance(new Zhangsan());
执行后,在bin目录下可以看到一个名为$Proxy0的.java文件,内容如下:
package com.itboy.custom;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
public class $Proxy0 implements com.itboy.jdk.Person{
MyInvocationHandler h;
public $Proxy0(MyInvocationHandler h) {
this.h=h;
}
public java.lang.String getName() {
try{
Method m=com.itboy.jdk.Person.class.getMethod("getName",new Class[]{});
return (java.lang.String)this.h.invoke(this,m,null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public java.lang.String getDate() {
try{
Method m=com.itboy.jdk.Person.class.getMethod("getDate",new Class[]{});
return (java.lang.String)this.h.invoke(this,m,null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public void buyTicket() {
try{
Method m=com.itboy.jdk.Person.class.getMethod("buyTicket",new Class[]{});
this.h.invoke(this,m,null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public void butTicket() {
try{
Method m=com.itboy.jdk.Person.class.getMethod("butTicket",new Class[]{});
this.h.invoke(this,m,null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public java.lang.String getBegin() {
try{
Method m=com.itboy.jdk.Person.class.getMethod("getBegin",new Class[]{});
return (java.lang.String)this.h.invoke(this,m,null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public java.lang.String getDestination() {
try{
Method m=com.itboy.jdk.Person.class.getMethod("getDestination",new Class[]{});
return (java.lang.String)this.h.invoke(this,m,null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
将其放到src中检验其正确性。。。。。。
(6)这一步肯定就是将这个Java文件编译为字节码.class文件咯,在MyProxy中添加如下代码
//3、编译源代码,并且生成.class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
Iterable interable = manager.getJavaFileObjects(file);
JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, interable);
task.call();
manager.close();
继续执行上述测试类,可以看到了一个$Porxy0.class文件,反编译打开如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.itboy.custom;
import com.itboy.jdk.Person;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
public class $Proxy0 implements Person {
MyInvocationHandler h;
public $Proxy0(MyInvocationHandler var1) {
this.h = var1;
}
public String getName() {
try {
Method var1 = Person.class.getMethod("getName");
return (String)this.h.invoke(this, var1, (Object[])null);
} catch (Error | RuntimeException var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public String getDate() {
try {
Method var1 = Person.class.getMethod("getDate");
return (String)this.h.invoke(this, var1, (Object[])null);
} catch (Error | RuntimeException var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public void buyTicket() {
try {
Method var1 = Person.class.getMethod("buyTicket");
this.h.invoke(this, var1, (Object[])null);
} catch (Error | RuntimeException var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public void butTicket() {
try {
Method var1 = Person.class.getMethod("butTicket");
this.h.invoke(this, var1, (Object[])null);
} catch (Error | RuntimeException var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public String getBegin() {
try {
Method var1 = Person.class.getMethod("getBegin");
return (String)this.h.invoke(this, var1, (Object[])null);
} catch (Error | RuntimeException var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public String getDestination() {
try {
Method var1 = Person.class.getMethod("getDestination");
return (String)this.h.invoke(this, var1, (Object[])null);
} catch (Error | RuntimeException var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
(7)接下来就要用到类加载器加载.class文件到jvm中了
//加载字节码到JVM,编译执行
public class MyClassLoader extends ClassLoader{
private File baseDir;
public MyClassLoader(){
String path = MyClassLoader.class.getResource("").getPath();
this.baseDir=new File(path);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String className = MyClassLoader.class.getPackage().getName() + "." + name;
if (baseDir!=null){
File classFile=new File(baseDir,name.replaceAll("\\.","/")+".class");
if (classFile.exists()){
FileInputStream in=null;
try {
in=new FileInputStream(classFile);
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buff=new byte[1024];
int len;
while ((len=in.read(buff))!=-1){
out.write(buff,0,len);
}
return defineClass(className,out.toByteArray(),0,out.size());
} catch (Exception e){
e.printStackTrace();
} finally {
if (null!=in){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return null;
}
}
然后再MyProxy中添加:
//4、将class文件中的内容,动态加载到JVM
//简单起见,写死哈哈
Class<?> proxyClass = loader.findClass("$Proxy0");
Constructor<?> constructor = proxyClass.getConstructor(MyInvocationHandler.class);
//5、返回被代理后的代理对象
return constructor.newInstance(h);
测试类:
Person obj = (Person) new MyHuangNiu().getInstance(new Zhangsan());
System.out.println(obj.getClass());
obj.butTicket();
执行后输出如下:
和第一个输出有所不同吧???????????,当然将这个过程中生成的动态类执行完删除就更完美了,这里就不粘贴了。。