Spring Study Notes (6) AOP Eve [1] jdk dynamic proxy instance resolution
JDK dynamic proxy technology
The most common application of dynamic proxy is AOP ). With AOP, we can obtain the methods, objects, input parameters, and response parameters when our program runs on a node, and dynamically add some new method logic before and after method calls, to meet our new needs, such as logging.
There are two common methods for dynamic Proxy: Dynamic proxy Based on JDK reflection technology and dynamic proxy Based on CGLib.
Use reflection technology to create a dynamic proxy
The core of JDK's dynamic Proxy creation is the java. lang. reflect. InvocationHandler interface and the java. lang. reflect. Proxy class. Let's analyze the requirements, take out model examples, and then explain the usage of these two core interfaces/classes based on the examples.
Requirement Analysis:
In the face of a large project, the classes may have been designed to be very huge and bloated, and there may be 10 methods in a class. Now, we need to monitor the performance of each method. The running time of the statistical method. If we start to end the record timestamp directly in each designed class method to calculate the time consumed by the method, there will be the following Disadvantages:
1. our log records are intrusive, and a large number of redundant code is embedded. If you need to modify the code later, you need to modify it for each method, it does not comply with the open and closed design principles, and is easy to maintain and error prone.
2. from the business logic point of view, the Performance Statistics Code has nothing to do with the business functions implemented by our existing classes. If we integrate them together, it will lead to coupling between two unrelated functions, does not comply with the principle of clear responsibilities.
So, is there a way to not modify our original class, but also enhance our class functions? For example, here we add Performance Monitoring for every method of our class? The answer is to use dynamic proxy.
Instance display 1. Define our proxy Interface
package com.proxy.demo1;public interface MyProxy { void method1() throws InterruptedException; void method2() throws InterruptedException; void method3() throws InterruptedException;}
2. Define our proxy object-a huge and bloated "old class"
Package com. proxy. demo1; // This is a "long history" class in our project, with complete functions and many methods. Now we need to implement performance statistics for each method // Our proxy class needs to implement our proxy interface. To some extent, this is also intrusive. But the weakest intrusion. Public class OldClass implements MyProxy {@ Override public void method1 () throws InterruptedException {System. out. println ("processing business logic 1"); Thread. sleep (100); // simulate the process of business logic 4 process System. out. println ("business logic 1 processed");} @ Override public void method2 () throws InterruptedException {System. out. println ("processing business logic 2"); Thread. sleep (200); // simulate the process of business logic 2 process System. out. println ("business logic 2 processed") ;}@ Override public void method3 () thro Ws InterruptedException {System. out. println ("processing business logic 3"); Thread. sleep (300); // simulate the process of business logic 3 process System. out. println ("business logic 3 finished");} // there are many other methods below ..}
3. Define our invokationHandler
Package com. proxy. demo1; import java. lang. reflect. invocationHandler; import java. lang. reflect. method; // here we use generics. Suppose we have many classes that require performance monitoring, you can change the class to be monitored at the generic identifier when creating this class object. // Note that you need to implement the InvocationHandler interface public class in the JDK reflection package. // here we use generics. Assume that many classes require performance monitoring, you can change the class to be monitored at the generic identifier when creating this class object. // Note that you need to implement the InvocationHandler interface in the JDK reflection package.
Implements InvocationHandler {// private E target object to be proxy; public MyInvokationHandler (E target) {this.tar get = target;}/*** @ param: * proxy: proxy instance * method on which the method is called: The method called on the target object. * Args: an array of objects that contain the parameter values of method calls on the proxy instance. If the interface method does not use parameters, null * parameters of the basic type are encapsulated in the appropriate basic package class (such as java. lang. integer or java. lang. boolean. * @ Return the value returned by the method call from the proxy instance. If the declared return type of the interface method is a basic type, * the value returned by this method must be an instance of the corresponding BASIC packaging object class; otherwise, it must be a type that can be assigned to the declared return type. * If the value returned by this method is null and the return type of the interface method is basic, the method call on the proxy instance will throw NullPointerException * otherwise, if the value returned by this method is not compatible with the declared return type of the preceding interface method, ClassCastException will be thrown during method calls on the proxy instance. * @ Throws Throwable-an exception thrown from the method call on the proxy instance. * The type of the exception must be assigned to any exception type declared in the throws clause of the Interface Method * or unchecked exception type java. lang. RuntimeException or java. lang. Error. * If this method throws a checked exception, the exception cannot be assigned to any exception type declared in the throws clause of the interface method. * An UndeclaredThrowableException containing the exception thrown by the proxy instance is thrown. * // @ Override public Object invoke (Object proxy, Method method, Object [] args) throws Throwable {Long beginTime = System. currentTimeMillis (); // record start time // call the method of the target Object and obtain the return value of this method. As the return value of our proxy method (invoke), Object returnValue = method. invoke (target, args); // target is the target class of our method, and args is the method parameter System. out. println ("method" + method. getName () + "Call ended, time consumed" + (System. currentTimeMillis ()-beginTime); return returnValue ;}}
Literally, InvocationHandler is the call processor, where it is a method call processor. More broadly speaking, we can regard it as an interceptor. When we call a method in the proxy class, it will be blocked by MyInvocationHandler and then call our invoke method.
4. Test Method
Package com. proxy. demo1; import java. lang. reflect. invocationHandler; import java. lang. reflect. proxy; public class MainTest {public static void main (String args []) throws InterruptedException {// create our Proxy object OldClass oldClass = new OldClass (); // create our "interceptor" and inject the InvocationHandler = new MyInvocationHandler as the proxy object
(OldClass);/* getProxy returns a proxy instance of the specified interface, which can assign a method call to the specified call handler, here is our custom handler's first parameter-defining the class loader of the proxy class second parameter-the list of interfaces to be implemented by the proxy class third parameter-assigning the call handler for method calls InvocationHandler * /MyProxy myProxy = (MyProxy) proxy. newProxyInstance (MyProxy. class. getClassLoader (), new Class [] {MyProxy. class}, handler); myProxy. method1 (); myProxy. method2 (); myProxy. method3 ();}}
5. Print the result
Processing business logic 1
Business Logic 1 processed
Method method1 call ended, time consumed: 101
Processing business logic 2
Business Logic 2 completed
Method method2 call ended, time consumed: 200
Processing business logic 3
Business logic 3 completed
Method method3 call ended, time consumed: 301
We use the proxy class to call the OldClass method to monitor the performance of all the methods in the OldClass for time consumption statistics. However, we did not embed any related business logic code in the OldClass, the only modification is to implement our proxy interface.
Instance analysis
The core implementation of our method call is to use invocationHandler. In fact, we call the method of the proxy object through the proxy interface, as shown inmyProxy.method1()
We actually call the invoke method in our Custom handler, but in the invoke method, according to the input object (oldClass) and parameters (No parameter is passed here ), the method1 in our oldClass is called again. In addition, through this dynamic proxy, we also needModify our upper-layer interfaceFor example, if I call the method1 method of oldClass in oldClassBoss, create a proxy in our oldClassBoss andmyProxy.method1();
To enhance the original performance monitoring function. This is what we need to be clear about.