Java静态代理和动态代理
什么是代理?
代理是一种设计模式。通过代理对象间接的反问被代理对象。(是的就是这么简单)
怎么理解呢?
举个例子:
小明*(被代理对象)*去详情了,想约女孩出来看电影(代理事务),但是不想自己去找女孩,这时候找到媒婆(代理对象)去传话,达到约会的目的。
我们通过上图想一想,代理的好处和作用。
- 为什么小明不自己去找小美说呢? ---->
加入小明是个丑八怪,那直接去找小美,那不是凉凉。找媒婆的话就能直接把话带到且让自己相貌啥的不直接被小美知道。
同理:通过代理对象实现代理事务,大大的保障了被代理对象信息安全。(484很容易理解了) - 通过媒婆告诉小美有啥好处呢?
你想想媒婆那嘴多能说,见到小美不得给小明先夸一波啥对吧,让小明更有魅力了。
同理:代理对象在不入侵被代理对象的前提下,对其进行方法增强。 - 代理对象为什么不直接实现代理事务,还要代理对象干嘛?
小明通过媒婆去找小美约会,最终还是小明跟小美约会。
同理:代理对象实现代理事务最终还是调用的代理对象的事务。
通过上述生动的讲述,是不是很容易理解了什么是代理了?为什么需要代理了?下面通过代码层面彻底分析和了解下代理。
Java代理分为静态代理和动态代理
两者有什么区别呢?我们先介绍完,再去分析(介绍完就很容易理解了)。
静态代理
通过上面的了解我们可以知道,想要实现代理是不是得满足几个条件:
- 对象:代理对象(媒婆)、被代理对象(小明)、代理事务(请小美去看电影这件事)
- 代理对象需要实现被代理对象的功能,所以代理对象需要实现跟被代理对象一样的方法。(媒婆找小美说话,说的就是请小美去看电影这件事,这件事和小明想说的是一样的)
- 代理对象需要集成被代理对象的继承类。(就是说被代理对象集成了AA,被代理对象实现时也需要集成AA,这个很好理解)
- 代理对象时间的代理事务本质上就是使用被代理对象的事务,则需要初始化被代理对象。(媒婆找到小美说去看电影,最终去的还是小明,实现事务的还是小明)
有了几点我们开始代码层面实现静态代理:
public interface SayToWatchTv {
public void sayToWatchTv();
}
被代理对象小明:跟小美说去看电影。
/**
* 代理对象-- 小明 on 2021/3/23.
*/
public class NoProxyClass implements SayToWatchTv{
public void sayToWatchTv() {
System.out.println("小美我们去看电影吧");
}
}
代理对象媒婆,需要继承和小明一样的接口,且实现代理事物,但是最终还是调用的小明对象的实现方法
/**
* Created by on 2021/3/23.
* 代理对象媒婆
*/
public class ProxyClass implements SayToWatchTv{
NoProxyClass noProxyClass = new NoProxyClass(); // 初始化被代理对象
public void sayToWatchTv() {
noProxyClass.sayToWatchTv(); // 最终还是调用了被代理对象的方法
}
}
public class MainClass {
public static void main(String[] args) {
ProxyClass proxyClass =new ProxyClass();
proxyClass.sayToWatchTv();
}
}
结果:
看到这是不是感觉静态代理tm也太简单了吧,神经病吧,为啥我不直接调用被代理对象的方法哦。还绕这个弯路。
那现在对照着代理的优点我们分析下:
- 如果有天老板对你说,小明在对小美邀请前,还去理头发了,把这个事务写进去。但是小明类这边你不要动,代码上个员工写的。
— > 这个时候我们是不是可直接在代理对象中去实现是吧。(代理对象在不入侵被代理对象的前提下,对其进行方法增强)
/**
* Created by on 2021/3/23.
* 代理对象媒婆
*/
public class ProxyClass implements SayToWatchTv{
NoProxyClass noProxyClass = new NoProxyClass();
public void sayToWatchTv() {
System.out.println("小明去理了发");
noProxyClass.sayToWatchTv();
}
}
2. 很容易看出来,main方法里面直接调用的是代理对象,而不是被代理对象。同理:通过代理对象实现代理事务,大大的保障了被代理对象信息安全。
上面就是静态代理。我们设想一下,现在我们代理对象是程序员在知道被代理对象信息之后,手动创建的代理类。那要是系统里面有很多需要带的对象,使用静态代理是不是需要手动创建很多代理对象才行。这样代理冗余太多。太杂了,而且太耗资源了。那么有没有一种方法,可以不用手动创建代理对象,虽然我事先不知道被代理对象,但是我可以通过传参数的形式动态的去创建代理类呢?
答案是肯定的:这就是我们接下来要说的动态代理。
动态代理
至于静态代理和动态代理的区别上面都说过了,也容易理解:动态创建代理类。
那么需要怎样去动态的去实现动态代理内呢?
想一想:
1.需要动态创建代理类,那就需要能动态构造类,那我们可以使用到java的类类加载器ClassLoader。
2.之前说了代理类最终还是使用的被代理对象的方法,但是现在代理对象我们是参数传入的,我们实现不知道需要代理啥类,有什么方法。所以我们需要从参数类中获取这些信息。那我们可以用到java的类反射机制。
被代理接口
public interface SayToWatchTv {
public void sayToWatchTv();
}
被代理对象实现类
/**
* 代理对象-- 小明 on 2021/3/23.
*/
public class NoProxyClass implements SayToWatchTv{
public void sayToWatchTv() {
System.out.println("小美我们去看电影吧");
}
}
代理类
/**
* Created by Administrator on 2021/3/23.
*/
public class ProxyDT implements InvocationHandler{
//这个就是我们要代理的真实对象
SayToWatchTv sayToWatchTv; // 被代理对象接口
//构造方法,给我们要代理的真实对象赋初值
public SayToWatchTv getProxy(SayToWatchTv sayToWatchTv) { // 用于构建代理类
this.sayToWatchTv = sayToWatchTv;
Object proxy = Proxy.newProxyInstance(sayToWatchTv.getClass().getClassLoader(), // 类加载器
sayToWatchTv.getClass().getInterfaces(),// 反射机制获取类信息
this); //被代理对象接口
return (SayToWatchTv) proxy;
}
// 用于出发调用代理事务
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = method.invoke(sayToWatchTv,args);
return res;
}
public class MainClass {
public static void main(String[] args) {
ProxyDT proxyDT = new ProxyDT(); // 初始化代理类
NoProxyClass noProxyClass =new NoProxyClass(); // 需要被代理的类
SayToWatchTv sayToWatchTv = proxyDT.getProxy(noProxyClass);
sayToWatchTv.sayToWatchTv();
}
}
}
结果
动态代理有几个核心点:
1.动态构建代理类。 Proxy.newProxyInstance
2.main方法中调用sayToWatchTv时,就是触发了invoke方法.而该方法的 本质就是调用了被代理的是实现方法。
所以动态代理实现的原理其实就是静态代理的原则,只是运用了类加载器和反射等机制,实现动态创建代理类。
上面说的JDK实现的动态代理。还有中方式是基于CGLIB的动态代理,这个后面再说。