介绍
Proxy对象的生成,使用了JDK的Api,动态的在内存中构建代理对象(需要指定接口)
结构
- 接口:通过接口或抽象类声明业务方法。
- 目标类:实现目标接口,实现具体业务
- 代理(Proxy)类:动态生成代理对象,不需要创建代理类
优点
为解决静态代理中,代理类必须实现接口的问题,Java给出了动态代理,动态代理具有如下特点:
Proxy对象不需要实现接口
缺点
目标类必须实现接口
生成代理对象的api
Proxy
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
参数说明:
ClassLoader loader:指定当前目标对象使用类加载器。
方法固定:target.getClass().getClassLoader()Class<?>[] interfaces:目标对象实现的接口
如果传入接口类对象,代码是:target.getClass().getInterfaces()
如果传入接口,代码是:new Class[]{接口.class}。如:mybatis生成mapper接口(dao接口)对象原理InvocationHandler h:调用目标对象的方法时,其实质是调用该方法。传入目标对象方法的参数,也会传入到该方法中
返回值: 返回代理对象
InvocationHandler
InvocationHandler.invoke(Object proxy, Method method, Object[] args)
参数说明:
Object proxy:jdk动态生成的代理类对象Method method:当前执行的方法Object[] args:当前执行的方法的参数
返回值: 与 目标对象的方法返回值相同
例子
定义 接口类
定义 演员类
定义导演类(主启动类)
public class Director {
public static void main(String[] args) {
IActor actor=new Actor();
/*
* 动态实现代理
* 注意:只能强转成 IActor 接口,不能强转成 Actor 类
*/
IActor agent=(IActor) Proxy.newProxyInstance(
actor.getClass().getClassLoader(),
actor.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//取出参数
Integer money=(Integer) args[0];
Integer days=(Integer) args[1];
//代理类可以在目标对象的方法之前,增加额外的代码
if( money/days<1){
System.out.println("Agent==每天酬劳低于1万,不合作!");
return false;
}
System.out.println(String.format("Agent==经纪人扣30%%劳务费:%s,实付70%%给演员",money*3/10));
//代理类可以修改传入目标对象的参数,返回值
args[0]=money*7/10;
//调用目标类对象的方法
Object res = method.invoke(actor, args);
//代理类可以在目标对象的方法之后,增加额外的代码
System.out.println("Agent==合作愉快,期待下次合作!");
return true;
}
}
);
int money=350;
// int money=280;
int days=300;
System.out.println(String.format("Director==谈合作:%s万,%s天,可以吗?",money,days));
boolean b=agent.deal(money,days);
if(b) {
System.out.println("Director==开拍");
}else{
System.out.println("Director==演员没谈成,不能开拍");
}
}
}
注意: IActor agent=(IActor)new Agent(actor).getProxyInstance(); 只能强转成 IActor 接口,不能强转成 Actor 类
这就是为什么在java中,很多地方都说:面向接口编程的原因