代理模式是常用的java設計模式,它的特徵是代理類與委託類有同樣的接口,代理類主要負責為委託類預處理消息、過濾消息、把消息轉發給委託類,以及事後處理消息等。代理類與委託類之間通常會存在關聯關係,一個代理類的對象與一個委託類的對象關聯,代理類的對象本身並不真正實現服務,而是通過調用委託類的對象的相關方法,來提供特定的服務。
JAVA各種動態代理實現的比較
接口
interface AddInterface{ int add(int a, int b);}interface SubInterface{ int sub(int a, int b);}實現類
class Arithmetic implements AddInterface, SubInterface{ @Override public int sub(int a, int b) { return ab; } @Override public int add(int a, int b) { return a+b; }}方式1: JDK自帶的動態代理
1、實現方式
Java在JDK1.3後引入的動態代理機制,使我們可以在運行期動態的創建代理類。使用動態代理實現AOP需要有四個角色:被代理的類,被代理類的接口,織入器,和InvocationHandler,而織入器使用接口反射機制生成一個代理類,然後在這個代理類中織入代碼。被代理的類是AOP裡所說的目標,InvocationHandler是切面,它包含了Advice和Pointcut。
2、vInvocationHandler接口的實現
class JdkDPQueryHandler implements InvocationHandler{ private Arithmetic real; public JdkDPQueryHandler(Arithmetic real){ this.real = real; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); System.out.println(method); System.out.println("the method: " + methodName + "開始, 參數: "+Arrays.asList(args)); Object result = method.invoke(real, args); System.out.println("the method: "+methodName+"結束, 結果: " + result); return result; }}3、創建代理類並且調用代理類
public class Main{ private static int a = 4, b = 2; public static Object createJDKProxy(Arithmetic real){ Object proxyArithmetic = Proxy.newProxyInstance(real.getClass().getClassLoader(), real.getClass().getInterfaces(), new JdkDPQueryHandler(real)); return proxyArithmetic; } public static void main(String[] args){ Arithmetic real = new Arithmetic(); Object proxyArithmetic = createJDKProxy(real); ((AddInterface)proxyArithmetic).add(a, b); ((SubInterface)proxyArithmetic).sub(a, b); }}方式2:動態字節碼生成(cglib)
1、實現方式
Enhancer和MethodInterceptor。 Enhancer可以用來動態的生成一個類,這個類可以繼承指定的一個類,實現指定的一些接口。同時,Enhancer在生成一個類之前需要指定一個Callback,當類方法調用時,方法的執行被分配給這個Callback,MethodInterceptor是一個使用比較多的繼承自Callback的接口,它只有一個方法聲明。
2、接口InvocationHandler(jdk中)和接口MethodInterceptor(cglib中)對比
public interface MethodInterceptor extends Callback { public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable; } public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }從參數構成上,methodInterceptor的輸入參數比Invocationhandler多1個,其實前3個參數對象的含義與Invocationhandler的含義是相同的。
第一個參數表示調用方法來自哪個對象;
第二個參數表示調用方法的Method對象;
第三個參數表示此次調用的輸入參數列表;
多出來的參數是MethodProxy 類型的,它應該是cglib生成用來代替Method對象的一個對象,使用MethodProxy比調用JDK自身的Method直接執行方法效率會有提升。
3、實現1
MethodInterceptor接口的實現
class CglibDPQueryInterceptor implements MethodInterceptor{ private Arithmetic real; public CglibDPQueryInterceptor(Arithmetic real){ this.real = real; } @Override public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable { String methodName = method.getName(); System.out.println(method); System.out.println("the method: " + methodName + "開始, 參數: "+Arrays.asList(args)); //Object result = method.invoke(real, args);//兩種方式都是可以得Object result = proxy.invoke(real, args); System.out.println("the method: "+methodName+"結束, 結果: " + result); return result; }}創建代理類並調用代理類
public class Main{ private static int a = 4, b = 2; public static Object createCglibProxy(Arithmetic real){ Enhancer enhancer = new Enhancer(); enhancer.setCallback(new CglibDPQueryInterceptor(real)); enhancer.setInterfaces(real.getClass().getInterfaces()); return enhancer.create(); } public static void main(String[] args){ Arithmetic real = new Arithmetic(); Object proxyArithmetic = createCglibProxy(real); ((AddInterface)proxyArithmetic).add(a, b); ((SubInterface)proxyArithmetic).sub(a, b); }}注意了,MethodProxy在對執行函數的時候,提供了2個方法
public Object invoke (Object obj, Object[] args) throws Throwable public Object invokeSuper(Object obj, Object[] args) throws Throwable
其中,javadoc上說這個invoke()方法可以用於相同類中的其他對象的方法執行,也就是說這個方法中的obj需要傳入相同一個類的另一個對象(上述方法中就是傳入了Arithmetic類的不同對象),否則會進入無限遞歸循環(測試之後還真是出現了StackOverflowError)。仔細的想一想就會發現,public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy)中的target是實現的代理類對象,通過target調用add()方法時會觸發intercept()方法被調用,如果在intercept()方法中再調用method.invoke(target, args),就相當於add()方法中又調用add()方法,導致無限的遞歸循環。但是如果執行method.invoke(real, args)則不會,因為real和target是同一個類不同對象,real是真實邏輯主題,target是真實主題real的代理。
下面一個例子來模擬一下:
interface SolveInterface{ void solve();}class Real implements SolveInterface{ public void solve(){ System.out.println("Real Solve!"); }}class Target extends Real{ private Object obj; public void setObject(Object obj){ this.obj = obj; } private void invoke(){ try { Method method = SolveInterface.class.getMethod("solve", new Class[]{}); method.invoke(obj, new Class[]{}); } catch (Exception e) { e.printStackTrace(); } } public void solve(){ System.out.println("Target Solve!"); invoke(); }} public class Main{public static void main(String[] args) throws Exception{ Target target = new Target(); target.setObject(new Real());//正確//target.setObject(target);//發生循環調用target.solve(); }}其實Method的invoke()方法會根據obj的類型去調用對應的solve()方法,也就是多態性。
4、實現2
MethodInterceptor接口的實現
class CglibDPQueryInterceptor implements MethodInterceptor{ @Override public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable { String methodName = method.getName(); System.out.println(method); System.out.println("the method: " + methodName + "開始, 參數: "+Arrays.asList(args)); // 打印類信息:target.getClass();省略Object result = proxy.invokeSuper(target, args); System.out.println("the method: "+methodName+"結束, 結果: " + result); return result; }}創建代理類並調用代理類
public class Main{ private static int a = 4, b = 2;public static Object createCglibProxy(){ Enhancer enhancer = new Enhancer(); enhancer.setCallback(new CglibDPQueryInterceptor()); enhancer.setSuperclass(Arithmetic.class); return enhancer.create(); } public static void main(String[] args){ Arithmetic real = new Arithmetic(); Object proxyArithmetic = createCglibProxy(); ((AddInterface)proxyArithmetic).add(a, b); ((SubInterface)proxyArithmetic).sub(a, b); }}注意了,實現2中Enhancer 沒有設置接口,因為設置了Superclass了(也就是代理類的父類是Arithmetic),我們的代理類會繼承它,而Arithmetic已經實現了我們的接口。為了證明這一點,可以在MethodInterceptor的intercept方法中打印target.getClass()的類信息,你會發現cglib的兩種方式代理類的父類是不同的。如下:
實現1:
public class com.test.Arithmetic$$EnhancerByCGLIB$$4fa786eb extends java.lang.Object
實現2:
public class com.test.Arithmetic$$EnhancerByCGLIB$$4fa786eb extends com.test.Arithmetic
方式3: javassist生成動態代理(代理工廠創建或者動態代碼創建)
Javassist是一個編輯字節碼的框架,可以讓你很簡單地操作字節碼。它可以在運行期定義或修改Class。使用Javassist實現AOP的原理是在字節碼加載前直接修改需要切入的方法。這比使用Cglib實現AOP更加高效,並且沒太多限制,實現原理如下圖:
實現1:
接口的實現
class JavassistDPQueryHandler implements MethodHandler{ @Override public Object invoke(Object target, Method method, Method proxy, Object[] args) throws Throwable { String methodName = method.getName(); System.out.println(method); System.out.println("the method: " + methodName + "開始, 參數: "+Arrays.asList(args)); Object result = proxy.invoke(target, args); System.out.println("the method: "+methodName+"結束, 結果: " + result); return result; }}創建代理類並調用代理類
public class Main{ private static int a = 4, b = 2;public static Object createJavassistProxy() throws Exception{ ProxyFactory factory = new ProxyFactory(); factory.setSuperclass(Arithmetic.class); factory.setHandler(new JavassistDPQueryHandler()); return factory.createClass().newInstance(); } public static void main(String[] args) throws Exception{ Arithmetic real = new Arithmetic(); Object proxyArithmetic = createJavassistProxy(); ((AddInterface)proxyArithmetic).add(a, b); ((SubInterface)proxyArithmetic).sub(a, b); }}注意:MethodHandler接口中invoke方法的定義,如下:
public Object invoke(Object target, Method method, Method proxy, Object[] args)
method代表調用方法的Method對象,proxy是代理類產生並代替method的對象,否則用method.invoke(target, args)會產生無限循環調用。
實現2:
javassist使用動態java代碼常見代理過程和前文的方法略有不同。 javassist內部可以通過動態java代碼,生成字節碼。這種方式創建的動態代理可以非常靈活,甚至可以在運行時產生業務邏輯。
//自定義攔截器接口interface InterceptorHandler { /** * 調用動態代理對象的方法將反射本方法,可在本方法實現中添加類似AOP的事前事後操作,只有在本方法體中加入如下代碼* 被代理的方法才會被執行,返回值將返回給代理最後返回給程序* @param obj Object 被代理的對象* @param method Method 被代理對象的方法* @param args Object[] 被代理對象的方法的參數* @return Object 被代理對象的方法執行後的返回值* @throws Throwable */ public Object invoke(Object obj, Method method, Object[] args) throws Throwable; } //攔截器的實現class InterceptorHandlerImpl implements InterceptorHandler{ @Override public Object invoke(Object obj, Method method, Object[] args) throws Throwable { String methodName = method.getName(); System.out.println(method); System.out.println("the method: " + methodName + "開始, 參數: "+Arrays.asList(args)); Object result = method.invoke(obj, args); System.out.println("the method: "+methodName+"結束, 結果: " + result); return result; }}class MyProxyImpl { /** 動態代理類的類名後綴*/ private final static String PROXY_CLASS_NAME_SUFFIX = "$MyProxy_"; /** 攔截器接口*/ private final static String INTERCEPTOR_HANDLER_INTERFACE = "com.test.InterceptorHandler"; /** 動態代理類的類名索引,防止類名重複*/ private static int proxyClassIndex = 1; /** * 暴露給用戶的動態代理接口,返回某個接口的動態代理對象,注意本代理實現需和com.cuishen.myAop.InterceptorHandler攔截器配合* 使用,即用戶要使用本動態代理,需先實現com.cuishen.myAop.InterceptorHandler攔截器接口* @param interfaceClassName String 要動態代理的接口類名, eg test.StudentInfoService * @param classToProxy String 要動態代理的接口的實現類的類名, eg test.StudentInfoServiceImpl * @param interceptorHandlerImplClassName String 用戶提供的攔截器接口的實現類的類名* @return Object 返回某個接口的動態代理對象* @throws InstantiationException * @throws IllegalAccessException * @throws NotFoundException * @throws CannotCompileException * @throws ClassNotFoundException * @see com.cuishen.myAop.InterceptorHandler */ public static Object newProxyInstance(String interfaceClassName, String classToProxy, String interceptorHandlerImplClassName) throws InstantiationException, IllegalAccessException, NotFoundException, CannotCompileException, ClassNotFoundException { Class interfaceClass = Class.forName(interfaceClassName); Class interceptorHandlerImplClass = Class.forName(interceptorHandlerImplClassName); return dynamicImplementsInterface(classToProxy, interfaceClass, interceptorHandlerImplClass); } /** * 動態實現要代理的接口* @param classToProxy String 要動態代理的接口的實現類的類名, eg test.StudentInfoServiceImpl * @param interfaceClass Class 要動態代理的接口類, eg test.StudentInfoService * @param interceptorHandlerImplClass Class 用戶提供的攔截器接口的實現類* @return Object 返回某個接口的動態代理對象* @throws NotFoundException * @throws CannotCompileException * @throws InstantiationException * @throws IllegalAccessException */ private static Object dynamicImplementsInterface(String classToProxy, Class interfaceClass, Class interceptorHandlerImplClass) throws NotFoundException, CannotCompileException, InstantiationException, IllegalAccessException { ClassPool cp = ClassPool.getDefault(); String interfaceName = interfaceClass.getName(); //動態指定代理類的類名String proxyClassName = interfaceName + PROXY_CLASS_NAME_SUFFIX + proxyClassIndex++; //要實現的接口的包名+接口名String interfaceNamePath = interfaceName; CtClass ctInterface = cp.getCtClass(interfaceNamePath); CtClass cc = cp.makeClass(proxyClassName); cc.addInterface(ctInterface); Method [] methods = interfaceClass.getMethods(); for(int i = 0; i < methods.length; i++) { Method method = methods[i]; dynamicImplementsMethodsFromInterface(classToProxy, cc, method, interceptorHandlerImplClass, i); } return (Object)cc.toClass().newInstance(); } /** * 動態實現接口裡的方法* @param classToProxy String 要動態代理的接口的實現類的類名, eg test.StudentInfoServiceImpl * @param implementer CtClass 動態代理類的包裝* @param methodToImpl Method 動態代理類裡面要實現的接口方法的包裝* @param interceptorClass Class 用戶提供的攔截器實現類* @param methodIndex int 要實現的方法的索引* @throws CannotCompileException */ private static void dynamicImplementsMethodsFromInterface(String classToProxy, CtClass implementer, Method methodToImpl, Class interceptorClass, int methodIndex) throws CannotCompileException { String methodCode = generateMethodCode(classToProxy, methodToImpl, interceptorClass, methodIndex); CtMethod cm = CtNewMethod.make(methodCode, implementer); implementer.addMethod(cm); } /** * 動態組裝方法體,當然代理裡面的方法實現並不是簡單的方法拷貝,而是反射調用了攔截器裡的invoke方法,並將接收到的參數進行傳遞* @param classToProxy String 要動態代理的接口的實現類的類名, eg test.StudentInfoServiceImpl * @param methodToImpl Method 動態代理類裡面要實現的接口方法的包裝* @param interceptorClass Class 用戶提供的攔截器實現類* @param methodIndex int 要實現的方法的索引* @return String 動態組裝的方法的字符串*/ private static String generateMethodCode(String classToProxy, Method methodToImpl, Class interceptorClass, int methodIndex) { String methodName = methodToImpl.getName(); String methodReturnType = methodToImpl.getReturnType().getName(); Class[] parameters = methodToImpl.getParameterTypes(); Class[] exceptionTypes = methodToImpl.getExceptionTypes(); StringBuffer exceptionBuffer = new StringBuffer(); //組裝方法的Exception聲明if(exceptionTypes.length > 0) exceptionBuffer.append(" throws "); for(int i = 0; i < exceptionTypes.length; i++) { if(i != exceptionTypes.length - 1) exceptionBuffer.append(exceptionTypes[i].getName()).append(","); else exceptionBuffer.append(exceptionTypes[i].getName()); } StringBuffer parameterBuffer = new StringBuffer(); //組裝方法的參數列表for(int i = 0; i < parameters.length; i++) { Class parameter = parameters[i]; String parameterType = parameter.getName(); //動態指定方法參數的變量名String refName = "a" + i; if(i != parameters.length - 1) parameterBuffer.append(parameterType).append(" " + refName).append(","); else parameterBuffer.append(parameterType).append(" " + refName); } StringBuffer methodDeclare = new StringBuffer(); //方法聲明,由於是實現接口的方法,所以是public methodDeclare.append("public ").append(methodReturnType).append(" ").append(methodName).append("(").append(parameterBuffer).append(")").append(exceptionBuffer).append(" {/n"); String interceptorImplName = interceptorClass.getName(); //方法體methodDeclare.append(INTERCEPTOR_HANDLER_INTERFACE).append(" interceptor = new ").append(interceptorImplName).append("();/n"); //反射調用用戶的攔截器接口methodDeclare.append("Object returnObj = interceptor.invoke(Class.forName(/"" + classToProxy + "/").newInstance(), Class.forName(/"" + classToProxy + "/").getMethods()[" + methodIndex + "], "); //傳遞方法裡的參數if(parameters.length > 0) methodDeclare.append("new Object[]{"); for(int i = 0; i < parameters.length; i++) { //($w) converts from a primitive type to the corresponding wrapper type: eg //Integer i = ($w)5; if(i != parameters.length - 1) methodDeclare.append("($w)a" + i + ","); else methodDeclare.append("($w)a" + i); } if(parameters.length > 0) methodDeclare.append("});/n"); else methodDeclare.append("null);/n"); //對調用攔截器的返回值進行包裝if(methodToImpl.getReturnType().isPrimitive()) { if(methodToImpl.getReturnType().equals(Boolean.TYPE)) methodDeclare.append("return ((Boolean)returnObj).booleanValue();/n"); else if(methodToImpl.getReturnType().equals(Integer.TYPE)) methodDeclare.append("return ((Integer)returnObj).intValue();/n"); else if(methodToImpl.getReturnType().equals(Long.TYPE)) methodDeclare.append("return ((Long)returnObj).longValue();/n"); else if(methodToImpl.getReturnType().equals(Float.TYPE)) methodDeclare.append("return ((Float)returnObj).floatValue();/n"); else if(methodToImpl.getReturnType().equals(Double.TYPE)) methodDeclare.append("return ((Double)returnObj).doubleValue();/n"); else if(methodToImpl.getReturnType().equals(Character.TYPE)) methodDeclare.append("return ((Character)returnObj).charValue();/n"); else if(methodToImpl.getReturnType().equals(Byte.TYPE)) methodDeclare.append("return ((Byte)returnObj).byteValue();/n"); else if(methodToImpl.getReturnType().equals(Short.TYPE)) methodDeclare.append("return ((Short)returnObj).shortValue();/n"); } else { methodDeclare.append("return (" + methodReturnType + ")returnObj;/n"); } methodDeclare.append("}"); System.out.println(methodDeclare.toString()); return methodDeclare.toString(); } } public class Main{ public static void main(String[] args) throws Exception{ //分別對應代理類要實現的接口類名, 需要代理類的類名, 用戶自定義攔截器實現類的類名Object proxyArithmetic = MyProxyImpl.newProxyInstance("com.test.ArithmeticInterface", "com.test.Arithmetic", "com.test.InterceptorHandlerImpl"); ((ArithmeticInterface)proxyArithmetic).add(a, b); ((ArithmeticInterface)proxyArithmetic).sub(a, b); }}打印一下動態實現接口的代碼如下:
public int add(int a0,int a1) {com.test.InterceptorHandler interceptor = new com.test.InterceptorHandlerImpl();Object returnObj = interceptor.invoke(Class.forName("com.test.Arithmetic").newInstance(), Class.forName("com.test.Arithmetic").getMethods()[0], new Object[]{($w)a0,($w)a1});return ((Integer)returnObj).intValue();}public int sub(int a0,int a1) {com.test.InterceptorHandler interceptor = new com.test.InterceptorHandlerImpl();Object returnObj = interceptor.invoke(Class.forName("com.test.Arithmetic").newInstance(), Class.forName("com.test.Arithmetic").getMethods()[1], new Object[]{($w)a0,($w)a1});return ((Integer)returnObj).intValue();}以上就是關於java中動態代理實現機制的詳細介紹,希望對大家的學習有所幫助。