1. 什么是代理:由接口、实现类、以及代理类组成的一种设计模式(我自己的理解,可以看下面的快速入门案例)
2. 为什么要用代理:在不侵入目标方法的前提下增强目标方法的功能,这也符合Java的开闭原则
3. 怎样使用代理:代理分为静态代理和动态代理,我们一个一个的看
4. 首先准备好接口和被代理类以及要用到的公共方法,code
interface SingPlay {
void singPlay();
}
class Singer implements SingPlay {
@Override
public void singPlay() {
System.out.println("演唱...");
}
}
class Common {
public static void giveMoney() {
System.out.println("先给表演费...");
}
}
4. 静态代理,code:
// 静态代理类
class SingPlayProxy implements SingPlay {
private final Singer singer;
public SingPlayProxy(Singer singer) {
this.singer = singer;
}
@Override
public void singPlay() {
Common.giveMoney(); // 我们加入了演唱前先给钱的方法
singer.singPlay();
}
}
// 测试静态代理
Singer singer = new Singer();
SingPlayProxy singPlayProxy = new SingPlayProxy(singer);
singPlayProxy.singPlay();
// 测试结果
先给表演费...
演唱...
5. 上面就是我们静态代理的整个过程。此时我们的需求变了,我想在演唱的同时,加上讲相声的表演,于是我们需要修改我们的接口以及代理类,code
interface SingPlay {
void singPlay();
void play();
}
class Singer implements SingPlay {
@Override
public void singPlay() {
System.out.println("演唱...");
}
@Override
public void play() {
System.out.println("讲相声...");
}
}
class Common {
public static void giveMoney() {
System.out.println("先给表演费...");
}
}
6. 由于我们讲相声的表演也是收费的,所以代理类的方法也需要加入收费的逻辑,code
class SingPlayProxy implements SingPlay {
private final Singer singer;
public SingPlayProxy(Singer singer) {
this.singer = singer;
}
@Override
public void singPlay() {
Common.giveMoney();
singer.singPlay();
}
@Override
public void play() {
Common.giveMoney();
singer.play();
}
}
7. 在以上的基础上,假如我又引入了跳舞、弹琴、敲鼓、朗诵等等一系列的收费方法以后,我们会发现代理类对于 Common.giveMoney() 这个方法的复用性太差了(没有复用)。而且接口新增方法之后,代理类也需要修改,是不是很烦?至此,我们可以引出静态代理的第一个问题:代理类在对同一个接口的多个方法实行统一处理时,代码复用性差。代理类和被代理类之间的代码耦合性强。此时,我们自然而然就想到了动态代理,接下来我们看看动态代理怎么做
8. 对同一接口的不同方法实行统一处理,动态代理,code
// 动态代理代码
Singer singer = new Singer();
SingPlay singPlayProxy = (SingPlay) Proxy.newProxyInstance(
DynamicProxy.class.getClassLoader(), // 第一个参数:一般是动态代理类的类加载器(这个好像写Singer以及SingerPlay都可以..)
Singer.class.getInterfaces(), // 第二个参数:被代理类的接口(这个不能写错)
(proxy, method, args1) -> { // 第三个参数:InvocationHandler接口的lambda表达式
Common.giveMoney(); // 这里写一次就可以了, 也可以根据不同的方法名特殊处理
method.invoke(singer, args1);
return null;
});
singPlayProxy.singPlay();
singPlayProxy.play();
// 测试结果
先给表演费...
演唱...
先给表演费...
讲相声...
9. 可以看到,动态代理 Common.giveMoney() 方法写一次就可以在所有的方法上应用了,实现了代码的复用。对于动态代理的参数什么,以及原理什么的,等下会发一个大佬的链接,这会儿可以先向下接着看。
10. 在第5步的基础上,假如我们的需求又变了,我们需要增加一个跳舞功能的接口和唱歌并列,于是我们要增加我们的代理类以及接口,code
// 唱歌、讲相声
interface SingPlay {
void singPlay();
void play();
}
class Singer implements SingPlay {
@Override
public void singPlay() {
System.out.println("演唱...");
}
@Override
public void play() {
System.out.println("相声...");
}
}
// 跳舞(新加入)
interface DancePlay {
void dancePlay();
}
class Dancer implements DancePlay {
@Override
public void dancePlay() {
System.out.println("跳舞...");
}
}
class Common {
public static void giveMoney() {
System.out.println("先给表演费...");
}
}
11. 我们还需要增加一个支持跳舞表演的代理类,code
// 唱歌、讲相声的代理类
class SingPlayProxy implements SingPlay {
private final Singer singer;
public SingPlayProxy(Singer singer) {
this.singer = singer;
}
@Override
public void singPlay() {
Common.giveMoney();
singer.singPlay();
}
@Override
public void play() {
Common.giveMoney();
singer.play();
}
}
// 跳舞的代理类(新增加)
class DancePlayProxy implements DancePlay {
private final Dancer dancer;
public DancePlayProxy(Dancer dancer) {
this.dancer = dancer;
}
@Override
public void dancePlay() {
Common.giveMoney();
dancer.dancePlay();
}
}
12. 假如我现在还需要很多接口来表示不同的表演呢,那是不是我需要创建很多的代理类,随着代理类的越来越多,项目会越来越臃肿,越来越难维护对不对。至此,我们引出了静态代理的第二个问题:不同的接口会创建不同的代理类,造成代理类很多,代码臃肿。我们又想到了动态代理类对不对,我们一起来看一下它会怎么做
13. 针对不同接口,动态代理,code
// 唱歌、讲相声
Singer singer = new Singer();
SingPlay singPlayProxy = (SingPlay) Proxy.newProxyInstance(
DynamicProxy.class.getClassLoader(),
Singer.class.getInterfaces(),
(proxy, method, args1) -> {
Common.giveMoney(); // 先给钱
method.invoke(singer, args1);
return null;
});
singPlayProxy.singPlay();
singPlayProxy.play();
System.out.println();
// 跳舞
Dancer dancer = new Dancer();
DancePlay dancerPlayProxy = (DancePlay) Proxy.newProxyInstance(
DynamicProxy.class.getClassLoader(),
Dancer.class.getInterfaces(),
(proxy, method, args1) -> {
Common.giveMoney(); // 先给钱
method.invoke(dancer, args1);
return null;
});
dancerPlayProxy.dancePlay();
// 测试结果
先给表演费...
演唱...
先给表演费...
相声...
先给表演费...
跳舞...
14. 上面就是动态代理的例子,和第11步相比是不是舒爽多了,而且我们还可以封装生成动态代理代码的部分,使我们的动态代理代码更加简洁。
15. 除了上面这些区别之外,我们还要在根据名字区分一下两个代理:
- 静态代理:静态,在编译时生成代理类和.class文件
- 动态代理:动态,在运行时生成代理类和.class文件
16. 对于动态代理的运行原理,可以参考这位大佬的博文(我就不按照大佬的思路造轮子了,仰视一下大佬吧,哈哈哈哈~):
https://mp.weixin.qq.com/s/h-hNC45BvTR499kDNg-Vuw
===============================================================================================
上面这些就是自己对java代理的理解,学无止境,可能有些不对的地方,希望大佬们指正。
拜拜~~~