In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-27 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article focuses on "how to understand the Java agent mechanism", interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn how to understand the Java agent mechanism.
How is reflection used in dynamic agents? The mental map of the full text is as follows:
1. General coding before learning the agent, let's review our general coding: all variables of type interface are always transformed upward and point to an instance.
1) first, define an interface:
Public interface SmsService {String send (String message);}
2) then write its implementation class:
Public class SmsServicseImpl implements SmsService {public String send (String message) {System.out.println ("send message:" + message); return message;}}
3) finally, create an instance of the implementation class, transform it into an interface, and call:
SmsService s = new SmsServicseImpl (); s.send ("Java")
This is the way we usually write code. There is a big difference between the agency mode and this way, as shown below.
two。 Overview of Agent Model
To put it simply, the proxy pattern is to use the proxy object instead of the access to the real object, so that it can provide additional functional operations and extend the function of the target object without modifying the original target object. "
There are roughly three roles in the agent model:
Real Subject: real class, that is, delegated class and delegated class. Used to really complete business service functions
Proxy: proxy class. Realize your own request with the corresponding function of Real Subject, and the proxy class object does not really realize its business function.
Subject: defines the interface that both the RealSubject and Proxy roles should implement.
Generally speaking, "the main function of the proxy mode is to extend the function of the target object, for example, you can add some additional operations before and after the execution of a method of the target object, and you do not have to modify the original code of the method." If you have learned the AOP of Spring, you will certainly be able to understand this sentence very well.
For example: you found Xiao Hong to help you question Xiao Lu, Xiao Hong is regarded as my agent class Proxy, and you are Real Subject, because what Xiao Hong wants to convey is actually what you said. Then the Subject that you and Xiao Hong both need to implement is to talk. Since you both can talk, you both look the same to the outside world (funny, just understand it, don't take it seriously)
Seeing here, I wonder if you can understand why both delegate classes and proxy classes need to implement the same interface.
That's to keep the behavior consistent, and there's no difference between the two from the visitor's point of view. In this way, through the middle layer of the proxy class, the delegate class object is well hidden and protected, and the external direct access to the delegate class object can be effectively shielded. At the same time, you can also add additional operations to the agent class, such as "Xiao Hong will dance before speaking, and the outside world will think that you will dance before speaking, so this realizes the functional enhancement of the delegate class."
There are two ways to realize the agent mode: static agent and dynamic agent.
3. Static agent
What is a static agent
Let's first take a look at the implementation steps of a static proxy:
1) define an interface (Subject)
2) create a Real Subject class to implement this interface
3) create a proxy class (Proxy) that also implements this interface
4) "inject the delegate class Real Subject into the proxy class Proxy" and call the corresponding method in Real Subject in the method of the proxy class. In this way, we can block access to the target object through the proxy class and do what we want to do before and after the execution of the target method.
From the perspective of implementation and application, in static proxies, our enhancements to each method of the target object are done manually and are very inflexible (for example, once new methods are added to the interface, both the target object and the proxy object have to be modified) and troublesome (you need to write a separate proxy class for each target class). There are very few practical application scenarios, and there are few scenarios in which static proxies are used in daily development.
At the JVM level, "static proxies turn interfaces, delegate classes, and proxy classes into actual .class files at compile time. "
Code example
1) define the interface for sending SMS messages
Public interface SmsService {String send (String message);}
2) create a Real Subject class to implement this interface
Public class SmsServiceImpl implements SmsService {public String send (String message) {System.out.println ("send message:" + message); return message;}}
3) create a proxy class (Proxy) that also implements this interface
4) inject the delegate class Real Subject into the proxy class Proxy, and call the corresponding method in Real Subject in the method of the proxy class. In this way, we can block access to the target object through the proxy class and do what we want to do before and after the execution of the target method.
Public class SmsProxy implements SmsService {/ / before injecting the delegate class into the proxy class private final SmsService smsService; public SmsProxy (SmsService smsService) {this.smsService = smsService;} @ Override public String send (String message) {/ / call the delegate class method, we can add our own operation System.out.println ("before method send ()"). / / call the delegate class method smsService.send (message); / / after calling the delegate class method, we can also add our own operation System.out.println ("after method send ()"); return null;}}
So, how do you use this enhanced send method?
Public class Main {public static void main (String [] args) {SmsService smsService = new SmsServiceImpl (); SmsProxy smsProxy = new SmsProxy (smsService); smsProxy.send ("Java");}}
After running the above code, the console prints out:
Before method send () send message:java after method send ()
As you can see from the output, we have enhanced the send () method of the delegate class SmsServiceImpl.
Of course, we can also see from the above code that static proxies have some drawbacks. If we now have a new delegate class that implements the SmsService interface, if we want to enhance the delegate class, we need to write a new delegate class and then inject the new delegate class, which is very inflexible. In other words, a static proxy is a delegated corresponding to a proxy class, can it be "made into a generic proxy class"? For this reason, the application of dynamic agent is born.
4. Java bytecode generation framework
Before we talk about dynamics, we need to talk about .class bytecode files in detail. The dynamic proxy mechanism is closely related to the Java bytecode generation framework.
In the reflection above, we mentioned that a Class class corresponds to a .class bytecode file, which means that all the information about a class is stored in the bytecode file. Bytecode is actually a binary file that contains machine code that only JVM can recognize.
The parsing process goes like this: JVM reads the .class bytecode file, takes out the binary data, loads it into memory, parses the information in the bytecode file, and generates the corresponding Class class object:
Obviously, this process takes place at compile time.
So, because JVM loads the class through a .class bytecode file (that is, binary information), if we follow the format and structure of the .class bytecode file organized by the Java compilation system at run time, generate the corresponding binary data, and then load the binary data into the corresponding class. In this way, we are done creating a class dynamically at run time. This idea is actually the idea of dynamic agent.
At run time, the corresponding binary data is generated according to the organization rules of .class bytecode files according to the JVM specification. Currently, there are many open source frameworks that can accomplish this function, such as
ASM
CGLIB
Javassist
.
It should be noted that "CGLIB is based on ASM". Here's a brief comparison between ASM and Javassist:
Javassist source code-level API is easier to use than actual bytecode operations in ASM.
Javassist provides a higher level of abstraction for complex bytecode-level operations. Javassist source-level API requires little bytecode knowledge, or even any actual bytecode knowledge, so it is easier and faster to implement.
Javassist uses reflection, which makes it slower than ASM.
"overall, ASM is much faster than Javassist and provides better performance, but Javassist is relatively easy to use." each has its own advantages.
Taking Javassist as an example, let's take a look at the powerful ability of these frameworks to generate .class bytecode files at run time.
Normally, our code to create a class looks like this:
Package com.samples; public class Programmer {public void code () {System.out.println ("I am a Programmer,Just Coding.");}}
The following bytecode of the same Programmer class is created through Javassist:
Import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.CtNewMethod; public class MyGenerator {public static void main (String [] args) throws Exception {ClassPool pool = ClassPool.getDefault (); / / create Programmer class CtClass cc= pool.makeClass ("com.samples.Programmer"); / / define method CtMethod method = CtNewMethod.make ("public void code () {}", cc) / / insert the method code method.insertBefore ("System.out.println (\" Iyma Programmer,Just Coding.\ "); cc.addMethod (method); / / Save the generated bytecode cc.writeFile (" d://temp ");}}
Open Programmer.class through the decompiler and you can see the following code:
It's so scary!
5. What is a dynamic proxy OK? now that you understand the Java bytecode generation framework, you can start to learn about dynamic proxy (Dynamic Proxy).
Looking back at the static agent, we abstract the execution process of the static agent into the following figure:
As you can see, the proxy class simply adds some operations before and after the delegate class method is called. The difference of delegate class leads to the difference of proxy class.
So in order to make a universal proxy class, we extract the action of calling the delegate class method and encapsulate it into a universal processing class, so we have the InvocationHandler role (processing class) in the dynamic proxy.
Therefore, there is a role of handling class between the proxy class and the delegate class, which is mainly "a unified call to the action of the proxy class calling the delegate class method", that is, the InvocationHandler uniformly handles the operation of the proxy class calling the delegate class method. Look at the following picture:
From a JVM perspective, a dynamic agent dynamically generates a .class bytecode file at run time and loads it into JVM. This has been mentioned in the Java bytecode generation framework.
Although dynamic agents are rarely used in our daily development, it is almost a necessary technology in the framework. After learning the dynamic agent, it is also very helpful for us to understand and learn the principles of various frameworks. "the implementation of Spring AOP, RPC and other frameworks all rely on dynamic agents."
As far as Java is concerned, there are many ways to implement dynamic agents, such as:
JDK dynamic agent
CGLIB dynamic agent
Javassit dynamic agent
.
These three dynamic proxy mechanisms are explained in detail below.
6. JDK dynamic proxy mechanism
Use steps
Let's first take a look at the steps for using the JDK dynamic proxy mechanism:
1) define an interface (Subject)
2) create a Real Subject class to implement this interface
3) create a processing class and implement the InvocationHandler interface, override its invoke method (call the method of the delegate class using the reflection mechanism in the invoke method, and customize some processing logic), and inject the delegate class into the processing class
This method has the following three parameters:
Proxy: proxy class object (see next)
Method: remember the Method.invoke we talked about in the reflection of the last article? This is it, through which we can call the method of the delegate class (reflection).
Args: a list of parameters passed to delegate class methods
4) create a proxy object (Proxy): create a proxy object of the delegate class object through Proxy.newProxyInstance ()
This method requires three parameters:
Classloader ClassLoader
An array of interfaces implemented by the delegate class, at least one interface needs to be passed in
The called InvocationHandler instance handles the interface method (that is, the instance of the class we created in step 3)
In other words, when the proxy object created through the newProxyInstance () of the Proxy class calls the method, it actually calls the invoke () method of the processing class that implements the InvocationHandler interface, and you can customize the processing logic in the invoke () method, such as what to do before and after the method execution.
Code example
1) define an interface (Subject)
Public interface SmsService {String send (String message);}
2) create a Real Subject class to implement this interface
Public class SmsServiceImpl implements SmsService {public String send (String message) {System.out.println ("send message:" + message); return message;}}
3) create a processing class and implement the InvocationHandler interface, override its invoke method (call the method of the delegate class using the reflection mechanism in the invoke method, and customize some processing logic), and inject the delegate class into the processing class
Import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class DebugInvocationHandler implements InvocationHandler {/ / injects the delegate class into the processing class (here we use Object instead of easy to extend) private final Object target; public DebugInvocationHandler (Object target) {this.target = target } / / override the invoke method @ Override public Object invoke (Object proxy, Method method, Object [] args) throws InvocationTargetException, IllegalAccessException {/ / before calling the method, we can add our own operation System.out.println ("before method" + method.getName ()); Object result = method.invoke (target, args) / / after calling the method, we can also add our own operation System.out.println ("after method" + method.getName ()); return result;}}
4) define a factory class that creates a proxy object (Proxy): create a proxy object of the delegate class object through Proxy.newProxyInstance ()
Public class JdkProxyFactory {public static Object getProxy (Object target) {return Proxy.newProxyInstance (target.getClass () .getClassLoader (), target.getClass () .getInterfaces (), new DebugInvocationHandler (target));}}
5) practical use
SmsService smsService = (SmsService) JdkProxyFactory.getProxy (new SmsServiceImpl ()); smsService.send ("Java")
After running the above code, the console prints out:
Before method send send message:Java after method send
7. CGLIB dynamic proxy mechanism
Use steps
"one of the deadliest problems with JDK dynamic proxy is that it can only proxy the implementation class of an interface, and the proxy class can only proxy the methods implemented in the interface. If the implementation class has its own private method and the interface does not have it, the method cannot make proxy calls."
To solve this problem, we can use the CGLIB dynamic proxy mechanism.
As mentioned above, CGLIB (Code Generation Library) is an ASM-based Java bytecode generation framework that allows us to modify and generate bytecode dynamically at run time. The principle is to "generate a subclass through bytecode technology, intercept calls to the parent method in the subclass, and weave additional business logic". Key words have you noticed, intercept! CGLIB introduces a new role is the "method interceptor" MethodInterceptor. Similar to the processing class InvocationHandler in JDK, it is also used to implement the unified call of methods. Look at the following picture:
In addition, because CGLIB adopts the way of "inheritance", the class being proxied cannot be modified by final.
Many well-known open source frameworks use CGLIB, such as "AOP module in Spring: if the target object implements an interface, it defaults to JDK dynamic proxy, otherwise it uses CGLIB dynamic proxy".
Let's take a look at the steps for using the CGLIB dynamic proxy:
1) first create a delegate class (Real Subject)
2) create a method interceptor to implement the interface MethodInterceptor, and override the intercept method. Methods used by intercept to intercept and enhance delegate classes (similar to the invoke method in JDK dynamic proxy InvocationHandler)
The method has four parameters:
Object var1: delegate class object
Method var2: intercepted methods (methods that need to be enhanced in the delegate class)
Object [] var3: method input parameter
MethodProxy var4: the original method used to call the delegate class (the underlying layer is also through the reflection mechanism, but not Method.invoke, but the MethodProxy.invokeSuper method)
3) create a proxy object (Proxy): create a proxy object of the delegate class object through Enhancer.create ()
In other words, when the proxy object created through the create () of the Enhancer class calls the method, it actually calls the intercept () method of the processing class that implements the MethodInterceptor interface, and you can customize the processing logic in the intercept () method, such as what to do before and after the method execution.
❝can find that the steps of CGLIB dynamic proxy mechanism are similar to those of JDK dynamic proxy mechanism. The core of CGLIB dynamic proxy is method interceptor MethodInterceptor and Enhancer, while the core of JDK dynamic proxy is to deal with class InvocationHandler and Proxy. ❞
Code example
Unlike JDK dynamic proxies, no additional dependencies are required. CGLIB is an open source project, and you need to add related dependencies manually if you want to use it.
Cglib cglib 3.3.0
1) first create a delegate class (Real Subject)
Public class AliSmsService {public String send (String message) {System.out.println ("send message:" + message); return message;}}
2) create a method interceptor to implement the interface MethodInterceptor, and override the intercept method
Import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class DebugMethodInterceptor implements MethodInterceptor {@ Override public Object intercept (Object o, Method method, Object [] args, MethodProxy methodProxy) throws Throwable {/ / before calling the method, we can add our own operation System.out.println ("before method" + method.getName ()) / / call the delegate class's method Object object = methodProxy.invokeSuper (o, args) through reflection; / / after calling the method, we can also add our own operation System.out.println ("after method" + method.getName ()); return object;}}
3) create a proxy object (Proxy): create a proxy object of the delegate class object through Enhancer.create ()
Import net.sf.cglib.proxy.Enhancer; public class CglibProxyFactory {public static Object getProxy (Class clazz) {/ / create dynamic proxy enhancement class Enhancer enhancer = new Enhancer (); / / set class loader enhancer.setClassLoader (clazz.getClassLoader ()); / / set delegate class (set parent class) enhancer.setSuperclass (clazz) / / set method interceptor enhancer.setCallback (new DebugMethodInterceptor ()); / / create proxy class return enhancer.create ();}}
❝We can see from setSuperclass why CGLIB is based on inheritance. ❞
4) practical use
AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy (AliSmsService.class); aliSmsService.send ("Java")
After running the above code, the console prints out:
Before method send send message:Java after method send
Comparison between JDK dynamic Agent and CGLIB dynamic Agent
1) the JDK dynamic proxy is based on the delegate class that implements the interface, while the CGLIB dynamic proxy is based on the subclass that inherits the delegate class and implements the proxy through the subclass.
2) JDK dynamic proxy can only proxy classes that implement interfaces and can only enhance existing methods in interfaces, while CGLIB can proxy classes that do not implement any interfaces.
3) in terms of efficiency, in most cases, the JDK dynamic agent is more efficient, and this advantage becomes more obvious with the upgrade of the JDK version.
❝mentioned that the "Javassist dynamic proxy mechanism" is also common. Like CGLIB, as a Java bytecode generation framework, Javassist inherently has the ability to dynamically create a class at run time, so it's easy to implement dynamic proxies. Dubbo uses Javassit as a dynamic proxy by default. ❞
8. Under what circumstances do you use dynamic agents
1) one of the design principles in the design pattern is the "open-close principle", that is, "closed for modification, open for extension". We sometimes take over a lot of previous code in our work, in which the logic of the code is confusing. It is difficult to modify the code, so we can enhance the class through agents.
2) when we use the "RPC framework", the framework itself does not know in advance which methods of which interfaces will be called by each business party. At this time, we can establish a middleman for the client by means of dynamic proxy, and it is also convenient for the framework to build logic. to some extent, it is also a manifestation of the loose coupling between the client code and the framework.
3) the "AOP of Spring" mechanism also uses dynamic proxies, which are not discussed in detail here.
9. Comparison between static agent and dynamic agent
1) "flexibility": dynamic proxies are more flexible, do not need to implement interfaces, can directly proxy implementation classes, and do not need to create a proxy class for each target class. In addition, in static proxies, once new methods are added to the interface, both the target object and the proxy object have to be modified, which is very troublesome.
2) "JVM level": static agents turn interfaces, implementation classes, and proxy classes into actual .class bytecode files at compile time. The dynamic agent generates class bytecode dynamically at run time and loads it into JVM.
At this point, I believe you have a deeper understanding of "how to understand the Java agent mechanism". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.