詳解java中靜態署理完成機制。本站提示廣大學習愛好者:(詳解java中靜態署理完成機制)文章只能為提供參考,不一定能成為您想要的結果。以下是詳解java中靜態署理完成機制正文
署理形式是經常使用的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 a-b; } @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 要靜態署理的接口類名, e.g test.StudentInfoService * @param classToProxy String 要靜態署理的接口的完成類的類名, e.g 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 要靜態署理的接口的完成類的類名, e.g test.StudentInfoServiceImpl * @param interfaceClass Class 要靜態署理的接口類, e.g 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 要靜態署理的接口的完成類的類名, e.g 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 要靜態署理的接口的完成類的類名, e.g 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: e.g. //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中靜態署理完成機制的具體引見,願望對年夜家的進修有所贊助。