Java的代理模式是应用非常广泛的设计模式之一,也叫作委托模式,其目的就是为其他的对象提供一个代理以控制对某个对象的访问和使用,代理类负责为委托类预处理消息,过滤消息并转发消息,以及对消息执行后续处理。
代理就是充当一个中间人的角色。
按照代理的创建时期,代理类可以分为两种:
静态代理:指由程序员直接创建生成源代码,在对其编译生成.class文件,在程序运行之前就已经存在
动态代理:在程序运行时,使用java的反射机制动态创建而成。其中动态代理又分为JDK代理(需要接口)和cglib代理(不需要接口)
下面将以程序案例的方式演示Java静态代理和动态代理的区别
假设现在需要实现计算机的加减乘除操作,现有如下接口和实现类;
1 package Reflection.proxy; 2 3 /** 4 * Created by : Infaraway 5 * DATE : 2017/3/3 6 * Time : 15:12 7 * Funtion : 计算器的功能 8 */ 9 public interface Calculator {10 11 int add(int a, int b);12 int sub(int a, int b);13 14 void mul(int a, int b);15 void div(int a, int b);16 }
1 package Reflection.proxy; 2 3 /** 4 * Created by : Infaraway 5 * DATE : 2017/3/3 6 * Time : 15:14 7 * Funtion : 8 */ 9 public class CalculatorImpl implements Calculator{10 @Override11 public int add(int a, int b) {12 //System.out.println("The method add begin...);13 int result = a + b;14 //System.out.println("The method add end...);15 System.out.println(result);16 return result;17 }18 19 @Override20 public int sub(int a, int b) {21 int result = a - b;22 System.out.println(result);23 return result;24 }25 26 @Override27 public void mul(int a, int b) {28 int result = a * b;29 System.out.println(result);30 }31 32 @Override33 public void div(int a, int b) {34 int result = a / b;35 System.out.println(result);36 }37 }
如上部分add方法所示,现在希望在每个方法的实现之前打印方法开始和方法结束的日志信息,那么最容易的方法就是在源代码中的每个方法全部加上,但是这样非常的繁琐(需要编写大量的相同的代码),并且代码的维护性非常的差!
为了解决这个问题,我们需要使用代理的方法来解决。
静态代理:
首先我们使用静态代理的解决方法:
package Reflection.proxy;/** * Created by : Infaraway * DATE : 2017/3/3 * Time : 19:51 * Funtion : java静态代理类实现 */public class StaticProxy implements Calculator { private CalculatorImpl calculatorImpl; public StaticProxy(CalculatorImpl calculatorImpl){ this.calculatorImpl = calculatorImpl; } @Override public int add(int a, int b) { System.out.println("the add method begin..."); //调用被代理类的方法 int result = calculatorImpl.add(a, b); System.out.println("the add method end..."); return result; } @Override public int sub(int a, int b) { System.out.println("the sub method begin..."); //调用被代理类的方法 int result = calculatorImpl.sub(a, b); System.out.println("the sub method end..."); return result; } @Override public void mul(int a, int b) { System.out.println("the mul method begin..."); //调用被代理类的方法 calculatorImpl.mul(a, b); System.out.println("the mul method end..."); } @Override public void div(int a, int b) { System.out.println("the div method begin..."); //调用被代理类的方法 calculatorImpl.div(a, b); System.out.println("the div method end..."); }}
显然,静态代理方法并不能改变原来繁琐的步骤,并且每个代理类只能为一个借口服务,这样的话,程序中必然会存在非常多的代理,并且这样的代理仍然会产生修改代码困难的问题;
因此,解决这个问题的一个非常好的办法出现了,那就是动态代理~
动态代理:
动态代理又分为两种:JDK代理 和 cglib代理
JDK代理:主要针对的是有接口的情况;
其中JDK动态代理包含了一个接口和一个类:
Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法: public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException
/** * ClassLoader :由动态代理产生的对象由哪个类加载器来加载 通常情况下和被代理对象使用同样的类加载器; * Class [] : 由动态代理产生的对象必须要实现的接口的Class数组; * InvocationHandler : 当具体调用代理对象方法时,将产生的具体行为; 通常使用匿名内部类的方式实现。 */
InvocationHandler接口:
public interface InvocationHandler { public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; } 参数说明:/** * @param proxy 被代理的对象 * @param method: 正在被调用的方法 * @param args :调用方法时传入的参数 * @return 被调用方法的返回值 * @throws Throwable */因此,当时用JDK代理方式实现上述的需求时,则如下代码所示:
Reflection.proxy; org.junit.Test; java.lang.reflect.InvocationHandler; java.lang.reflect.Method; java.lang.reflect.Proxy; DynamicJDKProxy { @Test dynamicJDKProxy(){ Calculator calculator = CalculatorImpl(); Calculator calculatorProxy = (Calculator) Proxy.newProxyInstance( calculator.getClass().getClassLoader(), Class[]{Calculator.}, InvocationHandler() { @Override Object invoke(Object proxy, Method method, Object[] args) Throwable { System.out.println("The method "+method.getName()+" begin..."); Object obj = method.invoke(calculator, args); System.out.println("The method "+method.getName()+" end..."); obj; } }); //测试打印输出结果 calculatorProxy.mul(2, 3); result = calculatorProxy.add(1, 5); System.out.println(result); }}
运行结果:
The method mul begins...6The method mul ends...The method add begins...6The method add ends...6
虽然上述方法很好的解决了问题,但是JDK动态代理必须依靠接口实现,如果某些类并没有实现接口,那么就不能使用JDK代理方式;
所以这里又给出一种新的代理方法:cglib动态代理来解决接口的问题。
cglib代理:针对没有接口的情况;主要是针对类来实现,主要思想是对指定的目标类来生成一个子类,然后覆盖子类的方法实现增强。
需要实现MethodInterceptor接口,实现intercept方法。该代理中在add方法前后加入了自定义的切面逻辑,目标类add方法执行语句为proxy.invokeSuper(object, args);
Reflection.proxy; net.sf.cglib.proxy.Enhancer; net.sf.cglib.proxy.MethodInterceptor; net.sf.cglib.proxy.MethodProxy; java.lang.reflect.Method; DynamicCglibProxy MethodInterceptor { Object target; Object createProxy(Object target){ .target = target; Enhancer enhancer = Enhancer(); enhancer.setSuperclass(.target.getClass()); enhancer.setCallback(); enhancer.create(); } @Override Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) Throwable { System.out.println("The method "+method.getName()+" begins..."); Object object = methodProxy.invokeSuper(obj, objects); System.out.println("The method "+method.getName()+" ends..."); object; }}
Reflection.proxy; org.junit.Test; TestDynamicCglibProxy { @Test testProxy(){ DynamicCglibProxy CglibProxy = DynamicCglibProxy(); CalculatorImpl testCalculator = (CalculatorImpl) CglibProxy.createProxy( CalculatorImpl()); testCalculator.add(3,5); }}
想要更进一步了解Java代理模式,则需要认真学习Java的反射机制