金莲想happy
package proxy;
public class Jinlian implements IHappy{
@Override
public void happy() {
System.out.println("金莲正在happy...");
}
}
阎婆惜也想happy
package proxy;
class Yanpoxi implements IHappy{
@Override
public void happy() {
System.out.println("阎婆惜正在happy...");
}
}
她们俩有共同的接口,定义了一个happy方法,分别实现
package proxy;
public interface IHappy {
void happy();
}
但是她们只想happy,不想打扫房间,退房
那怎么办?找别人帮忙打扫,退房不就完事了么
找王婆好了
import org.junit.Test;
import proxy.IHappy;
import proxy.Jinlian;
import proxy.MyInvocationHandler;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Client {
/**
* 实现类完成动态代理
*/
@Test
public void test01() {
//1.创建委托者对象
Jinlian jinlian = new Jinlian();
//2.获取委托者对象的字节码文件对象
Class clazz = jinlian.getClass();
//3.使用jdk的动态代理,创建代理者的对象
//方法的三个参数分别是:
//3.1. classLoader类加载器
//3.2. 要代理的接口的字节码对象数组
//3.3. InvocationHandler的实现类对象,真正定义代理的细节在这个实现类中定义,我们可以使用匿名内部类
// 也可以使用实现类实现invocationHandler接口,传入委托者对象
//代理对象的类型应该是被代理的接口类型拿,到了代理对象,需要强转为接口类型。
IHappy proxyInstance =
(IHappy) Proxy.newProxyInstance(clazz.getClassLoader(),
clazz.getInterfaces(),
new MyInvocationHandler(jinlian));
//我们的目标是:代理金莲的happy方法
//我们调用的应该是代理者的happy()方法
proxyInstance.happy();
}
/**
* 匿名内部类完成动态代理
*/
@Test
public void test02(){
//1.获取委托者对象
Jinlian jinlian = new Jinlian();
//2.拿到委托者对象的字节码文件对象
Class clazz = jinlian.getClass();
/*3.Proxy的newProxyInstance方法
参数1.委托者对象类加载器
参数2.委托者对象字节码数组对象
参数3.匿名内部类invocationHandler
*/
IHappy proxyInstance = (IHappy) Proxy.newProxyInstance(
clazz.getClassLoader(),
clazz.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//4.1用method.getName()判断方法名字是否是需要的happy方法
if(method.getName().equals("happy")){
//5.1前置加强
System.out.println("王婆帮忙开好房间");
//5.2代理者调用委托者原本方法
method.invoke(jinlian,args);
//5.3后置加强
System.out.println("王婆帮忙打扫房间退房");
return null;
}else{
return method.invoke(jinlian,args);
}
}
});
//上面只是拿到了加强以后的代理对象,
//还需要调用happy方法告诉加强的代理对象该执行任务并加强了
proxyInstance.happy();
}
}
自定义的增强类
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler{
//4.然后invocationHandler的实现类有一个成员变量,也就是调用者(委托者)
// 成员变量为接口类型,由构造方法传入调用者的接口也可以说是父类对象women
// 因为不确定是哪种类型,但知道都继承了接口IHappy,所以用多态的方式接收委托者
private IHappy women;
//构造方法,用于接收委托者对象,传入的是jinlian,拿到的是IHappy接口的women对象
public MyInvocationHandler(IHappy women) {
this.women = women;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*1. invoke方法什么时候执行: 代理者调用任何方法都会执行,所以我们在invoke方法中编写具体的代理规则
2. invoke方法的三个参数是什么意思:
2.1.Object proxy,也就是拿到的代理者对象本身,不要去使用他,会发生递归
2.2. Method method,拿到的代理者对象将要调用的委托者的方法,
2.3. args 表示代理对象调用委托者的方法时候传入的参数
3.此时我们要对happy()方法进行代理,其它方法不代理(调用委托者原本的方法)
3.1 判断该方法是不是happy()方法
method.getName()拿到代理者要代理的委托者的方法的名字,判断与happry是否相同
因为我们这里只增强happy方法,而传进来的委托者可能有多个方法。
*/
if (method.getName().equals("happy")) {
//如果是happy方法则进行增强
//3.2前置增强
System.out.println("开好房间准备happy...");
//3.3调用委托者的该方法
method.invoke(women,args);
//3.4后置增强
System.out.println("打扫房间...");
//return null表示方法结束,不会执行后面的代码。因为已经执行了method.invoke(women,args);所以这里不需要返回任何东西
return null;
}
//方法不需要被代理,那么就执行委托者原本的方法
return method.invoke(women,args);
//可以把method.invoke(women,args)简单的理解为直接调用该方法。参数和普通调用一样。
}
}
解析
一.实现对象
首先创建委托者的class对象
Class clazz = jinlian.getClass();
然后用Proxy类的newProxyInstance方法
传入三个参数
- 类加载器ClassLoader loader,
clazz.getClassLoader() - 类实现?Class<?>[] interfaces,
clazz.getInterfaces() - 匿名内部类InvocationHandler h
或实现类
new MyInvocationHandler(jinlian)
强转为委托人和代理人共同的接口类型
IHappy proxyInstance = (IHappy)Proxy.newProxyInstance(参数1,参数2,参数3);
proxyInstance 代理对象执行方法。
二.实现类
上述 参数3-invocationHandler的实现类有一个成员变量,也就是调用者(委托者)
接口类型,由构造方法传入调用者的接口也可以说是父类对象women
,因为不确定是哪种类型,但知道都继承了接口IHappy,所以用多态的方式接收委托者
private IHappy women;
//构造方法
public MyInvocationHandler(IHappy women) {
this.women = women;
//invocationHandler接口的默认方法invoke(参数1,参数2,参数3)
public Object invoke(Object proxy, Method method, Object[] args) {}
//1. invoke方法什么时候执行: 代理者调用任何方法都会执行,所以我们在invoke方法中编写具体的代理规则
参数1 Object proxy,也就是拿到的代理者对象本身,不要去使用他,会发生递归
参数2 Method method,拿到的代理者对象将要调用的委托者的方法,
参数3 args 表示代理对象调用委托者的方法时候传入的参数
//method.getName()拿到代理者要代理的委托者的方法的名字,判断与happry是否相同
因为我们这里只增强happy方法,而传进来的委托者可能有多个方法。
if (method.getName().equals("happy")) {
//如果是happy方法则进行增强
//前置工作
System.out.println("开好房间准备happy...");
//调用委托者的该方法,传入参数,
1,委托者对象,决定要调用哪个委托者的方法
2.调用该方法时需要的参数列表,这些都在代理的时候通过反射拿到了。
method.invoke(women,args);
//后置工作
System.out.println("打扫房间...");
return null;
}
//方法不需要被代理,那么就执行委托者原本的方法
return method.invoke(women,args);
}
可以把method.invoke(women,args)简单的理解为直接调用该方法。参数和普通调用一样。
或匿名内部类里有