什么是动态代呢
在运行期动态生成代理对象,控制被代理对象的访问,这有别于静态代理在编译期生成代理对象。
作用是什么
代理对象可以丰富被代理对象的操作,大白话,就是可以在访问前后增加若干代码处理,例如在方法前后打印日志判断方法执行时间。
动态代理例子1 - 生成代理对象
Proxy.newProxyInstance 是JDK提供生成动态代理对象的api。通过newProxyInstance动态生成Api的实现类,调用setName以及getName方法,都会回调到InvocationHandler的invoke方法,invoke就是对接口方法的具体实现。
//Api接口定义两个方法
public interface Api {
void setName(String name);
String getName();
}
//调用
public void test() {
Api api = (Api) Proxy.newProxyInstance(Api.class.getClassLoader(), new Class[]{Api.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.d("test", "method name:" + method.getName());//打印调用的方法名
if ("getName".equals(method.getName())) {//如果是调用getName则返回"yang"
return "yang";
} else if ("setName".equals(method.getName())) {//如果是调用setName方法则打印请求参数
for (Object a : args) {//打印参数
Log.d("test", "args:" + a);
}
}
return null;//对应无返回值的方法,直接返回null即可。
}
});
//调用处
api.setName("yang");
String name=api.getName();
Log.d("test", "getName=" +name );
}
打印结果如下:
2020-09-23 10:40:47.150 20218-20218/com.yang.myapplication6 D/test: method name:setName
2020-09-23 10:40:47.150 20218-20218/com.yang.myapplication6 D/test: args:yang
2020-09-23 10:40:47.150 20218-20218/com.yang.myapplication6 D/test: method name:getName
2020-09-23 10:40:47.150 20218-20218/com.yang.myapplication6 D/test: getName=yang
动态代理例子2 - 增强被代理对象的能力
MyApi 是Api接口的具体实现,同样通过Proxy.newProxyInstance生成代理对象,不同的是,在invoke方法中,增加代码处理以后需要反射调用MyApi 实现的方法。
public class MyApi implements Api {
@Override
public void setName(String name) {
Log.d("test", "setName name=" + name);
}
@Override
public String getName() {
return "getName";
}
}
final Api api = new MyApi();//被代理对象
//返回代理对象
Api proxyApi = (Api) Proxy.newProxyInstance(api.getClass().getClassLoader(), api.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.d("test", "invoke");
return method.invoke(api, args);//调用被代理对象方法
}
});
//调用代理对象
proxyApi.setName("yang");
String name=proxyApi.getName();
Log.d("test", "getName=" + name);
Proxy.newProxyInstance 接口
是JDK提供生成动态代理对象的api
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
参数解析:
- ClassLoader :类加载器,传入被代理对象的类加载器即可,例子2中 被代理对象为Api,所以传入api.getClass().getClassLoader();
- interfaces,代理对象需要实现接口,例子2中,代理对象需要实现Api接口,所以传入api.getClass().getInterfaces(),也可以传入new Class[]{Api.class};
- InvocationHandler,拦截器,当调用方法时,会回调到InvocationHandler的invoke方法,这里可以丰富访问;
InvocationHandler.invoke 回调
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{}
参数解析:
- proxy:动态代理对象,例子2中代理对象就是proxyApi ,如果在invoke内部调用了method.invoke(proxy,args),将会出现死循环,最后抛出异常;
- method:被调用方法的Method对象,调用setName,则Method就是setName方法的封装;
- args:被调用方法的参数,调用setName,则args就是new Object[]{name};
代理对象生成在哪呢
代理对象是在运行时生成的,我也没找到他具体生成的地方,以下代码是查看网上的博客得到的
public final class $Proxy0 extends Proxy implements Api { //1
static {//2
try {
m1 = Class.forName("com.yang.myapplication6.Api").getMethod("setName");
m2 = Class.forName("com.yang.myapplication6.Api").getMethod("getName");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
private static Method m1;
private static Method m2;
public $Proxy0(InvocationHandler var1) throws {//3
super(var1);
}
public final void setName(String name) {//4
try {
super.h.invoke(this, m1, new Object[]{name});
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
@Override
public String getName() {
try {
return (String) super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
- 注释1:$Proxy0是代理对象的类型,它继承Proxy以及实现Api接口;
- 注释2:静态代码块中,反射获取Api接口定义的方法Method;
- 注释3:构建对象,需要传入InvocationHandler对象;
- 注释4:setName方法的具体实现,会调用super.h.invoke(this, m1, new Object[]{name}),这个h就是在生成动态代理对象传入的InvocationHandler,所以当在外部通过代理对象调用相应方法时候,会回调到InvocationHandler.invoke;
类图
![](https://img.haomeiwen.com/i20808175/688937a9f9653466.png)
动态代理使用场景
动态代理,适用于对所有代理方法做统一处理的场景;
动态代理可以没有实现,实现类由虚拟机生成;
总结
- 动态代理可以丰富被代理对象的行为;
- 动态代理对象是动态生成的;
以上分析有不对的地方,请指出,互相学习,谢谢哦!
网友评论