The proxy model is a commonly used Java design model. Its characteristic is that the proxy class and the delegate class have the same interface. The proxy class is mainly responsible for preprocessing messages, filtering messages, forwarding messages to the delegate class, and processing messages after the event. There is usually an association between the proxy class and the delegate class. The object of a proxy class is associated with the object of a delegate class. The object of the proxy class itself does not truly implement the service, but provides specific services by calling the relevant methods of the delegate class object.
Comparison of various dynamic proxy implementations of JAVA
interface
interface AddInterface{ int add(int a, int b);}interface SubInterface{ int sub(int a, int b);} Implementation Class
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; }}Method 1: Dynamic proxy built into JDK
1. Implementation method
The dynamic proxy mechanism introduced by Java after JDK1.3 allows us to dynamically create proxy classes during runtime. Implementing AOP using dynamic proxy requires four roles: the proxy class, the proxy class interface, the weaving device, and the InvocationHandler. The weaving device uses the interface reflection mechanism to generate a proxy class, and then weave code into this proxy class. The proxy class is the target mentioned in AOP. InvocationHandler is a section, which contains Advice and Pointcut.
2. Implementation of vInvocationHandler interface
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 + "Start, Parameters: "+Arrays.asList(args)); Object result = method.invoke(real, args); System.out.println("the method: "+methodName+" ends, result: " + result); return result; }}3. Create a proxy class and call the proxy class
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); }}Method 2: Dynamic bytecode generation (cglib)
1. Implementation method
Enhancer and MethodInterceptor. Enhancer can be used to dynamically generate a class, which can inherit a specified class and implement some specified interfaces. At the same time, Enhancer needs to specify a Callback before generating a class. When the class method is called, the execution of the method is assigned to this Callback. MethodInterceptor is a more widely used interface inherited from Callback, and it only has one method declaration.
2. Comparison of interface InvocationHandler (in jdk) and interface MethodInterceptor (in 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; } In terms of parameter composition, the input parameters of methodInterceptor are 1 more than Invocationhandler. In fact, the meaning of the first three parameter objects is the same as that of Invocationhandler.
The first parameter indicates which object the call method comes from;
The second parameter represents the Method object that calls the method;
The third parameter represents the input parameter list for this call;
The extra parameters are of MethodProxy type, which should be an object generated by cglib to replace the Method object. Using MethodProxy will improve efficiency than calling the JDK's own Method's own method directly.
3. Realize 1
Implementation of MethodInterceptor interface
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 + "start, parameters: "+Arrays.asList(args)); //Object result = method.invoke(real, args); // Both methods can get Object result = proxy.invoke(real, args); System.out.println("the method: "+ methodName+" ends, result: " + result); return result; }}Create a proxy class and call a proxy class
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); }}Note that MethodProxy provides 2 methods when executing functions.
public Object invoke (Object obj, Object[] args) throws Throwable public Object invokeSuper(Object obj, Object[] args) throws Throwable
Among them, the javadoc says that this invoke() method can be used for the execution of other objects in the same class, that is, the obj in this method needs to pass into another object of the same class (the above method is to pass into different objects of the Arithmetic class), otherwise it will enter an infinite recursive loop (StackOverflowError really appears after the test). If you think about it carefully, you will find that the target in public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) is an implemented proxy object. When the add() method is called through the target, the intercept() method will be triggered to be called. If method.invoke(target, args) is called in the intercept() method, it is equivalent to calling the add() method again, resulting in an infinite recursive loop. But if you execute method.invoke(real, args) it will not, because real and target are different objects in the same class, real is the real logical topic, and target is the proxy for real topic real.
Here is an example to simulate:
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());//Correct//target.setObject(target);//Cyclic call occurs target.solve(); }}In fact, the method's invoke() method will call the corresponding solve() method according to the obj type, that is, polymorphism.
4. Realize 2
Implementation of MethodInterceptor interface
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(method); System.out.println("the method: " + methodName + "Start, parameters: "+Arrays.asList(args)); // Print class information: target.getClass(); omit Object result = proxy.invokeSuper(target, args); System.out.println("the method: "+methodName+" ends, result: " + result); return result; }}Create a proxy class and call a proxy class
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); }}Note that Enhancer does not set the interface in Implementation 2, because Superclass is set (that is, the parent class of the proxy class is Arithmetic), our proxy class will inherit it, and Arithmetic has implemented our interface. To prove this, you can print the class information of target.getClass() in the intercept method of MethodInterceptor. You will find that the parent class of cglib proxy class is different. as follows:
Implementation 1:
public class com.test.Arithmetic$$EnhancerByCGLIB$$4fa786eb extends java.lang.Object
Implementation 2:
public class com.test.Arithmetic$$EnhancerByCGLIB$$4fa786eb extends com.test.Arithmetic
Method 3: javassist generates dynamic proxy (agent factory creation or dynamic code creation)
Javassist is a framework for editing bytecode, which allows you to operate bytecode very simply. It can define or modify Class during runtime. The principle of implementing AOP using Javassist is to directly modify the method that needs to be cut into before the bytecode is loaded. This is more efficient than using Cglib to implement AOP, and there are no many restrictions. The implementation principle is as follows:
Implementation 1:
Implementation of interfaces
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(method); System.out.println("the method: " + methodName + "Start, Parameters: "+Arrays.asList(args)); Object result = proxy.invoke(target, args); System.out.println("the method: "+methodName+" ends, result: " + result); return result; }}Create a proxy class and call a proxy class
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); }}Note: The definition of the invoke method in the MethodHandler interface is as follows:
public Object invoke(Object target, Method method, Method proxy, Object[] args)
method represents the Method object that calls the method, proxy is the object that generates and replaces the method by proxy class. Otherwise, using method.invoke(target, args) will generate an infinite loop call.
Implementation 2:
The common proxy process of using dynamic javassist is slightly different from the previous method. Inside javassist, dynamic java code can be generated to generate bytecode. Dynamic proxy created in this way can be very flexible and can even generate business logic at runtime.
//Custom interceptor interface InterceptorHandler { /** * Calling the method of dynamic proxy object will reflect this method. You can add AOP-like pre- and post-operations to the implementation of this method. Only if the following code is added to this method body* The proxy method will be executed, and the return value will be returned to the proxy and finally returned to the program* @param obj Object The proxy object* @param method Method The proxy object* @param args Object[] Parameters of the proxy object* @return Object The return value after the execution of the proxy object's method* @throws Throwable */ public Object invoke(Object obj, Method method, Object[] args) throws Throwable; } //Implementation of interceptor 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(method); System.out.println("the method: " + methodName + "Start, parameters: "+Arrays.asList(args)); Object result = method.invoke(obj, args); System.out.println("the method: "+methodName+" ends, result: " + result); return result; }}class MyProxyImpl { /** Class name suffix of dynamic proxy class*/ private final static String PROXY_CLASS_NAME_SUFFIX = "$MyProxy_"; /** Interceptor interface*/ private final static String INTERCEPTOR_HANDLER_INTERFACE = "com.test.InterceptorHandler"; /** The class name index of the dynamic proxy class to prevent duplication of class names */ private static int proxyClassIndex = 1; /** * The dynamic proxy interface exposed to the user, returns the dynamic proxy object of a certain interface. Note that the implementation of this proxy must be used with the com.cuishen.myAop.InterceptorHandler interceptor*, that is, if the user wants to use this dynamic proxy, he must first implement the com.cuishen.myAop.InterceptorHandler interceptor interface* @param interfaceClassName String The interface class name to be dynamic proxy, eg test.StudentInfoService * @param classToProxy String The class name of the implementation class of the interface to be dynamically proxyed, eg test.StudentInfoServiceImpl * @param interceptorHandlerImplClassName String The class name of the implementation class of the interceptor interface provided by the user* @return Object Returns the dynamic proxy object of a certain interface* @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); } /** * Dynamic implementation of the interface to be proxyed* @param classToProxy String The class name of the implementation class of the interface to be dynamically proxyed, eg test.StudentInfoServiceImpl * @param interfaceClass Class Interface class to be dynamically proxyed, eg test.StudentInfoService * @param interceptorHandlerImplClass Class User-provided Implementation Class of Interceptor Interface* @return Object Returns the dynamic proxy object of a certain interface* @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(); //Dynamicly specify the class name of the proxy class String proxyClassName = interfaceName + PROXY_CLASS_NAME_SUFFIX + proxyClassIndex++; //The package name of the interface to be implemented + interface name 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(); } /** * Method in dynamic implementation of the method* @param classToProxy String The class name of the implementation class of the interface to be dynamically proxyed, eg test.StudentInfoServiceImpl * @param implementer CtClass The packaging of the dynamic proxy class* @param methodToImpl Method The packaging of the interface method to be implemented in the dynamic proxy class* @param interceptorClass Class The interceptor implementation class provided by the user* @param methodIndex int Index of the method to be implemented* @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); } /** * Dynamically assemble the method body. Of course, the method implementation in the proxy is not a simple method copy, but reflects the invoke method in the interceptor and passes the received parameters* @param classToProxy String The class name of the implementation class of the interface to be dynamically proxyed, eg test.StudentInfoServiceImpl * @param methodToImpl Method The wrapper of the interface method to be implemented in the dynamic proxy class* @param interceptorClass Class The user-provided interceptor implementation class* @param methodIndex int index of the method to be implemented* @return String The string of the dynamic assembly method*/ 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 declaration of assembly method 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(); // parameter list of assembly method for(int i = 0; i < parameters.length; i++) { Class parameter = parameters[i]; String parameterType = parameter.getName(); //Dynamic specifying the variable name of the method parameter 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(); //Method declaration, since it is a method that implements the interface, it is public methodDeclare.append("public ").append(methodReturnType).append(" ").append(methodName).append("(").append(parameterBuffer).append(")").append(exceptionBuffer).append(" {/n"); String interceptorImplName = interceptorClass.getName(); //Methodbody methodDeclare.append(INTERCEPTOR_HANDLER_INTERFACE).append("interceptor = new ").append(interceptorImplName).append("();/n"); //Reflection call user's interceptor interface methodDeclare.append("Object returnObj = interceptor.invoke(Class.forName(/"" + classToProxy + "/").newInstance(), Class.forName(/"" + classToProxy + "/").getMethods()[" + methodIndex + "], "); //Pass the parameters in the method 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"); //Wrap the return value of the call interceptor 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("return("return("return("return();/n"); else if(methodToImpl.getReturnType().equals(Double.TYPE)) methodDeclare.append("return("return("return("return("return("return();/n"); else if(methodToImpl.getReturnType().equals(Double.TYPE)) methodDeclare.append("return("return("return("return("return("return("return("methodToImpl.getReturnType().equals(Double.TYPE)) methodDeclare.append("return("return("return("return("return("methodToImpl.getReturnType().equals(Double.TYPE)) methodDeclare.append("return("return("return("return("return("methodToImpl.getReturnType().equals(Double.TYPE)) methodDeclare.append("return("return("return("return("methodToImpl.getReturnType().equal ((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(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{ //Respond to the interface class name to be implemented by the proxy class, the class name of the proxy class is required, the user-defined interceptor implementation class class name Object proxyArithmetic = MyProxyImpl.newProxyInstance("com.test.ArithmeticInterface", "com.test.Arithmetic", "com.test.InterceptorHandlerImpl"); ((ArithmeticInterface)proxyArithmetic).add(a, b); ((ArithmeticInterface)proxyArithmetic).sub(a, b); }}Print the code for dynamically implementing the interface as follows:
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();} The above is a detailed introduction to the dynamic proxy implementation mechanism in Java, and I hope it will be helpful to everyone's learning.