一、代理概念
1.1 代理是个什么东西
- 代理,顾名思义,就是有两个对象A和B,通过A来访问B,就说A代理了B 或者 B被A代理
- 体现在java中,就是有两个类:类Proxy和类Class,Proxy代理类Class,就是通过Proxy的对象来访问Class的对象
- 那么怎么体现Proxy和Class的关系呢,就是他们都实现同一个接口Interface
- 虽然如此,但是Class实现Interface的方式是自己干活,而Proxy实现Interface的方式是喊Class干活
- 比如有一个明星叫美丽,她的经纪人叫老王,他们都实现一个接口:明星,明星的定义是要会唱歌跳舞
- 你是一个活动的组织人(Main),想找个明星来参加活动,想到了明星的实现:美丽
- 但是你不能直接找美丽,你要联系她的经纪人,即代理:老王
- 你告诉老王,活动上要唱歌跳舞,那是老王来唱歌跳舞吗?不是,老王实际去喊美丽来唱歌跳舞
public interface Star {
public void Sing();
public void Dance();
}
public class RealStar implements Star {
private String name = "";
public RealStar(String name) {
this.name = name;
}
@Override
public void Sing() {
System.out.println(this.name + "在唱歌");
}
@Override
public void Dance() {
System.out.println(this.name + "在跳舞");
}
}
public class Laowang implements Star {
private Star star = null;
public Laowang(Star star) {
this.star = star;
}
@Override
public void Sing() {
star.Sing();
}
@Override
public void Dance() {
star.Dance();
}
}
public class Party {
public static void main(String[] args) {
Star Meili = new RealStar("美丽");
Laowang laowang = new Laowang(Meili);
laowang.Sing();
laowang.Dance();
}
}
美丽在唱歌
美丽在跳舞
- 经过例子,应该对代理有了一个基本的了解,可以明确代理的相关理论了
1.2 概念
- 代理的定义:
- Provide a surrogate or placeholder for another object to control access to it
- 为其他对象提供一种代理以控制对这个对象的访问
- 代理的三要素:
- Subject 抽象主体角色
- 可以是抽象类 或 接口,是一个最普通的业务类型定义,无特殊要求
- RealSubject 具体主体角色
- 也叫做被委托角色、被代理角色,是业务逻辑的具体执行者
- Proxy 代理主体角色
- 也叫做委托类、代理类,它负责对真实角色的应用,把所有抽象主体类定义的方法限制委托给真实主体角色实现,并且对其增强。
1.3 增强
- 关于增强这个要点上例并未体现,而这一点很重要,通过代理类的自定义方法实现
- 因为美丽是个明星,她只要专心提升唱歌跳舞的技能就行了,其他琐事交给经纪人做
- 比如收钱,老王作为经纪人要替手下的明星收钱吧,所以我们修改一下老王的类
public class Laowang implements Star {
private Star star = null;
public Laowang(Star star) {
this.star = star;
}
public void Money() {
System.out.println("老王收钱");
}
@Override
public void Sing() {
Money();
star.Sing();
}
@Override
public void Dance() {
Money();
star.Dance();
}
}
老王收钱
美丽在唱歌
老王收钱
美丽在跳舞
二、代理的分类
- 代理总共分为两大类
- 静态代理:需要自己写代理类,又分为两小类
- 动态代理:不需要自己写代理类
2.1 静态代理
- 像第一节中那样既知道实现类又知道代理类的情况,其实是不多见的
- 普通代理:只知代理类,不应知实现类;通过代理类访问实现类
- 强制代理:只知实现类,不知代理类;但还是通过代理类访问实现类
2.1.1 普通代理
- 对外部隐藏实现类,只通过代理类来达成需求,不能有直接访问实现类的可能
- 比如你知道美丽,于是跳过老王私下联系美丽来参加活动,那老王肯定不高兴了啊,必须对外隐藏
- 隐藏的方式就是修改构造方法
public class RealStar implements Star {
private String name = "";
public RealStar(Star star, String name) throws Exception {
if (star == null) {
throw new Exception(name + "不接私活的昂");
} else {
this.name = name;
}
}
}
public class Laowang implements Star {
private Star star = null;
public Laowang(String name) {
try {
star = new RealStar(this, name);
} catch(Exception e) {
e.printStackTrace();
}
}
}
public class Party {
public static void main(String[] args) {
Laowang laowang = new Laowang("美丽");
laowang.Sing();
laowang.Dance();
}
}
老王收钱
美丽在唱歌
老王收钱
美丽在跳舞
2.1.2 强制代理
- 强制代理与普通代理的前提恰恰相反:只知实现类,不知代理类
- 但目的是一样的:通过代理访问实现类
- 你要搞活动啦,想找美丽来表演,于是你直接给美丽打电话,美丽说不行啊你得找我经纪人,然后把老王的联系方式给你了
- 实现方式:实现类返回代理
public interface Star {
public void Sing();
public void Dance();
public Star getProxy();
}
public class RealStar implements Star {
private String name = "";
private Star proxy = null;
public RealStar(String name) {
this.name = name;
}
@Override
public Star getProxy() {
proxy = new Laowang(this);
return proxy;
}
@Override
public void Sing() {
if(this.isProxy()) {
System.out.println(this.name + "在唱歌");
} else {
System.out.println("请找老王");
}
}
@Override
public void Dance() {
if(this.isProxy()) {
System.out.println(this.name + "在跳舞");
} else {
System.out.println("请找老王");
}
}
private boolean isProxy() {
if (this.proxy == null) {
return false;
}
return true;
}
}
public class Laowang implements Star {
private Star star = null;
public Laowang(Star star) {
this.star = star;
}
@Override
public Star getProxy() {
return this;
}
public void Money() {
System.out.println("老王收钱");
}
@Override
public void Sing() {
Money();
star.Sing();
}
@Override
public void Dance() {
Money();
star.Dance();
}
}
public class Party {
public static void main(String[] args) {
Star meili = new RealStar("美丽");
Laowang laowang = (Laowang)meili.getProxy();
laowang.Sing();
laowang.Dance();
}
}
2.2 动态代理
- 很多教程都会说动态代理和静态代理的区别是:静态代理要自己手写代理类,而动态代理不用
- 实际上,是的。但动态代理要手写一个实现了InvocationHandler的类,以完成代理功能
- 所以个人认为,动态代理不用手写代理类这个区别,感觉像是一个文字游戏一样
- 严格来讲,应该是动态代理不用手写实现三要素中的Subject的代理类而已
- anyway,不论是手写Subject的代理类,还是手写InvocationHandler的实现类,总是代理功能的逻辑都是由编写代码的我们来决定和完成的
- 还是明星、美丽、老王,我们使用动态代理的方式来完成
public interface Star {
public void Sing();
public void Dance();
}
public class RealStar implements Star {
private String name = "";
public RealStar(String name) {
this.name = name;
}
@Override
public void Sing() {
System.out.println(this.name + "在唱歌");
}
@Override
public void Dance() {
System.out.println(this.name + "在跳舞");
}
}
public class Laowang implements InvocationHandler {
Object obj = null;
public Laowang(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(this.obj, args);
return null;
}
}
public class Party {
public static void main(String[] args) {
Star meili = new RealStar("美丽");
InvocationHandler handler = new Laowang(meili);
Star proxy = (Star)Proxy.newProxyInstance(meili.getClass().getClassLoader(),
meili.getClass().getInterfaces(),
handler);
proxy.Sing();
proxy.Dance();
}
}
- 这个程序是比较难理解的,我们一个类一个类来分析
- Star这个interface,是三要素中的Subject,定义最基本的功能,没什么好说的
- RealStar这个实现类,是三要素中的RealSubject,是具体执行逻辑的那个角色
- Laowang这个类,要详细说一下了
- 从文字游戏的角度来看,他确实是没有实现Star类,只是实现了InvocationHandler,所以可以说动态代理并没有手写代理类
- 那么他是怎么实现代理功能的呢
- 1、他实现了InvocationHandler,而InvocationHandler是JDK提供的动态代理接口,所以Laowang等于声明自己也是一个handler(我称呼他为增强处理器),所以后面可以作为参数传入Proxy的方法中去创建一个动态代理对象
- 2、老王的构造方法,此处我们将其参数定义为Object,意味着创建老王的时候,传入什么对象,老王就代理什么对象。明白了吗,这个时候老王已经不仅仅是代理美丽或者说代理明星了,他可以代理任何东西,狗、猫、树都可以。
- 如果把Object改成Star类型,那么老王就只能代理明星
- 3、老王里的invoke方法,这是动态代理的核心。类似于拦截器一样的功能,不论你在main里面调用代理的任何方法,最终都是去执行这个invoke方法。invoke的三个参数后面会详细解释。
- 例子中的invoke很简单,你要调用什么功能,invoke就去调用同样的功能,不做任何增强。
- 4、总结下来就是,通过构造方法决定老王代理什么东西,通过invoke方法决定如何增强
- Party这个类,总共分为4步
- 第一步,创建被代理对象,就是指定我们要美丽这个明星来表演
- 第二步,创建一个handler,把美丽传进去表示老王你要代理美丽
- 第三步,动态创建一个代理对象proxy,通过Proxy类的静态方法newProxyInstance()实现,这里三个参数后面也会详细解释,老王带着美丽到场了!
- 第四步,通过代理对象,完成功能,唱歌啊跳舞啊
- 所以,个人认为,动态代理只是把静态代理里的代理类,换成了一个增强处理器而已。
- 不论怎样,作为开发者,都要写满三要素才能实现代理功能
2.2.1 invoke()方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
- 增强处理器的invoke方法,可以理解为一个强大的拦截器
- 无论外部如何调用接口内的方法,最终都是进入invoke方法里执行逻辑
- 三个参数:
- Object proxy,对我们来说没什么实际意义,可以先忽略
- Method method,就是外部调用的具体的方法了,调Sing()这里就是Sing(),调Dance()这里就是Dance()
- Object[] args,很好理解,外部调用方法时传入的参数(参数很可能不止一个,所以是个数组)
- 例子中的invoke(),没有增强只是原封不动的转调实现类方法,可以修改一下
public class Laowang implements InvocationHandler {
public void Money(int money) {
System.out.println("老王收钱:" + money + "元");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("Sing")) {
Money(10);
method.invoke(this.obj, args);
}
if(method.getName().equals("Dance")) {
Money(20);
method.invoke(this.obj, args);
}
return null;
}
}
老王收钱:10元
美丽在唱歌
老王收钱:20元
美丽在跳舞
2.2.2 Proxy.newProxyInstance()方法
public static Object newProxyInstance(Classloader loader,
@NotNull Class<?>[] interfaces,
@NotNull reflect.InvocationHandler h)
throws IllegalArgumentException
- 参数1:生成代理对象的类装载器(一般使用被代理对象的类的类装载器)
- 参数2:被代理对象们,通过接口数组指定(即被代理对象的接口们,例中是Star)
- 参数3:增强处理器(传入InvocationHandler的实现类的对象,例中是Laowang)
- 最后明确几个概念:
- 通过Proxy.newProxyInstance()生成的动态处理器对象,会默认你全部实现了参数2中传入所有接口中的所有方法(它单方面对外宣布,这些我全都在增强处理器的invoke()中重写啦!)
- 所以不论该对象在main中调用参数2的接口们中的任何方法,动态处理器都会回到参数3增强处理器里去执行invoke()方法
- 实现动态代理必须有接口,没有接口无法实现动态代理
- 所以有cglib代理(子类代理),不需要接口实现代理,就是另一个回事了
2.2.3 匿名内部类
public interface Star {
public void Sing();
public void Dance();
}
public class RealStar implements Star {
private String name = "";
public RealStar(String name) {
this.name = name;
}
@Override
public void Sing() {
System.out.println(this.name + "在唱歌");
}
@Override
public void Dance() {
System.out.println(this.name + "在跳舞");
}
}
public class Party {
public static void main(String[] args) {
Star meili = new RealStar("美丽");
Star laowang = (Star)Proxy.newProxyInstance(meili.getClass().getClassLoader(),
meili.getClass().getInterfaces(),
(proxy, method, arg1) -> {
System.out.println("老王收钱");
method.invoke(meili, arg1);
return null;
});
laowang.Sing();
laowang.Dance();
}
}