想要明白什么是动态代理,那么就一定要知道什么是动态代理,动态代理有啥用,为什么需要动态代理,是因为出现了什么问题,所以产生了动态代理这门技术。所以在谈动态代理之前,我们先来看看什么是静态代理:
什么是静态代理(Static Proxy)
什么是代理?代理就是给目标类提供一个代理对象,由代理对象控制目标对象的引用。
代理有啥好处呢?①通过代理对象的方式间接的访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性,②通过代理对象对原有的业务增强。
如果代理不重要,老板为啥会一人整一个秘书呢?因为秘书可以代替老板做很多不必要老板亲自出场的工作。或者有人想见老板(真正的对象),则需要先访问秘书(代理对象),秘书同意之后才能见面。
假如小李是一个江南皮革厂的老板,但是在创业初期,他没有足够的前,所以只能先低价购入韩国皮鞋,然后有小张觉得小李的鞋不错,而且便宜,所以就一直让小李代购。
他们的UML图如下:
先看一个例子:
tip:为了通俗所以使用了汉字,实际开发切忌不可使用汉字
先看抽象接口:
package com.bean.pixie;
//抽象接口:描述了服务提供者的行为
public interface 理宁 {
void saleShoes(String size);//尺码
}
然后是真实的对象:
package com.bean.pixie;
//代理对象
public class 小李 implements 卖鞋{
//被包含的真实对象
private 理宁 lining;
public 小李(理宁 lining) {
this.lining = lining;
}
@Override
public void saleShoes(String size) {
doSomethingBefore();
lining.saleShoes(size);
doSomethingAfter();
}
//前置增强
private void doSomethingBefore() {
System.out.println("检查鞋子是不是ok");
}
//后置增强
private void doSomethingAfter() {
System.out.println("将鞋子发送给客户");
}
}
由于小李(代理对象)并非是真实生产鞋子的,所以它包含一个真实对象做实际的工作,但是小李存在的意义是在真实对象做实际工作前后去做一些其他的方法。
看看客户小张的代码:
//客户小张>>
public class 小张 {
public static void main(String[] args) {
卖鞋 shoe = new 理宁();
小李 li = new 小李((理宁) shoe);
li.saleShoes("42");
}
}
看一下打印结果:
这就是静态代理的流程。
静态代理的缺点
假设小李的业务越做越好,越做越大,然后又有很多新的客户,并且客户有很多新的需求,比如有的要日本鞋,有的要欧美鞋。这个整体结构就变了。
需要新建新的抽象类、新的公司、新的用户,如果需求不断在增加,则代理对象会不停的实现接口,写新方法,代码就会非常非常冗余,非常多,这就造成了严重的耦合问题。
这就违反了设计模式七大原则之一:开闭原则(ocp),所谓ocp原则就是对程序扩展是open的,但是对于源码修改是close的,显然静态代理必须要修改源码。另外当前这个例子,还应该遵循单一原则:一个类或者一个接口只负责一个任务,尽量设计出职责单一的接口。依赖倒转原则:高层模块不应该依赖底层模块,解耦高层与底层,既然是面向接口编程,当实现发生变化时,只需提供新的实现类,不需要修改高层模块代码。
什么是动态代理
后来小李有钱了,开了个代购公司,公司有很多员工,有的员工很了解日本的鞋,有的很了解韩国货,有的很了解欧美货等等,当客户来买鞋的时候,小李只需要动态的挑选了解对方需求的员工然后去让他们负责就行了,这就体现了动态性。
假设小李又和一家新公司按踏进行了合作,按踏专门卖洋鞋的,所以产生了一个抽象接口:卖洋鞋:
package com.bean.pixie;
public interface 卖洋鞋 {
void saleForeignShows(int size);
}
按踏是专门卖洋鞋的,所以他需要实现这个接口:
package com.bean.pixie;
public class 按踏 implements 卖洋鞋{
@Override
public void saleForeignShows(int size) {
System.out.println("洋鞋尺寸为"+size+"码");
}
}
看看小李代购工厂的代码:
package com.bean.pixieCompany;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class 小李代购公司 implements InvocationHandler {
//被代理的对象
private Object 鞋厂;
public void set鞋厂(Object 鞋厂) {
this.鞋厂 = 鞋厂;
}
//通过动态代理对方法进行增强
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
doSomethingBefore();
Object invoke = method.invoke(鞋厂, args);
doSomethingAfter();
return invoke;
}
//通过Proxy获得动态代理对象
public Object getProxyInstance() {
return Proxy.newProxyInstance(鞋厂.getClass().getClassLoader(),鞋厂.getClass().getInterfaces(),this);
}
//前置增强
private void doSomethingBefore() {
System.out.println("检查鞋子是不是ok");
}
//后置增强
private void doSomethingAfter() {
System.out.println("将鞋子发送给客户");
}
}
此时我们新建一个测试类,里面有两种用户,有不同需求:
package com.bean.pixieCompany;
import com.bean.pixie.卖洋鞋;
import com.bean.pixie.卖鞋;
import com.bean.pixie.按踏;
import com.bean.pixie.理宁;
public class 客户 {
public static void main(String[] args) {
//1.卖洋鞋的工作由按踏公司生产
卖洋鞋 fshoe = new 按踏();
//2.卖普通鞋工作由理宁公司生产
卖鞋 sshoe = new 理宁();
//3.小李成立的代购公司
小李代购公司 liCom = new 小李代购公司();
//4.小刘来买洋鞋,则需要将卖洋鞋的工厂传给小刘代购公司
liCom.set鞋厂(fshoe);
//5.一号员工对卖洋鞋很有一套,交由一号员工处理
卖洋鞋 No_1 = (卖洋鞋)liCom.getProxyInstance();
//6.一号员工为小刘服务,完成代购
No_1.saleForeignShows(39);
System.out.println("==================");
//7.想买普通鞋的小赵来买鞋,则需要传入理宁代购公司对象
liCom.set鞋厂(sshoe);
//8.委派适合卖普通鞋的二号员工
卖鞋 No_2 = (卖鞋)liCom.getProxyInstance();
No_2.saleShoes("42");
}
}
想了解动态代理,反射原理必须清楚,想了解反射的童鞋可以参考一下我的上篇博客:https://blog.csdn.net/u011679785/article/details/98853403
我在这里简单介绍一下这个动态代理的原理:
由于小李代购公司有一个真实代理对象是Object类型的,所以可以接受任何类型的鞋厂,由于有不同的需求,所以设置不同的公司给鞋厂对象,设值后,小李代购公司这个类的工厂就有对象了,我想做的下一步是要调用当前这个工厂的卖鞋方法,所以接下来就是通过反射获得代理实例的过程,需要调用自己写的getProxyInstance这个方法,可以看出方法的返回值是Proxy.newInstance(ClassLoader c,Class<?>[] Interfaces,InvocationHandler);根据类加载器可以确定要把哪个类加载到内存,给了接口,可以确定这个类实现了哪些接口,最后一个InvocationHandler就是实际去调用代理对象方法的一个接口,什么意思呢,就是通过InvocationHandler接口中的唯一方法invoke,去调用你想使用的方法,通过getProxyInstance方法获得了一个代理对象,他是你想要的类型,由于InvocationHandler的缘故,所以该代理对象会调用当前InvocationHandler的invoke方法,执行前后增强的操作,并且返回最终的代理对象,拿到了最终的代理对象之后,就可以随意调用它的方法了。
直接说不容易理解,我们来看一下流程图:
这是小李代购公司代购的流程图,再看反射的流程图,为啥可以拿到理宁或者按踏类:
总结
通过动态代理,可以减少程序的耦合度,分工明确,但是反射等知识有点不好理解,需要童鞋深入研究一下~~~