什么是代理
简而言之,代理就是增强一个对象的功能。目标对象,指被增强对象,代理对象,指增强后的对象。
java实现代理的两种方法
静态代理
继承
代理对象继承目标对象,重写需要增强的方法;
缺点:会代理类过多,非常复杂
聚合
目标对象和代理对象实现同一个接口,代理对象当中要包含目标对象。
缺点:也会产生类很多,只不过比继承少一点点
总结:如果在不确定的情况下,尽量不要去使用静态代理。
动态代理
jdk的动态代理:
public interface TestDao {
String query(String str) throws Exception;
void doPrint(String str) throws Exception;
void doPrint() throws Exception;
}
public class TestDaoImpl implements TestDao {
@Override
public String query(String str) {
System.out.println("执行了query():"+str);
return "ok";
}
@Override
public void doPrint(String str) {
System.out.println("执行了doPrint(String str):"+str);
}
@Override
public void doPrint() {
System.out.println("执行了doPrint()");
}
}
public class JdkInvocationHandler implements InvocationHandler {
private Object target;
public JdkInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行增强逻辑
System.out.println("此处执行了增强逻辑......");
return method.invoke(target,args);
}
}
public class TestJdkProxy {
public static void main(String[] args) {
TestDao proxyInstance = (TestDao)Proxy.newProxyInstance(TestJdkProxy.class.getClassLoader(), new Class[]{TestDao.class},
new JdkInvocationHandler(new TestDaoImpl()));
proxyInstance.doPrint("hello");
}
}
通过ide反编译工具可以查看一下jdk生成的代理类文件:
由此可以得到jdk动态代理只能实现接口的原因,因为代理类$Proxy已经extends Proxy了,而java是单继承。
手写一个自己的模拟jdk动态代理:
import java.lang.reflect.Method;
public interface MyInvocationHandler {
Object invoke(Method method);
Object invoke(Object proxy, Method method, Object[] args);
}
import java.lang.reflect.Method;
public class MyJdkInvocationHandler implements MyInvocationHandler {
private Object target;
public MyJdkInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Method method) {
//此处实现无参方法增强逻辑
System.out.println("实现了无参方法增强逻辑...");
try {
return method.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
//此处实现有参方法增强逻辑
System.out.println("实现了有参增强逻辑...");
try {
return method.invoke(target,args);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
/**
* 我自己写的proxy类
*/
public class Proxy {
public static Object newInstance(ClassLoader loader,
Class<?>[] interfaces,
MyInvocationHandler h){
Object proxy=null ;
//只实现一个接口
Class targetInf = interfaces[0];
//获取接口的所有方法
Method methods[] =targetInf.getDeclaredMethods();
//接口名
String infName = targetInf.getSimpleName();
/**
* 接下来开始拼接生成代理类的.java文件
*/
String content = generateProxyContent(targetInf,infName,methods);
File file =new File("d:\\com\\su\\$Proxy.java");
try {
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file);
fw.write(content);
fw.flush();
fw.close();
/**
* 将.java编译成.class 这段代码不是关键
*/
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
Iterable units = fileMgr.getJavaFileObjects(file);
JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
t.call();
fileMgr.close();
URL[] urls = new URL[]{new URL("file:D:\\\\")};
URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class clazz = urlClassLoader.loadClass("com.su.$Proxy");
Constructor constructor = clazz.getConstructor(MyInvocationHandler.class);
proxy = constructor.newInstance(h);
}catch (Exception e){
e.printStackTrace();
}
return proxy;
}
private static String generateProxyContent(Class targetInf,String infName,Method methods[]){
String line="\n";
String tab ="\t";
String content ="";
String packageContent = "package com.su;"+line;
String importContent = "import "+targetInf.getName()+";"+line
+"import com.proxy.util.MyInvocationHandler;"+line
+"import java.lang.Exception;"+line
+"import java.lang.reflect.Method;"+line;
String clazzFirstLineContent = "public class $Proxy implements "+infName+"{"+line;
String filedContent =tab+"private MyInvocationHandler h;"+line;
String constructorContent =tab+"public $Proxy (MyInvocationHandler h){" +line
+tab+tab+"this.h =h;"
+line+tab+"}"+line;
String methodContent = "";
for (Method method : methods) {
String returnTypeName = method.getReturnType().getSimpleName();
String methodName =method.getName();
Class args[] = method.getParameterTypes();
int parameterCount = method.getParameterCount();
String argsContent = "";
String paramsContent="";
String argsField = "";
int flag =0;
for (Class arg : args) {
String temp = arg.getSimpleName();
//String
//String arg0,Sting arg1,
argsContent+=temp+" arg"+flag+",";
argsField+="arg"+flag+",";
paramsContent+=arg.getSimpleName()+".class,";
flag++;
}
if (argsContent.length()>0){
argsContent=argsContent.substring(0,argsContent.lastIndexOf(",")-1);
argsField=argsField.substring(0,argsField.lastIndexOf(",")-1);
paramsContent=paramsContent.substring(0,paramsContent.lastIndexOf(","));
}
//方法无参
if(parameterCount==0){
if("void".equals(returnTypeName)){
methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+")throws Exception {"+line
+tab+tab+"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\");"+line
+tab+tab+"h.invoke(method);"+line;
methodContent+=tab+"}"+line;
}else{
methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+")throws Exception {"+line
+tab+tab+"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\");"+line
+tab+tab+"return ("+returnTypeName+")h.invoke(method);"+line;
methodContent+=tab+"}"+line;
}
}else{
//方法有参
if("void".equals(returnTypeName)){
methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+")throws Exception {"+line
+tab+tab+"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\","+paramsContent+");"+line
+tab+tab+"h.invoke(null,method,new Object[]{"+argsField+"});"+line;
methodContent+=tab+"}"+line;
}else{
methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+")throws Exception {"+line
+tab+tab+"Method method = Class.forName(\""+targetInf.getName()+"\").getDeclaredMethod(\""+methodName+"\","+paramsContent+");"+line
+tab+tab+"return ("+returnTypeName+")h.invoke(null,method,new Object[]{"+argsField+"});"+line;
methodContent+=tab+"}"+line;
}
}
}
content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}";
return content;
}
}
public class TestMyJdkProxy {
public static void main(String[] args) throws Exception{
TestDao proxyInstance = (TestDao)Proxy.newInstance(TestMyJdkProxy.class.getClassLoader(),new Class[]{TestDao.class},
new MyJdkInvocationHandler(new TestDaoImpl()));
// proxyInstance.doPrint("hello");
// proxyInstance.doPrint();
proxyInstance.query("hello");
}
}
中间生成的.java文件如下:
我们发现,调用代理对象的某个方法时候,会调用h.invoke()方法,这也解释了为什么会执行invoke。
至此完成了自己的动态代理,但和jdk的动态代理相比还有许多的缺点,比如进行了磁盘IO,没有使用缓存等会加大性能开销。