欢迎关注头条号:Java小野猫
一、什么是代理
代理是一种设计模式,它提供了一种通过代理访问目标对象的方式。在应用代理之前,我们调用对象的过程如下:
客户端直接调用对象并获取返回值。而应用了代理之后,我们调用对象的过程变成如下:
客户端先调用代理,代理再去调用目标对象,目标对象执行后的返回值先经过代理,再由代理返回给客户端。
二、代理的作用
应用代理后,任何对目标对象的调用和目标对象的返回值都要经过代理,因此我们可以在代理中实现非侵入式的代码扩展,让我们在不用修改源码的情况下对目标对象的某个方法的功能进行增强。我们可以在调用目标对象方法的前后进行任何操作,甚至阻止对目标对象方法的调用。
除此之外,代理还是AOP编程思想的一种实现方式。
三、代理的实现
在本例中,我们将通过静态代理和动态代理,实现在目标对象的方法执行前添加“权限检查”功能和方法执行后添加“日志记录”功能。
静态代理和动态代理都要求被代理的目标对象有实现接口,且要增强的方法在接口中必须有定义,因此这里我们创建一个Test1接口和它的实现类Test1Impl,该类实例化的对象就是接下来将要被代理的目标对象。
1 public interface Test1 {2 3 public void service1();4 5 }
1 public class Test1Impl implements Test1{2 3 @Override4 public void service1() {5 System.out.println("业务处理……");6 }7 8 }
另外,我们还需要创建一个切面类Aspect,里面包含我们要添加的功能的代码。
1 public class Aspect { 2 3 public static void check() { 4 System.out.println("权限检查……"); 5 } 6 7 public static void log() { 8 System.out.println("日志记录……"); 9 }10 }
1、静态代理的实现
静态代理的实现比较简单,我们只需编写一个代理类Test1Proxy:
1 public class Test1Proxy implements Test1{ //代理类需要实现与目标对象相同的接口 2 3 private Test1 test1 = null; 4 5 public Test1Proxy(Test1 test1) { //获取目标对象 6 this.test1 = test1; 7 } 8 9 @Override10 public void service1() { //代理对象的方法11 Aspect.check(); //权限检查12 test1.service1(); //代理对象调用目标对象方法13 Aspect.log(); //日志记录14 }15 16 }
客户端代码如下:
1 public class Main {2 public static void main(String[] args) {3 Test1 test1 = new Test1Impl(); //创建目标对象4 test1 = new Test1Proxy(test1); //创建目标对象的代理对象5 test1.service1(); //代理对象调用service1()方法6 }7 }
运行结果如下:
静态代理虽然实现简单,但存在一些问题。
第一,如果我们要对实现了不同接口的多个对象实现代理,就要编写实现了不同接口的代理类。
第二,如果要对目标对象的多个方法进行增强,则需要在多个方法中重复编写新功能的代码。
第三,一旦接口需要添加方法,我们不仅需要修改目标类,还需要额外修改代理类。
要解决这些问题,就需要用到动态代理。
2、动态代理的实现
同静态代理一样,动态代理也需要编写一个代理类,但不同的是,我们需要编写的是可以动态产生代理对象的代理工厂类ProxyFactory,代码如下:
1 import java.lang.reflect.InvocationHandler; 2 import java.lang.reflect.Method; 3 import java.lang.reflect.Proxy; 4 5 public class ProxyFactory { 6 7 public static Object getProxyObject(Object targetObject) { //根据目标对象得到代理对象 8 return Proxy.newProxyInstance( //返回创建的代理对象 9 targetObject.getClass().getClassLoader(), //获取目标对象的类加载器10 targetObject.getClass().getInterfaces(), //获取目标对象实现的接口11 new InvocationHandler() { //匿名内部类,含有代理对象的方法12 /**13 * 调用目标对象的方法时,都会先调用此方法14 * proxy:代理对象15 * method:调用目标对象方法名16 * args:调用目标对象方法时的参数17 */18 @Override19 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {20 Aspect.check(); //权限验证21 Object result = method.invoke(targetObject, args); //通过反射调用目标对象方法22 Aspect.log(); //日志记录23 return result; //返回目标对象方法执行后的返回值24 }25 });26 }27 }
编写好代理工厂类后,我们就可以来产生代理对象,客户端代码如下:
1 public class Main {2 public static void main(String[] args) {3 Test1 test1 = new Test1Impl(); //创建目标对象4 test1 = (Test1) ProxyFactory.getProxyObject(test1); //根据目标对象创建代理对象5 test1.service1(); //通过代理对象调用目标对象的方法6 }7 }
运行结果如下:
与静态代理相比,动态代理会自动的根据目标对象创建对应的代理对象,并且在执行过程中动态的对目标对象的方法进行增强,这就降低了代码编写量和维护成本。但动态代理会对接口中所有定义过的方法进行增强,如果希望只对部分方法进行增强,需要在invoke()方法中增加判断逻辑。
私信头条号,发送:“资料”,获取更多“秘制” 精品学习资料
如有收获,请帮忙转发,您的鼓励是作者最大的动力,谢谢!