代理是什么
代理,可以通俗理解为中介/中间人,其实非常简单
生活中存在相当多代理的概念,房产中介,股票经纪人,基金经理,商标代理等
Java当中的代理,其内核也是这样的一个概念:
由代理类创建的代理对象,去代理目标类创建的目标对象
以下我们将代理对象简称为proxyObj,将目标对象简称为targetObj
代理的作用
以房屋中介为例,中介作为买房者和房东之间的中间人,是房东的出售/出租房屋的代理
1. 增强功能
没有房屋中介这个代理之前,房东需要自己去寻找租户,在茫茫租房者中,寻找一个觉得价格合适,位置合适,硬件设施合适的租户,非常耗费时间和精力;
有了房屋中介之后,中介有更好的推广渠道,也拥有更专业的沟通技巧;中介可以帮房东做筛选,过滤掉那些谈不拢的租户;中
介可以拟定更为专业的购房合同……
综上,房东找寻租户的能力变强了,这显然归功于房屋中介。
2. 访问控制
没有中介之前,不管什么样的租户都可以直接接触到房东,可能每天都要和几十名潜在租户沟通
现在有了中介,如果租户想要联系房东,必须先经过中介的“校验”,确认你有租房意愿,基本上没什么太大的变动之后,才会作为
中间人,将租户和房东联系起来。
这显然加强了租户对房东的访问限制
当然,代理的功能其实非常强大,还有一些涉及到解耦,懒加载等功能,本文不对此做深入讲解,读者可以再去搜索并了解!
Java中代理的实现方式
静态代理,简单来说就是,targetObj和proxyObj之间1对1,1对多的关系
动态代理,则是1个proxyObj,对应任意数量的targetObj(1对多)
静态代理的1对多,和动态代理的1对多,不是一个概念,我们下面用代码来阐述
不管哪种代理,大道至简,我们需要做的事情都是只有4件:
- 创建targetObj
- 创建proxyObj
- 明确要代理的targetObj的方法
- 执行proxyObj的方法间接调用targetObj的被代理方法
静态代理
静态代理借助Java的接口实现
Fightable接口
// 连接目标类和代理类的桥梁:接口
public interface Fightable {
void fight(Integer power);
}
目标类,实现Fightable接口
// 静态代理目标类
public class Hero implements Fightable {
@Override
public void fight(Integer power) {
System.out.println("英雄战斗力为" + power);
}
}
代理类,也去实现Fightable接口,目的是明确要代理的targetObj的方法
public class HeroProxy implements Fightable {
private Hero hero = new Hero();
@Override
public void fight(Integer power) {
System.out.println("hero被代理……");
power++;
hero.fight(power);
}
}
执行proxyObj的方法间接调用targetObj的被代理方法
public class ToRun {
public static void main(String[] args) {
HeroProxy heroProxy = new HeroProxy();
heroProxy.fight(6);
// hero被代理……
// 英雄战斗力为6
}
}
我们发现hero对象的方法被执行,同时增加了一些heroProxy自己的业务代码,增强了hero的战斗力(功能)
上述的代理,有一个很大的缺点,就是如果当代理类创建的targetObj多起来之后,实现代理就会变得非常复杂
比如下面这两种方式:
1. 静态代理多对多
每多一种目标类,就会多一种目标类
2. 静态代理1对多
ABCProxy
public class ABCProxy implements A1, B1, C1 {
private A a = new A();
private B b = new B();
private C c = new C();
@Override
public void a1() {
System.out.println("ABCProxy实现了A1,B1,C1接口");
a.a1();
}
@Override
public void b1() {
System.out.println("ABCProxy实现了A1,B1,C1接口");
b.b1();
}
@Override
public void c1() {
System.out.println("ABCProxy实现了A1,B1,C1接口");
c.c1();
}
}
ToRun类
public class ToRun {
public static void main(String[] args) {
ABCProxy abcProxy = new ABCProxy();
abcProxy.a1();
abcProxy.b1();
abcProxy.c1();
}
}
这种写法,虽然也是一种一对多,但是却是一种非常笨的1对多,假设我有100个需要代理的类对象,
你总不能让代理类实现100个接口吧?那样的话,代码可读性和可维护性会非常差,最终变成屎山代码
动态代理
接下来就要讲到我们聪明的动态代理了
动态代理,proxyObj是自动生成的
我们先来看一种过渡的动态代理情况,这种写法并没有实现动态代理(proxyObj仍然需要我们自己创建),但是可以帮助我们初步理解动态代理的写法,让学习曲线不那么陡峭
不是jdk动态代理的一种“动态代理”
和静态代理的一对多,其实是很相似的,不同的点在于,代理类的proxyObj不是一个对象,而是多个对象
一对多的静态代理
我们很容易能看出区别
这样做的好处是,不需要让代理类同时实现目标类实现的所有接口,非常简便
ClothesFactoryBuilder接口
public interface ClothesFactoryBuilder {
void prdClothes(String color);
}
目标类ClothesFactory,实现了ClothesFactoryBuilder接口
public class ClothesFactory implements ClothesFactoryBuilder {
@Override
public void prdClothes(String color) {
System.out.println("服装厂可以生产" + color + "颜色的衣服!");
}
}
ShoesFactoryBuilder接口
public interface ShoesFactoryBuilder {
void prdShoes(int size);
}
目标类ShoesFactory,实现了ShoesFactoryBuilder接口
public class ShoesFactory implements ShoesFactoryBuilder {
@Override
public void prdShoes(int size) {
System.out.println("鞋厂能生产鞋码为" + size + "码的鞋");
}
}
代理类FactoryProxy
public class FactoryProxy {
// jdk动态代理也是依靠接口来实现的,和静态代理类似
// 定义被代理的对象
public Object targetObject; // 用Object,避免了静态代理创建多个目标类对象
public FactoryProxy(Object o) {
this.targetObject = o;
}
}
使用Object当作代理对象,这样不用写多个代理对象,A类的对象作为A构造方法的参数创建代理对象,A类就会被代理
调用main方法
public class ToRun {
public static void main(String[] args) {
ShoesFactory shoesFactory = new ShoesFactory();
ClothesFactory clothesFactory = new ClothesFactory();
// 用户手动创建代理对象和目标对象,完成动态代理(代理类FactoryProxy创建多个代理对象代理目标类的对象)
FactoryProxy shoesProxy = new FactoryProxy(shoesFactory);
FactoryProxy clothesProxy = new FactoryProxy(clothesFactory);
shoesTargetObject.prdShoes(100);
clothesTargetObject.prdClothes("red");
}
}
这里我们已经能清晰的看到,创建了多个proxyObj,分别代理两个目标类的targetObj
基于JDK的动态代理(利用接口)
上面那种写没有实现动态代理,因为目标类的方法没有得到“增强”,刚才的写法甚至代理类完全不清楚代理什么方法,所以代理类也无法增强
jdk动态代理,主要依赖的反射机制实现,采用了反射当中的Proxy包的方法
目标类和目标方法的写法不变,重点是代理类的写法
代理类FactoryProxy
public class FactoryProxy implements InvocationHandler {
// jdk动态代理也是依靠接口来实现的,和静态代理类似
// 定义被代理的对象
private Object targetObject; // 用Object,避免了静态代理创建多个私有目标类对象的问题
public FactoryProxy(Object o) {
this.targetObject = o;
}
// 用反射获取interface对象,进而获取到需要代理增强的方法,避免代理类实现多个接口的问题
public Object getInterface() {
// 固定写法
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);//this是一个实现了InvocationHandler接口的对象
}
// jdk帮我们实现的,能够让我们调用目标类的核心方法(接口方法)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 功能增强的业务代码.因为有targetObject的存在,通过反射可以执行几乎目标类的任何方法
if (targetObject instanceof ShoesFactory) {
System.out.println("代理了ShoesFactory类的对象");
} else if (targetObject instanceof ClothesFactory) {
System.out.println("代理了ClothesFactory类的对象");
} else {
System.out.println("代理了" + targetObject.getClass().getName() + "类的对象");
}
// 传入目标类
return method.invoke(targetObject, args);
}
}
InvocationHandler在Spring框架实现中被大量使用,我们如果能理解透彻InvocationHandler,后续自己手写AOP的实现,理解Spring等肯定也都不是问题
getInterface方法让我们获取到目标类的targetObj的需要被代理的方法
invoke方法让我们能够执行targetObj的方法,只不过是通过接口来调用,而不是通过targetObj来调用
我们再来看最终运行的写法
这里用到了IOC,也就是控制反转,读者在这里可以把它理解成帮用户自动创建targetObj的一种机制
public static void main(String[] args) {
// 用IOC(控制反转)避免用户创建targetObj
// 让Spring帮我们创建目标类的对象,我们只需要创建代理类的对象即可,用代理类的对象代理目标类的对象
// 反射获取目标类的核心方法的执行接口对象
ShoesFactoryBuilder shoesFactoryBuilder =
(ShoesFactoryBuilder) new FactoryProxy(IOC.shoesFactory).getInterface();
shoesFactoryBuilder.prdShoes(100);
System.out.println("============================");
ClothesFactoryBuilder clothesFactoryBuilder =
(ClothesFactoryBuilder) new FactoryProxy(IOC.clothesFactory).getInterface();
clothesFactoryBuilder.prdClothes("red");
System.out.println("============================");
Fightable fightable = (Fightable) new FactoryProxy(IOC.hero).getInterface();
fightable.fight(5);
}
我们只需要新建一个FactoryProxy对象,在其中传入一个早已被IOC创建好的targetObj,就可以代理任何类的任何对象,执行任何接口中的方法
进行增强,进行访问控制
cjlib的代理,读者可以自己后续搜索,原理都是相同的