一、什么是动态代理
假设在Student类中有一个吃饭方法,当我需要给这个方法添加功能时,只能直接修改源代码(侵入式修改),但是在成熟的项目中,我们一般不会去这么干,可能导致更大的问题。
那如何在不修改原有代码的基础上增加功能呢?
此时我们就需要代理,由代理去完成新增的功能。
(特点:无侵入式的给代码增加额外的功能)
程序为什么需要代理?
只因哥是一个大明星,能唱能跳,在代码中反应为有唱歌方法个跳舞方法。
但是只因哥不是一个随便的人,在真正开始唱歌,跳舞之前需要进行准备工作
而只因哥作为一个大明星,这些准备工作不应该让他自己来完成(不应该在原有代码中新增功能)
这时候就需要一个中介公司,而公司会派出一个经纪人(代理),经纪人会帮助只因哥完成这些琐事。
简单来说,如果一个对象的成员方法中做的事情太多,就可以通过代理来转移部分职责。
代理长什么样?
当一个人来邀请只因哥唱歌跳舞的时候,只因哥会说去找他的代理
所以在代理人中也需要实现唱歌和跳舞方法。
如果想要代理只因哥,那代理人也必须实现唱歌跳舞方法,但是代理人不一定会唱歌跳舞。
代理人在干完琐事之后可以调用只因哥的唱歌跳舞方法。
所以对象有什么方法想被代理,代理就一定有对应的方法。
代理怎么知道?
代理人是怎么知道只因哥需要代理唱歌跳舞,而不需要代理打篮球呢
这时候就需要通过接口,在接口中有唱歌跳舞方法。
然后只因哥和代理人都去实现这个接口。
二、动态代理的代码实现
如何获取代理对象
"newProxyInstance(ClassLoader loader, Class<?>[] interface, InvocationHandler h)"
动态代理的代码实现
测试类
package com.qcqc.javaStudy;
public class dynamicProxyDemo1 {
public static void main(String[] args) {
BigStar star = new BigStar("只因哥");
Star starProxy = ProxyUtil.createProxy(star);
System.out.println(starProxy.sing("只因你太美"));
starProxy.dance();
}
}
BigStar
package com.qcqc.javaStudy;
public class BigStar implements Star{
private String name;
//唱歌
@Override
public String sing(String name) {
System.out.println(this.name + "正在唱" + name);
return "谢谢";
}
//跳舞
@Override
public void dance() {
System.out.println(this.name + "正在跳舞");
}
public BigStar() {
}
public BigStar(String name) {
this.name = name;
}
/**
* 获取
*
* @return name
*/ public String getName() {
return name;
}
/**
* 设置
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
public String toString() {
return "BigStar{name = " + name + "}";
}
}
ProxyUtil
package com.qcqc.javaStudy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/*
* 类的作用:创建一个代理
* */
public class ProxyUtil {
/*
* 作用:给明星的对象创建代理
*
* 形参:需要代理的对象
*
* 返回值:给明星创建的代理
* 可以直接用接口名,因为需要实现这个接口
* * 需求:外面的人想要大明星唱歌。
* 1.获取代理的对象
* 代理对象 = ProxyUtil.createProxy(大明星的对象);
* 2.再调用代理的方法
* 代理对象.唱歌方法("只因你太美");
* */
public static Star createProxy(BigStar bigStar){
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),//类的加载器,固定格式。
new Class[]{Star.class},//接口类的数组,表示需要代理的方法,可以多个接口,但是要保证实现类也实现这些接口
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//代理需要干什么事
/*
* 第一个参数:代理的对象
* 第二个参数:要运行的方法,假如外界调用sing方法,则这里的method就是sing
* 第三个参数:调用sing方法时调用的实参
*
* */
//准备工作
if ("sing".equals(method.getName())) {
System.out.println("准备话筒");
} else if ("dance".equals(method.getName())) {
System.out.println("准备场地");
}
return method.invoke(bigStar,args);
}
}
);
return star;
}
}
Star
package com.qcqc.javaStudy;
public interface Star {
//写需要被代理的方法
// 1.唱歌
public abstract String sing(String name);
// 2.跳舞
public abstract void dance();
}
测试结果
三、代码的运行过程
①在测试类中首先创建大明星的对象,然后将这个对象传递给createProxy方法。
②在ProxyUtil中产生了大明星的代理对象。
③当在测试类中调用sing方法,会自动调用匿名内部类中的invoke方法,其有三个参数
第一个参数表示代理对象,第二个参数就是调用的成员方法的对象,第三个参数是调用时的参数
④在invoke方法中执行完其他的代码后,返回值为method调用invoke的执行返回结果。