jdk动态代理使用及原理详解编程语言

jdk动态代理的使用

1.创建实现InvocationHandler接口的类,实现invoke(Object proxy, Method method, Object[] args)接口,其中invoke()执行的方法就为代理实例对象执行的方法。

其中proxy为代理对象,method为方法,args为方法的参数。

要想在原类方法上在进行再处理(如记录日志等),需要用构造方法把接口子类的实例传入,用method.invoke(原对象,args),则执行invoke里的函数原函数。

2.创建代理实例对象

   public static <T> T getProxy(Class<T> inf){
        return (T) Proxy.newProxyInstance(inf.getClassLoader(),new Class[]{inf},new ProxyInvocationHandler());
   }

其中inf为借口,第一个参数为借口的类加载器,第二个参数为接口的Class数组,第三个为代理的执行体。

最后根据返回的接口代理实例对象执行相应的方法即可。

/** 
 * 被代理接口 
 */ 
public interface Myinterface { 
    public List<Object> queryList(); 
    public String getName(); 
} 
 
/** 
 * 代理方法,Mybatis中接口无实例类, 
 * 所以此处理并不是在方法执行前后加日志等处理 
 * 而是生成数据库操作执行体 
 */ 
public class MapperProxy implements InvocationHandler { 
    @Override 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
        //当方法名为queryList时 
        if (method.getName().equals("queryList")) { 
            List list = new ArrayList(); 
            list.add("方法名为queryList()的代理执行结果"); 
            list.add(2); 
            list.add("autumn"); 
            return list; 
        } 
        //当放回类型为String时 
        if (method.getReturnType().toString().equals(String.class.toString())){ 
            return "查询数据库返回的字符串"; 
        } 
        return null; 
    } 
} 
 
/** 
 * 根据接口名称获取代理实例 
 */ 
public class SqlSession { 
    /** 
     * 获取接口代理对象实例 
     * @param inf 接口 
     * @param <T> 该接口的代理对象实例 
     * @return 
     */ 
    public static <T> T getMapper(Class<T> inf){ 
       return (T)Proxy.newProxyInstance(inf.getClassLoader(),new Class[]{inf},new MapperProxy()); 
    } 
} 
 
/** 
 * 测试接口无子类代理对象 
 */ 
public class Demo { 
    public static void main(String[] args) { 
        Myinterface inf = SqlSession.getMapper(Myinterface.class); 
        List<Object> result = inf.queryList(); 
        System.out.println("返回结果:"+result); 
        System.out.println("----------------------------------------"); 
        System.out.println("返回结果:"+inf.getName()); 
        System.out.println("----------------------------------------"); 
    } 
}

还有一种用法是传递接口的子类实例对象,在子类的实例对象方法执行前后加上其他操作(如日志处理等),传送门

jdk动态代理原理

jdk动态代理主要是一个构造函数newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)的原理

/** 
* 车类接口 
*/ 
public interface Moveable { 
public void move(); 
} 
/** 
* 车类接口具体实现类 
*/ 
public class Car implements Moveable { 
@Override 
public void move() { 
try { 
Thread.sleep(new Random().nextInt(3*1000)); 
} catch (InterruptedException e) { 
e.printStackTrace(); 
} 
System.out.println("car is running..."); 
} 
} 
/** 
* 模拟jdk中的InvocationHandler接口 
*/ 
public interface InvocationHandler { 
public void invoke(Object o, Method m); 
} 
/** 
* 代理实例的处理方法 
* 在原本的实例基础上进行增加功能 
* 接口也可以无子类,直接通过接口方法名调用代理的invoke方法 
*/ 
public class TimeHandler implements InvocationHandler { 
private Object target; 
public TimeHandler(Object target) { 
super(); 
this.target = target; 
} 
@Override 
public void invoke(Object o, Method m) { 
try { 
long starttime = System.currentTimeMillis(); 
System.out.println("开始计时...."); 
m.invoke(target); 
long endtime = System.currentTimeMillis(); 
System.out.println("计时结束,用时" 
+ (endtime - starttime) + "毫秒"); 
} catch (Exception e) { 
e.printStackTrace(); 
} 
} 
} 
public class Proxy { 
/** 
* 根据接口动态生成接口的子类对象并实现方法体,执行方法为h的invoke 
* @param infce 被代理接口 
* @param h  被代理类的方法执行体 
* @return 
* @throws Exception 
*/ 
@SuppressWarnings("unchecked") 
public static Object newProxyInstance(Class infce,InvocationHandler h) throws Exception{ 
String rt = "/r/n"; 
String methodStr = ""; 
for(Method m : infce.getMethods()){ 
methodStr += "    @Override" + rt + 
"    public void " + m.getName() + "() {" + rt + 
"  try{" + rt + 
"  Method md = " + infce.getName() + ".class.getMethod(/"" 
+ m.getName() + "/");" + rt + 
"  h.invoke(this,md);" +rt+ 
"  }catch(Exception e){ e.printStackTrace();}" + rt + 
"    }" ; 
} 
String str = 
"package com.qy.dymanic.jdkproxycode;" + rt + 
"import java.lang.reflect.Method;" + rt + 
"import com.qy.dymanic.jdkproxycode.InvocationHandler;" +  rt+ 
"public class $Proxy0 implements " + infce.getName() + " {" + rt + 
"    public $Proxy0(InvocationHandler h) {" + rt + 
"        this.h = h;" + rt + 
"    }" + rt + 
"  private InvocationHandler h;" + rt+ 
methodStr + rt + 
"}" ; 
//产生代理类的java文件 
String filename = System.getProperty("user.dir") +"/bin/com/qy/dymanic/jdkproxycode/$Proxy0.java"; 
File file = new File(filename); 
FileUtils.writeStringToFile(file, str); 
//编译 
//拿到编译器 
JavaCompiler complier = ToolProvider.getSystemJavaCompiler(); 
//文件管理者 
StandardJavaFileManager fileMgr = 
complier.getStandardFileManager(null, null, null); 
//获取文件 
Iterable units = fileMgr.getJavaFileObjects(filename); 
//编译任务 
CompilationTask t = complier.getTask(null, fileMgr, null, null, null, units); 
//进行编译 
        t.call(); 
fileMgr.close(); 
//load 到内存 
ClassLoader cl = ClassLoader.getSystemClassLoader(); 
Class c = cl.loadClass("com.qy.dymanic.jdkproxycode.$Proxy0"); 
Constructor ctr = c.getConstructor(InvocationHandler.class); 
return ctr.newInstance(h); 
} 
} 
/** 
* 模拟jdk动态代理源码 
* @throws Exception  
*/ 
public static void main(String[] args) throws Exception { 
Car car = new Car(); 
InvocationHandler h = new TimeHandler(car); 
Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class,h); 
m.move(); 
}

会在bin目录下生成两个文件

jdk动态代理使用及原理详解编程语言

其中生成的$Proxy0.java代码如下,此类为动态生成的接口的代理实例类。

package com.qy.dymanic.jdkproxycode; 
import java.lang.reflect.Method; 
import com.qy.dymanic.jdkproxycode.InvocationHandler; 
public class $Proxy0 implements com.qy.dymanic.jdkproxycode.Moveable { 
public $Proxy0(InvocationHandler h) { 
this.h = h; 
} 
private InvocationHandler h; 
@Override 
public void move() { 
try{ 
Method md = com.qy.dymanic.jdkproxycode.Moveable.class.getMethod("move"); 
h.invoke(this,md);    //这个是重点!!!InvocationHandler调用它的invoke(代理类,方法名)
   }catch(Exception e){ e.printStackTrace();} } }

源码

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/17824.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论