目录
一. 什么是动态代理
- 假设Student里面有一个吃饭的eat方法,那么现在我要给这个方法去增加其他的功能,比如说吃饭之前,要去拿筷子,要去盛饭,按照以前所学,我们只能把这两段代码都写在eat方法当中,那此时直接去修改代码,是叫做侵入式修改。
- 在以后在一个成熟的项目当中,我们一般很少会这么去干。
- 那么问题来了,我现在又不能去修改原有的代码,又要去增加额外的功能,那这个时候我该怎么办呢?
- 此时我们就得去找一个代理,什么是代理,说白了就是中介公司。
- 代理会帮你先去做拿筷子和盛饭这两个准备工作,等真正的吃饭了再去调用Student里面的方法去吃饭,这个呢其实就是动态代理。
1.为什么需要代理呢?
- 代理可以无侵入式的给对象增强其他的功能。
- 当调用者去调用对象中的方法的时候,其实呢不是直接通过对象去调用,而是先去调用代理中的方法,代理会做一些准备工作,然后呢再由代理去调用对象中的方法,是有这样的一个过程
二. 程序为什么需要代理?代理长什么样?
程序为什么需要代理?
- 说白了就是因为如果对象任务自己身上干的事情太多了,就可以通过代理转义部分的职责。
那么中介派出的这个代理它是长什么样子的呢?
- 代理里面就是对象要被代理的所有的方法
- 在代码当中代理长什么样子,说白了就是代理的里面可以写什么样的方法。
- 代理长什么样子,其实是跟对象差不多的。
- 对象要有什么方法想要被代理,那么代理呢也要有对应的方法,只不过方法里面干的事情是不太一样的,代理它会先把准备工作做完,然后再去调用对象中的方法。
- 代理是通过接口去知道对象想要代理的方法,在这个接口里面所有的方法就是我们想要代理的方法。
- 左边的代理对象和右边的目标对象都要去实现中间的这个接口才是可以的!
- 为什么需要代理?
- 代理可以无侵入式的给对象增强其他的功能。
- 代理长什么样?
- 代理里面就是对象要被代理的所有的方法。
- Java通过什么来保证代理的样子?
- Java通过接口保证代理的样子,目标对象和代理对象需要实现同一个接口,接口中就是被代理的所有的方法。
三. 如何为Java对象创建一个代理对象
在Java中,动态代理是通过Proxy.newProxyInstance()方法来实现的,它需要传入被动态代理的接口。
Java是不支持多继承的,Java只能单继承,JDK动态代理会在程序运行期间动态生成一个代理类$Proxy(),这个动态生成的代理类会继承java.lang.reflect.Proxy类,这也是动态代理的实现规范,所以就导致JDK里面的动态代理只能代理接口,而不能代理实现类。
目标对象:
package com.gch.d10_dynamic_proxy;
/**
定义大明星类:模拟对象
*/
public class BigStar implements Star{
private String name;
public BigStar() {
}
public BigStar(String name) {
this.name = name;
}
/**
* 唱歌
* @param name:歌曲的名字
* @return:"谢谢"
*/
@Override
public String sing(String name){
System.out.println(this.name + "正在唱" + name);
return "谢谢";
}
/**
跳舞
*/
@Override
public void dance(){
System.out.println(this.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 + "}";
}
}
动态代理的接口:
package com.gch.d10_dynamic_proxy;
/**
接口:接口当中定义的是对象所有想要被代理的方法(抽象方法)
我们可以把所有想要被代理的方法定义在接口当中
*/
public interface Star {
/**
* 唱歌
* @param name:歌曲名
*/
public abstract String sing(String name);
/**
跳舞
*/
public abstract void dance();
}
代理类:作用:给目标对象生成代理对象
package com.gch.d10_dynamic_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理类
* 类的作用:创建一个代理
*/
public class ProxyUtil {
/**
* 方法的作用:给一个对象,去创建一个代理对象
* 方法的返回值类型直接写接口的类型,因为我们的代理也是要去实现接口的
* @param bigstar:形参:被代理的明星对象
* @return 返回值:给明星创建的代理对象
*/
public static Star createProxy(BigStar bigstar){
/**
* java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:
* public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
* 参数一:用于指定用哪个类加载器,去加载生成的代理类 是类加载器把类的字节码文件加载到内存当中的
* 参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
* 参数三:用来指定生成的代理对象要干什么事情
*/
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(), // 参数一:用于指定用哪个类加载器,去加载生成的代理类
new Class[]{Star.class} // 参数二:指定接口,这个就表示我们生成的这个代理,也就是中介,它可以代理Star这个接口里面所有的方法
, new InvocationHandler() { // 参数三:用来指定生成的代理对象要干什么事情
@Override
/**
* 用来指定生成的代理对象要干什么事情 invoke:调用
* 参数一:表示代理的对象,重点看参数二和参数三
* 参数二:要运行的方法 sing,dance...
* 参数三:调用sing方法时,传递的实参
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("sing".equals(method.getName())){
System.out.println("准备话筒,收钱");
}else if("dance".equals(method.getName())){
System.out.println("准备场地,收钱");
}
// 代理去找大明星开始唱歌或者跳舞
// 代码的表现形式:调用大明星里面唱歌或者跳舞的方法
return method.invoke(bigstar,args);
}
});
return star;
}
}
package com.gch.d10_dynamic_proxy;
/**
需求:
外面的人想要大明星唱一首歌
1.获取代理对象
代理对象 = ProxyUtil.createProxy(大明星的对象);
2.再调用代理的唱歌方法
代理对象.唱歌的方法("只因你太美");
*/
public class Test {
public static void main(String[] args) {
// 1.创建对象
BigStar bigStar = new BigStar("鸡哥");
// 2.获取代理的对象 proxy:代理
Star proxy = ProxyUtil.createProxy(bigStar);
// 3.调用唱歌的方法
String result = proxy.sing("只因你太美");
System.out.println(result);
// 4.调用跳舞的方法
proxy.dance();
}
}