什么是代理模式
在我们开发业务代码的过程中,经常会遇到甲方粑粑提出的新需求~要在已经写好的业务模块中要添加一些功能,比如:增加日志记录、或者一些事务的支持等等。
这时候,你肯定不会为了增加一点功能而想去动那个已经写好测试好的模块–毕竟有可能改着改着bug变多(狗头)。
作为一个优秀的程序员肯定不会认同上面的理由,那找个正当的理由:如果有多个模块需要用到这个功能,如果每一个模块都改,那重复代码岂不是很多了?
所以,为了解决上面的问题,聪明的前辈们想出了
代理模式
的设计思想
举个例子:比如电脑本身只有运行的功能,如果要销售电脑,并不是电脑自己能销售,而是要通过一个代理商去销售这部电脑实现电脑的销售过程。
用图表示如下:
在Java中,有三种方式实现代理模式
1、静态代理
要实现静态代理,前提需要定义接口或者父类,
被代理对象和代理对象
一起实现相同的接口或者是继承相同的父类
。
引用上面的例子就是电脑对象需要有一个电脑接口,代理商也要实现电脑这个接口,因为只有这样才能
调用代理对象方法
的时候
同时也调用了被代理对象的方法
。
这三者的关系图是这样的:
Java代码实例:
电脑接口:Computer
public interface Computer {
void running();
}
电脑实现类:ComputerImpl
public class ComputerImpl implements Computer {
@Override
public void running() {
System.out.println("Computer is running...");
}
}
代理类:ComputerProxy
public class ComputerProxy implements Computer {
//被代理对象
private ComputerImpl computer;
public ComputerProxy(ComputerImpl computer) {
this.computer = computer;
}
@Override
public void running() {
computer.running();
System.out.println("computer is selling...");
}
}
总结:静态代理就是将需要被代理的作为代理对象的一个属性,然后在自己的实现方法中调用了被代理对象的方法,再在该方法里面进行功能的增强。
但是静态代理也有它的不足:代理类和被代理类都需要实现同一个接口,也要实现相同的方法,这样会导致大量的重复代码!并且如果有多个模块需要增加同一个功能,这样就会导致需要创建大量的接口。
因此,动态代理也就出现了~
2、动态代理
动态代理的特点主要是通过
反射机制
创建一个实现了一组给定接口的新类。
要在Java中实现动态代理,需要了解一个类和一个接口:
InvocationHandler接口:
public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}
参数说明:
Object proxy:指被代理的对象(上面的电脑类)
Method method:要调用的方法(电脑类的run方法)
Object[] args: 方法调用时所需要的参数(run方法的参数)
代理类(代理商)就是实现InvocationHandler接口的类。
Proxy类
Proxy是来自java.lang.reflect包的一个专门完成代理的操作类,可以通过该类为一个或多个接口动态的生成实现类。该类提供的一个newProxyInstance的方法:
Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new ProxyFactory(target));
参数说明:
target : 被代理对象(电脑类)。
target.getClass().getClassLoader():被代理对象类加载器,固定写法。
target.getClass().getInterfaces():被代理对象接口(相当与电脑接口)
java代码实例:
其中被代理对象和接口和静态代理的一样。
public class ProxyFactory implements InvocationHandler {
//要被代理的对象,这里指computerImpl
private Object target;
ProxyFactory(Object target){
this.target = target;
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new ProxyFactory(target));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(target, args);
System.out.println("computer is selling...");
return null;
}
}
动态代理测试:
public class ComputerTest {
public static void main(String[] args) {
System.out.println("Computer without proxy--------------");
Computer computer = new ComputerImpl();
computer.running();
System.out.println("\n");
System.out.println("Computer with proxy-----------------");
Computer computerProxy = (Computer) new ProxyFactory(new ComputerImpl()).getProxyInstance();
computerProxy.running();
}
}
运行结果如图:
3、Cglib代理
上面的两种代理的前提都是需要代理的目标对象要实现一个
接口
。但是有时候如果目标对象只是一个单独的对象,并没有实现任何的接口,这时候就可以用这个Cglib代理。
简而言之:Cglib代理,也叫做子类代理,原理是它在内存中构建一个子类对象从而实现对目标功能的扩展。在Java的Spring AOP中就使用到了Cglib代理。
使用Cglib代理,要求是被代理的对象
不能是被final所修饰
,因为它是通过创建子类的方式实现的,如果该类被final修饰,将无法继承!
还要实现MethodInterceptor这个接口,该接口中有一个intercept的方法和动态代理的invoke方法相似。
代码实现:
被代理类Computer
public class Computer {
public void running(){
System.out.println("computer is running...");
}
}
代理类:ProxyFactory(相当代理商)
public class ProxyFactory implements MethodInterceptor {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance(){
//工具类
Enhancer en = new Enhancer();
//设置父类为被代理类
en.setSuperclass(target.getClass());
//设置回调函数
en.setCallback(this);
//创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
method.invoke(target,objects);
System.out.println("computer is selling...");
return null;
}
}
测试类:
public class ComputerTest {
public static void main(String[] args) {
//没有代理,只有电脑的基本功能
System.out.println("Computer without proxy--------------");
Computer computer = new Computer();
computer.running();
System.out.println("\n");
//代理后,增加销售功能
System.out.println("Computer with proxy-----------------");
Computer computerProxy = (Computer) new ProxyFactory(new Computer()).getProxyInstance();
computerProxy.running();
}
}
总结:
Spring中的AOP编程就是用代理模式实现的。
如果需要用到代理模式:
如果被代理对象有实现接口,用动态代理
如果被代理对象没有接口,用Cglib代理
好了,上面三种代理模式就到这~古耐