代理模式的简述
对于程序编程而言,代理模式,可以说是一个降低程序与程序之间耦合度的一种方式,它的目的就是为了,在给一个程序添加额外功能的时候,对其原有的代码不做改变,而是对该程序的一个代理程序进行额外功能的增加,这个代理程序,不但能执行好原有程序的功能,还能够执行添加的额外功能。这种模式,尤其是在我们面向对象语言中,是发挥的淋漓尽致,因为我们操作的是一个对象,对象可以抽象为类或者接口,我们就可以通过一个代理类和接口,更加灵活的设计程序,而且对于程序而言,其扩展性和维护性也大大提高,所以代理模式对于我们学习编程的帮助无疑是巨大的。
基本的代理模式一般分为静态代理和动态代理两种:
静态代理
对于静态代理,我们可以很好的理解,因为其就是代理模式的一种最基础,最简单的实现,它体现的就是一个代理关系,举个例子:你要卖掉自己的二手汽车,正常没有代理模式的情况就是,你找到了一个愿意买二手汽车的人,告诉他我要卖掉我的二手汽车,然后双方聊的很嗨,很愉快的达成了交易。skr! —当然这是没有代理人的情况下,但如果你平时工作很忙,你想卖掉车,然而并没有时间去到处找愿意买车的人,这个时候,你就希望找一个代理人来帮你干这件事,代理人可以帮助你找到愿意买车的人,当然前提是要给人家代理费用啦-_-!,然后整个交易的过程就变成了,这个代理人找到一个愿意买车的人,然后,代理人和愿意买车的人谈好,交易完成后,把钱给你,你完成了卖车的理想!大功告成,这就是静态代理的主要思维,当然在我们高大上的Java中来具体实现这个静态代理,就是要先将你和代理人充当的这个卖方先抽象出来,然后再实例化你这个对象和代理人这个对象,当然代理人是替你卖车的,也就是说卖方的核心人物还是你,代理人只是替你找到了一个买家而已。
请看代码:
关于卖方的一个接口
public interface Seller {
public void sellcar();
}
关于“你”的一个实例对象
public class Person implements Seller {
public void sellcar() {
System.out.println("车的主人要卖车");
}
}
关于代理人的一个实例对象:
public class Agent implements Seller{
//添加一个你的对象属性,这是实现代理的一个关键点
private Person person;
//这是确定代理对象的方法
public void setPerson(Person person) {
this.person = person;
}
public void sellcar() {
Introduce();
person.sellcar();
fare();
}
//定义两个代理人特有的方法
private void Introduce(){
System.out.println("我是一个代理人");
}
private void fare(){
System.out.println("我要收代理费");
}
}
新建一个测试类:
public class Test {
public static void main(String[] args) {
Person person = new Person();
Agent agent = new Agent();
agent.setPerson(person);
agent.sellcar();
}
}
测试结果:
像这样的一个过程就是静态代理,可见对于这个代理人,它除了实现被代理人的功能外,自己还额外的添加进去了自己的方法,这样就实现了开头我们说的再没有改变原有的程序下,添加了额外的功能,这个代理人,就像是另一个自己,不过是经过改造的自己。skr!
总结静态代理的特点
优点
- 静态代理的一大好处就是,它能够使一个对象专注的完成一项内容,而不用担心以后扩展其他功能的时候,会不会影响到现在的代码设计;
- 如果需要扩展功能,我们就可以让代理来完成,更加的集中和方便;
- 缺点
- 静态代理纵然是对我们的程序设计灵活性和专注性的帮助很大;但是如果我们操作的对象越来越多,也就是说,现在不止你一个人要卖车,有十四亿人民都要卖车,那么我们就需要十四亿个代理人,这样的话,实例化十四亿个代理人对象,工作量是非常大的,所以,我们就希望有一个制造代理人的机器,我们只要给他需要代理服务的人的对象(就是告诉它你要给谁服务),这个机器就能够自动的生成一个代理人,专诚为您服务。它能够自动去创建多个代理人角色,不需要我们一个一个来创建了;这个机器就是接下来即将出场的动态代理;
动态代理
动态代理其实本质就是一句话:抽象一个代理的类!或者说是得到一个代理的接口!拿上面的例子来讲,就是制造一个专门可以代理卖车服务代理人的机器!
实现动态代理现在大概分为两类:
基于接口实现:JDK
基于类实现:cglib
-
JDK动态代理
下面我主要说这个JDK实现动态代理
当前来说,我们用的比较多的是JAVAssist来生成动态代理;
了解之前,需要先掌握两个类 -
InvocationHandler
-
Proxy
这个InvocationHandler是由代理实例的调用处理程序实现的接口
*#%$##@#%&^^这句话,语法是有点复杂啊,我来解释一下,这个InvocationHandler是一个接口,一个大体实现什么功能的接口?一个代理实例的调用处理程序需要实现的接口,#¥%¥@¥#……¥@,简单说一下,就是如果我们要制造一个能够自动创造代理人的机器,这个接口就是我们制造机器的工程图纸,代理实例的调用处理程序指的就是这个机器!
invoke(Object proxy,方法 method,Object[ ] args)处理代理实例上的方法调用并返回结果。
Proxy提供了创建动态代理类个实例的静态方法,它也是由这些方法创建的所有动态代理类的超类
我们仍旧拿上面你卖车和找代理人卖车的例子来实现
关于卖方的一个抽象接口
public interface Seller {
public void sellcar();
}
关于“你”的一个类
public class Person implements Seller {
public void sellcar() {
System.out.println("车的主人要卖车");
}
}
通过通过实现InvocationHandler接口得到的一个代理人的类;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Machine implements InvocationHandler {
//和静态代理不同点,这里是先设置一个被代理人的接口属性,这是实现代理的关键点
private Seller seller;
public void setSeller(Seller seller) {
this.seller = seller;
}
//这个方法是获得一个代理人的方法
public Object getProxy(){
这里的一个返回值是一个Proxy.newProxyInstance()方法,这个方法就是通过反射得到一个代理人对象,并将这个对象返回,方法是我们传入一个被代理人的类加载器,再通过反射添加一个被代理人的它实现的接口,再传入这个代理人的类的实例对象
return Proxy.newProxyInstance(seller.getClass().getClassLoader(),seller.getClass().getInterfaces(),this);
}
//这个方法是实现接口InvocationHadler的实现方法,
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Introduce();
//通过反射得到被代理人的方法
Object invoke = method.invoke(seller, args);
fare();
return invoke;
}
private void Introduce(){
System.out.println("我是一个代理人");
}
private void fare(){
System.out.println("我要收代理费");
}
}
建立一个测试类:
public class Test {
public static void main(String[] args) {
Person person = new Person();
Machine machine = new Machine();
machine.setPerson(person);
Seller proxy = (Seller) machine.getProxy();
proxy.sellcar();
}
}
测试结果:
这样就是我们得到了一动态代理的小demo,为了说明效果,我们可以再新建一个对象来进行测试
关于另一个要卖车人的类:
public class AnotherPerson implements Seller {
public void sellcar() {
System.out.println("我是蔡徐坤,除了刚才的人,我蔡徐坤今天也要卖车");
}
}
新建一个测试类:
public class Test {
public static void main(String[] args) {
AnotherPerson anotherPerson = new AnotherPerson();
Machine machine = new Machine();
machine.setSeller(anotherPerson);
Seller proxy = (Seller) machine.getProxy();
proxy.sellcar();
}
}
测试结果:
至此,我们就是通过JDK实现了动态代理,另外的一种动态代理的方法,也就是CGLIB(基于类实现动态代理),它适用于什么类型的呢?如果说,我们现在有一个对象,它并没有接口,只有一个继承类。这样子的话,我们就需要来使用CGLIB实现动态代理。使用CGLIB动态代理的话,我们需要去导入两个jar包分别是CGLIB和ASM .我们同样拿上面的卖车的例子来讲:
-
CGLIB
建一个卖车的类:public class AnotherPerson extends Seller {
public void sellcar() { System.out.println("我蔡徐坤今天也要卖车"); }
}
建立代理人类:
public class Machine implements MethodInterceptor {
public Object intercept(Object proxyobject, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//前置业务处理代码
System.out.println("我是一个代理人");
// invokeSuper方法还是为了获得父类的根方法,同之前的JDK=动态代理相同
methodProxy.invokeSuper(proxyobject,objects);
// 后置代理处理代码
System.out.println("代理完我要收代理费");
return null;
}
}
编写测试类:
import net.sf.cglib.proxy.Enhancer;
public class Test {
public static void main(String[] args) {
// 创建Enhancer实例,也就是cglib中的一个class generator
Enhancer enhancer = new Enhancer();
// 设置目标类
enhancer.setSuperclass(AnotherPerson.class);
// 设置代理人对象
enhancer.setCallback(new Machine());
//Seller是person继承的一个抽象父类,这里是多态的体现形式,获得了一个代理人对象,利用多态调用子类重写的方法
Seller o = (AnotherPerson) enhancer.create();
o.sellcar();
}
}
测试结果:
到这里我们就完成了一个CGLIB动态代理。
动态代理总结:动态代理具有静态代理的全部优点,其实可以说这种优点就是代理模式的优点,它能够使我们的程序再添加一些公共业务时能够更加的灵活,达到在不修改原始用户操作类代码的情况下,对其添加业务校验代码。而静态代理比较简单,不够灵活,适用面比较窄,实际的应用中使用的基本都是动态代理。Java中的动态代理实现包括JDK原生的动态代理和CGLIB动态代理。JDK动态代理必须要有接口,而CGLIB可以不要接口。两者相比较而言,JDK动态代理,它的适用性更高一点。