静态代理:编译时就已经确定了代理关系,代理类和被代理类均实现同一个接口,且代理类持有被代理类的引用。
动态代理:运行时确定代理关系,生成代理类,代理类持有特定的Handler类的引用,并由此特定类的引用通过反射调用被代理类的方法,在调用被代理类方法时,在被代理类方法前后插入代理类方法。
JDK动态代理:
接口:
/**
* @projectName:JavaProxy
* @fileName:People.java
* @packageName:club.younge.proxy
* @date:2016年9月4日上午11:29:44
* @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.
*
*/
package club.younge.proxy;
/**
* @className:People
* @function: TODO ADD FUNCTION.
* @reason: TODO ADD REASON.
* @date: 2016年9月4日 上午11:29:44
* @author Younge
* @version
* @since JDK 1.8
* @see
*/
public interface People {
public void eat() throws Throwable;
public void sleep() throws Throwable;
public void sport() throws Throwable;
}
被代理类:
/**
* @projectName:JavaProxy
* @fileName:Jason.java
* @packageName:club.younge.proxy
* @date:2016年9月4日上午11:31:55
* @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.
*
*/
package club.younge.proxy;
/**
* @className:Jason
* @function: TODO ADD FUNCTION.
* @reason: TODO ADD REASON.
* @date: 2016年9月4日 上午11:31:55
* @author Younge
* @version
* @since JDK 1.8
* @see
*/
public class Jason implements People{
@Override
public void eat() {
System.out.println("Jason is eating now.");
}
@Override
public void sleep() {
System.out.println("Jason is sleeping now.");
}
@Override
public void sport() {
System.out.println("Jason is sporting now.");
}
}
实现JDK的一个调用接口ProxyHandler:
/**
* @projectName:JavaProxy
* @fileName:ProxyHandler.java
* @packageName:club.younge.proxy.jdk
* @date:2016年9月4日上午11:34:57
* @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.
*
*/
package club.younge.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import club.younge.proxy.People;
/**
* @className:ProxyHandler
* @function: TODO ADD FUNCTION.
* @reason: TODO ADD REASON.
* @date: 2016年9月4日 上午11:34:57
* @author Younge
* @version
* @since JDK 1.8
* @see
*/
public class ProxyHandler implements InvocationHandler {
private People people;
public ProxyHandler(People people) {
this.people = people;
}
@Override
public Object invoke(Object obj, Method method, Object[] objArray) throws Throwable {
before();
method.invoke(people, objArray);
after();
return null;
}
private void before(){
System.out.println("Before eat you should washes hands!");
}
private void after(){
System.out.println("After eat you should do some work!");
}
}
JDK动态代理测试类:
/**
* @projectName:JavaProxy
* @fileName:SelfTest.java
* @packageName:club.younge.test
* @date:2016年9月4日上午11:44:37
* @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.
*
*/
package club.younge.test;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;
import club.younge.proxy.Jason;
import club.younge.proxy.People;
import club.younge.proxy.jdk.ProxyHandler;
import sun.misc.ProxyGenerator;
/**
* @className:SelfTest
* @function: TODO ADD FUNCTION.
* @reason: TODO ADD REASON.
* @date: 2016年9月4日 上午11:44:37
* @author Younge
* @version
* @since JDK 1.8
* @see
*/
public class JdkTest {
public static void main(String[] args) throws Throwable {
People people = (People) Proxy.newProxyInstance(People.class.getClassLoader(), new Class[]{People.class}, new ProxyHandler(new Jason()));
people.eat();
System.out.println("People's actual class:" + people.getClass().getName());
System.out.println("Game over!");
//createProxyFile();
}
public static void createProxyFile() throws IOException{
String proxyName = "ProxyPeople";
byte[] data = ProxyGenerator.generateProxyClass(proxyName, new Class[]{People.class}); //调试时的名称为$Proxy0
FileOutputStream fos = new FileOutputStream(proxyName + ".class");
fos.write(data);
fos.close();
}
}
测试结果:
Before eat you should washes hands!
Jason is eating now.
After eat you should do some work!
People's actual class:com.sun.proxy.$Proxy0
Game over!
从测试结果可以看出,JDK动态生成的代理类为com.sun.proxy.$Proxy0(类全名),将此动态生成的代理类的字节码输出为文件,然后通过JD-GUI反编译为java文件,代码如下:
package club.younge.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import club.younge.proxy.People;
/**
* generated by jdkTest and decompiled by jd-gui.
* @author Younge
*
*/
public final class ProxyPeople extends Proxy implements People {
private static final long serialVersionUID = 1L;
private static Method m1;
private static Method m4;
private static Method m2;
private static Method m5;
private static Method m3;
private static Method m0;
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m4 = Class.forName("club.younge.proxy.People").getMethod("eat", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m5 = Class.forName("club.younge.proxy.People").getMethod("sport", new Class[0]);
m3 = Class.forName("club.younge.proxy.People").getMethod("sleep", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
// return;
} catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
public ProxyPeople(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject) {
try {
return ((Boolean) this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void eat() {
try {
this.h.invoke(this, m4, null);
return;
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString() {
try {
return (String) this.h.invoke(this, m2, null);
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void sport() {
try {
this.h.invoke(this, m5, null);
return;
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void sleep() {
try {
this.h.invoke(this, m3, null);
return;
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode() {
try {
return ((Integer) this.h.invoke(this, m0, null)).intValue();
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
}
从反编译的代码看出,动态生成的代理类,继承JDK的Proxy类,并实现了我们自定义的People接口,并持有,我们实现的ProxyHandler类的引用。在调用代理类方法时,实则调用了ProxyHandler的invoke方法,然后在此invoke方法中,反射调用被代理类方法。最终代理类调用了被代理类的方法。
自己实现JDK的动态代理过程:
接口和被代理类如上。
我们需要手动实现JDK定义的Proxy的newProxyInstance()方法。
/**
* @projectName:JavaProxy
* @fileName:MyProxy.java
* @packageName:club.younge.proxy.self
* @date:2016年9月5日下午5:34:24
* @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.
*
*/
package club.younge.proxy.self;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
/**
* @className:MyProxy
* @function: TODO ADD FUNCTION.
* @reason: TODO ADD REASON.
* @date: 2016年9月5日 下午5:34:24
* @author Younge
* @version
* @since JDK 1.8
* @see
*/
public class MyProxy {
private static String rt = "\r\n";
@SuppressWarnings({ "rawtypes", "unchecked" })
public static Object newProxyInstance(ClassLoader classLoader, Class interfs,
MyInvocationHandler invocationHandler) {
try {
Method[] methods = interfs.getMethods();
// 1.创建一个JAVA文件,用流的方式创建一个JAVA文件,生成JAVA文件String内容
String proxyClass = "package club.younge.proxy.self;" + rt + "import java.lang.reflect.Method;" + rt
+ "public class $Proxy0 implements " + interfs.getName() + "{" + rt + "MyInvocationHandler h;" + rt
+ "public $Proxy0(MyInvocationHandler h) {" + rt + "this.h = h;" + rt + "}"
+ getMethodStr(methods, interfs) + rt + "}";
// 2.类生成文件
String fileName = "E:/GitLocal/Java/JavaProxy/JavaProxy" //请务必注意文件路径是否正确,检查您的工程目录
+ "/src/club/younge/proxy/self/$Proxy0.java";
File file = new File(fileName);
FileWriter fileWriter = new FileWriter(file);
fileWriter.write(proxyClass);
fileWriter.flush();
fileWriter.close();
// 3. 编译JAVA文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager stdFileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = stdFileMgr.getJavaFileObjects(fileName);
CompilationTask task = compiler.getTask(null, stdFileMgr, null, null, null, units);
task.call();
stdFileMgr.close();
//file.delete(); //编译后可将该文件删除,造成与JDK动态代理一样的效果
// 4.把class字节码加载到内存中 请务必注意文件路径是否正确,检查您的工程目录
MyClassLoader classLoader2 = new MyClassLoader("E:/GitLocal/Java/JavaProxy/JavaProxy"
+ "/src/club/younge/proxy/self/");
Class proxy0Class = classLoader2.findClass("$Proxy0");
Constructor constructor = proxy0Class.getConstructor(MyInvocationHandler.class);
Object object = constructor.newInstance(invocationHandler);
return object;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
@SuppressWarnings("rawtypes")
private static String getMethodStr(Method[] methods, Class interfs) {
StringBuilder builder = new StringBuilder();
builder.append(rt);
for (Method method : methods) {
builder.append("public void ").append(method.getName()).append("() throws Throwable {").append(rt)
.append("Method md = ").append(interfs.getName()).append(".class.getMethod(\"")
.append(method.getName()).append("\", new Class[]{});").append(rt)
.append("this.h.invoke(this, md, null);").append(rt).append("}").append(rt);
}
return builder.toString();
}
}
此方法主要做:
1.拼凑动态代理类的字符串
2.将上述字符串写入.java文件中
3.编译.java文件为class字节码,
4.将字节码文件加载到内存中
自定义invocationhandler接口及对应接口实现类和ClassLoader类:
/**
* @projectName:JavaProxy
* @fileName:MyInvocationHandler.java
* @packageName:club.younge.proxy.self
* @date:2016年9月5日下午5:30:26
* @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.
*
*/
package club.younge.proxy.self;
import java.lang.reflect.Method;
/**
* @className:MyInvocationHandler
* @function: TODO ADD FUNCTION.
* @reason: TODO ADD REASON.
* @date: 2016年9月5日 下午5:30:26
* @author Younge
* @version
* @since JDK 1.8
* @see
*/
public interface MyInvocationHandler {
public Object invoke(Object obj, Method method, Object[] objArray) throws Throwable;
}
/**
* @projectName:JavaProxy
* @fileName:MyProxyHandler.java
* @packageName:club.younge.proxy.self
* @date:2016年9月5日下午5:31:06
* @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.
*
*/
package club.younge.proxy.self;
import java.lang.reflect.Method;
import club.younge.proxy.People;
/**
* @className:MyProxyHandler
* @function: TODO ADD FUNCTION.
* @reason: TODO ADD REASON.
* @date: 2016年9月5日 下午5:31:06
* @author Younge
* @version
* @since JDK 1.8
* @see
*/
public class MyProxyHandler implements MyInvocationHandler{
private People people;
public MyProxyHandler(People people) {
this.people = people;
}
@Override
public Object invoke(Object obj, Method method, Object[] objArray) throws Throwable {
before();
people.eat();
after();
return null;
}
private void before(){
System.out.println("Before eat you should washes hands!");
}
private void after(){
System.out.println("After eat you should do some work!");
}
}
/**
* @projectName:JavaProxy
* @fileName:MyClassLoader.java
* @packageName:club.younge.proxy.self
* @date:2016年9月5日下午11:35:07
* @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.
*
*/
package club.younge.proxy.self;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* @className:MyClassLoader
* @function: TODO ADD FUNCTION.
* @reason: TODO ADD REASON.
* @date: 2016年9月5日 下午11:35:07
* @author Younge
* @version
* @since JDK 1.8
* @see
*/
public class MyClassLoader extends ClassLoader {
private File dir;
public MyClassLoader(String path) {
dir = new File(path);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (dir != null) {
File classFile = new File(dir, name + ".class");
if (classFile.exists()) {
FileInputStream fis = null;
try {
fis = new FileInputStream(classFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = fis.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
return defineClass("club.younge.proxy.self." + name, baos.toByteArray(), 0, baos.size());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return super.findClass(name);
}
}
/**
* @projectName:JavaProxy
* @fileName:SelfTest.java
* @packageName:club.younge.test
* @date:2016年9月5日下午5:38:08
* @copyright (c) 2016, heqy@finansir.nt All Rights Reserved.
*
*/
package club.younge.test;
import club.younge.proxy.Jason;
import club.younge.proxy.People;
import club.younge.proxy.self.MyProxy;
import club.younge.proxy.self.MyProxyHandler;
/**
* @className:SelfTest
* @function: TODO ADD FUNCTION.
* @reason: TODO ADD REASON.
* @date: 2016年9月5日 下午5:38:08
* @author Younge
* @version
* @since JDK 1.8
* @see
*/
public class SelfTest {
public static void main(String[] args) throws Throwable {
People people = (People) MyProxy.newProxyInstance(People.class.getClassLoader(), People.class, new MyProxyHandler(new Jason()));
people.eat();
System.out.println("Game over!");
}
}
测试结果:
Before eat you should washes hands!
Jason is eating now.
After eat you should do some work!
Game over!
与JDK动态代理效果类似。