In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly explains "what is the dynamic agent mode". The content of the explanation in the article is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought slowly and deeply. Let's study and learn "what is dynamic agent mode"!
Agent mode
Agent pattern (Proxy Pattern) is one of 23 design patterns, which belongs to structural pattern. What he means is that an object itself does not do the actual operation, but through other objects to get the desired results. The advantage of this is that on the basis of the implementation of the target object, you can enhance the additional functional operation, that is, to extend the functionality of the target object.
Here can reflect a very important programming idea: do not change the source code at will, if you need to modify, you can extend the method by proxy.
As shown in the figure above, the user cannot use the target object directly, but constructs a proxy object, which is transferred by the proxy object, which is responsible for invoking the real behavior of the target object, thus returning the results to the user.
In other words, the key point of the agent is the relationship between the proxy object and the target object.
In fact, an agent is just like an agent, for example, you are a star and have a lot of fans. You have a lot of traffic, and often many financiers come to you to discuss cooperation, etc., you must be too busy, because you have to deal with not only talking about cooperation, but also know about talent, filming, maintaining relationships with fans, marketing and so on. To this end, you find a broker, you put him in charge of talking about cooperation with the financier, the broker is very serious and responsible, and he has successfully completed the task, so the financier talks to you about cooperation and becomes the financier and your broker to talk about cooperation. You'll have more time for other things. As shown in the following figure
This is a static agent because the agent (broker) is chosen by you yourself.
But then, as your business gradually expanded, you couldn't choose every broker, so you just handed it over to the agent to do it for you. If you want to be popular in bilibili, then directly ask the agency to help you find an agent in charge of marketing. If you want to maintain your relationship with fans, you can just ask the agency to find some stooges for you. Then the diagram will change to the following
At this time, almost all your work is taken care of by the agency, and you don't know who they send to help you do these things, which depends on the actual situation, because the agency is not only responsible for you as a star, and everyone is good at different areas, so you will not be assigned a corresponding agent until you have actual needs, this situation is called dynamic agency.
Static agent
From the compilation time can determine whether the final execution method can be divided into static proxy and dynamic proxy, let's first demonstrate the dynamic proxy, there is a requirement, the leader wants to add a user to the system, but he does not add it himself, he asked the following programmers to add, let's take a look at the process.
First build a user interface and define a template method to save the user.
Public interface UserDao {void saveUser ();}
Build a user implementation class, which is the real way to perform user operations
Public class UserDaoImpl implements UserDao {@ Override public void saveUser () {System.out.println ("- Save user -");}}
Build a user agent class, which also has a method to save the user, but this method belongs to the agent method, which does not actually save the user, but internally holds a real user object to save the user.
Public class UserProxy {private UserDao userDao; public UserProxy (UserDao userDao) {this.userDao = userDao;} public void saveUser () {System.out.println ("- Agent start -"); userDao.saveUser (); System.out.println ("- Agent end -");}}
Here is the test method.
Public class UserTest {public static void main (String [] args) {UserDao userDao = new UserDaoImpl (); UserProxy userProxy = new UserProxy (userDao); userProxy.saveUser ();}}
Create a new user implementation class (UserDaoImpl) that does not perform user actions. Then create a user agent (UserProxy), execute the user agent user save (saveUser), its internal will call the user implementation class save user (saveUser) method, because we JVM can determine the final execution method at compile time, so the above agent mode is also called static proxy.
The proxy mode has the advantage of non-intrusiveness. If we add any new functions in the future, we can directly add a proxy class and let the proxy class invoke user operations, so that we can add new functions without changing the source code. Then life is good, we can directly add the functionality we want, in this beautiful day, cxuan added user agent, log agent, and so on countless agent classes. But the good times didn't last long. Cxuan found that every time he changed the code, he had to change each proxy class, which was very annoying. Have I wasted my precious time on changing every agent class?
Dynamic agent
JDK dynamic agent
So cxuan went online to ask for help, found a concept called dynamic agent, read through it, and found it interesting, so cxuan modified the code of the static agent, added a user agent for UserHandler, and did a test, as follows
Public class UserHandler implements InvocationHandler {private UserDao userDao; public UserHandler (UserDao userDao) {this.userDao = userDao;} @ Override public Object invoke (Object proxy, Method method, Object [] args) throws Throwable {saveUserStart (); Object obj = method.invoke (userDao, args); saveUserDone (); return obj } public void saveUserStart () {System.out.println ("- start insertion -");} public void saveUserDone () {System.out.println ("- insert complete -");}}
The test classes are as follows
Public static void dynamicProxy () {UserDao userDao = new UserDaoImpl (); InvocationHandler handler = new UserHandler (userDao); ClassLoader loader = userDao.getClass (). GetClassLoader (); Class [] interfaces = userDao.getClass (). GetInterfaces (); UserDao proxy = (UserDao) Proxy.newProxyInstance (loader, interfaces, handler); proxy.saveUser ();}
UserHandler is a user agent class, and the UserDao in the constructor is the real object. By hiding the UserDao in the UserHandler, the real method is executed through the UserDao in the UserHandler.
Classloader, interface array you can think of it as a method tree, each leaf node is a method, through the following proxy.saveUser () to tell JVM which method on the method tree is executed.
User agents are obtained through class loaders, interface arrays, and proxy classes. The saveUser method is equivalent to telling proxy which method you are going to execute in the end. This proxy.saveUser method is not the saveUser method that is executed directly. The final saveUser method is triggered by the invoke method in UserHandler.
The above agent pattern in which the final execution method cannot be determined at compile time and can only be obtained dynamically at run time is called a dynamic proxy.
The advantage of dynamic agents is that they can achieve non-intrusive code extensions and can also enhance methods. In addition, the amount of code can be greatly reduced to avoid the flooding of proxy classes.
So let's now summarize the characteristics of static and dynamic agents.
Static agent
Static proxy classes: created by programmers or generated by third-party tools and then compiled; the .class file of the proxy class already exists before the program runs.
The static agent knows in advance what is being represented.
Static proxy classes usually proxy only one class.
Dynamic agent
Dynamic agents are usually generated dynamically through the reflection mechanism when the program is running.
Dynamic proxy classes usually proxy all classes under the interface.
The dynamic agent does not know what to agent in advance and can only be determined at run time.
The call handler of the dynamic proxy must inherit the InvocationHandler interface in advance and create the proxy class dynamically using the newProxyInstance method in the Proxy class.
In the above code example, we define a UserDao interface, and then we have the implementation class of the UserDaoImpl interface, and what we get through the Proxy.newProxyInstance method is also the implementation class object of UserDao, so this is actually a dynamic proxy based on the interface. Also known as JDK dynamic proxy.
Is this the only dynamic agent technology? Now that it's all asked, of course it's not.
In addition, there are some other proxy technologies, but you need to load additional jar packages, so let's summarize all the proxy technologies and their characteristics.
JDK's dynamic proxy is easy to use and is built into JDK, so there is no need to introduce third-party Jar packages.
CGLIB and Javassist are both advanced bytecode generation libraries, and their overall performance is better than the dynamic proxy that comes with JDK, and it is very powerful.
ASM is a low-level bytecode generation tool, and using ASM is almost like using bytecode programming, requiring the most developers. Of course, it is also a dynamic proxy generation tool with the best performance. However, the use of ASM is very tedious, and the performance is not improved by an order of magnitude. Compared with advanced bytecode generation tools such as CGLIB, the maintainability of ASM programs is poor. If not in the situation where there are stringent requirements on performance, CGLIB or Javassist is recommended.
Let's introduce the use of these dynamic agent tools in turn.
CGLIB dynamic agent
We mentioned above that the JDK dynamic proxy is an interface-based proxy, while the CGLIB dynamic proxy implements the proxy for the class, which mainly generates a subclass for the specified class and overrides the methods in it, that is to say, the CGLIB dynamic proxy is carried out by means of class inheritance-> method rewriting. Let's take a look at the structure of the CGLIB dynamic proxy first.
As shown in the figure above, the proxy class inherits from the target class, and each time the method of the proxy class is called, it is intercepted in the interceptor, and then the method of the target class is called in the interceptor.
Let's demonstrate the use of CGLIB dynamic proxy through an example
First import the jar package related to CGLIB. We use MAVEN method.
Cglib cglib 3.2.5
Then we create a new UserService class to distinguish it from the UserDao and UserDaoImpl above.
Public class UserService {public void saveUser () {System.out.println ("- Save user -");}}
Then we create a custom method interceptor that implements the interceptor class
Public class AutoMethodInterceptor implements MethodInterceptor {public Object intercept (Object obj, Method method, Object [] args, MethodProxy methodProxy) throws Throwable {System.out.println ("- method interception -"); Object object = methodProxy.invokeSuper (obj, args); return object;}}
Here, explain what these parameters mean.
Object obj: obj is an instance of the CGLIB dynamic generation agent class
Method method: Method is the proxied method reference called by the entity class
Objectp [] args: this is the parameter list of the method
MethodProxy methodProxy: this is the reference of the generated proxy class to the method.
For the method called by the methodProxy parameter, there are two choices: invoke () and invokeSuper (). The difference between them is not explained in this article. Interested readers can refer to this article: Cglib source code to analyze the difference between invoke and invokeSuper.
Then we create a test class to test.
Public static void main (String [] args) {Enhancer enhancer = new Enhancer (); enhancer.setSuperclass (UserService.class); enhancer.setCallback (new AutoMethodInterceptor ()); UserService userService = (UserService) enhancer.create (); userService.saveUser ();}
The test class mainly involves the use of Enhancer. Enhancer is a very important class, which allows you to create a Java proxy for non-interface types. Enhancer dynamically creates subclasses of a given class and intercepts all methods of the proxy class. Unlike JDK dynamic proxies, both interfaces and classes work properly.
Both JDK dynamic proxy and CGLIB dynamic proxy hide the real object behind the proxy object in order to achieve the effect of proxy. Unlike JDK dynamic proxies, CGLIB dynamic proxies use Enhancer to create proxy objects, while JDK dynamic proxies use Proxy.newProxyInstance to create proxy objects; another point is that CGLIB can proxy most classes, while JDK dynamic proxies can only proxy classes that implement interfaces.
Javassist Agent
Javassist is a class library that edits bytecode in Java; it enables Java programs to define a new class at run time and modify the class file when JVM loads. The dynamic feature we use most frequently is reflection, and reflection is also the basis of dynamic proxies. The reason we don't mention the effect of reflection on dynamic proxies is that I want to talk about it in detail later. Reflection can find object properties and methods at run time. Modify scope, call methods through method names, and so on. Real-time applications do not frequently use reflection to create, because reflection is expensive, and another feature that is as powerful as reflection is Javaassist.
Let's start with a simple example to demonstrate Javaassist and how Javaassist creates dynamic proxies.
We still use the UserDao and UserDaoImpl mentioned above as the base classes.
We create a new AssistByteCode class with a createByteCode method in it, and the main thing this method does is generate the UserDaoImpl implementation class through bytecode. Let's take a look at its code.
Public class AssistByteCode {public static void createByteCode () throws Exception {ClassPool classPool = ClassPool.getDefault (); CtClass cc = classPool.makeClass ("com.cxuan.proxypattern.UserDaoImpl"); / / set interface CtClass ctClass = classPool.get ("com.cxuan.proxypattern.UserDao"); cc.setInterfaces (new CtClass [] {ctClass}) / / creation method CtMethod saveUser = CtMethod.make ("public void saveUser () {}", cc); saveUser.setBody ("System.out.println (\"-insert user -\ ");); cc.addMethod (saveUser); Class c = cc.toClass (); cc.writeFile (" / Users/mr.l/cxuan-justdoit ");}}
Since this article is not a specific study of Javaassist, we will not study the details too much, but will only focus on some of the more important classes of this framework
ClassPool:ClassPool is a container of CtClass, and a CtClass object is an instance of a class object, which, like the class object, contains properties, methods, and so on.
So what does the above code do? Get the CtClass instance of the interface and abstract class needed by CtClass through ClassPool, then add its own properties and methods through the CtClass instance, and output the binary stream to the root path of the current project through its writeFile. WriteFile is outputted internally using DataOutputStream.
After the stream is written, we open the .class file as follows
Public class UserDaoImpl implements UserDao {public void saveUser () {System.out.println ("- insert user -");} public UserDaoImpl () {}}
You can compare it to the UserDaoImpl above and find that the compiler is basically the same except for adding a public constructor for us.
After this simple example, cxuan shows you how to use the Javaassist dynamic proxy.
First, let's create an agent factory for Javaassist with the following code
Public class JavaassistProxyFactory {public Object getProxy (Class clazz) throws Exception {/ / Agent factory ProxyFactory proxyFactory = new ProxyFactory (); / / set the subclass proxyFactory.setSuperclass (clazz) to be created; proxyFactory.setHandler ((self, thisMethod, proceed, args)-> {System.out.println ("- start intercepting -") Object result = proceed.invoke (self, args); System.out.println ("- end intercept--"); return result;}); return proxyFactory.createClass (). NewInstance ();}}
Above we define a proxy factory, and a handler is created in the proxy factory. When the target method is called, the Javassist will call back the MethodHandler API method interception to call the actual execution method. You can implement your own business logic before and after the interception method. The final proxyFactory.createClass (). NewInstance () uses bytecode techniques to create the final subclass instance, which is similar to the InvocationHandler interface in JDK.
The test method is as follows
Public static void main (String [] args) throws Exception {JavaassistProxyFactory proxyFactory = new JavaassistProxyFactory (); UserService userProxy = (UserService) proxyFactory.getProxy (UserService.class); userProxy.saveUser ();}
ASM Agent
ASM is a set of Java bytecode generation architecture, which can dynamically generate subclasses or other proxy classes in binary format, or dynamically modify classes before they are loaded into memory by the Java virtual machine.
Let's use the ASM framework to implement a dynamic proxy, a dynamic proxy generated by ASM.
The following code is excerpted from https://blog.csdn.net/lightj1996/article/details/107305662
Public class AsmProxy extends ClassLoader implements Opcodes {public static void createAsmProxy () throws Exception {/ / the class modifier in the bytecode of the target class name is divided by "/" String targetServiceName = TargetService.class.getName () .replace (".", "/"); / / the facet class name String aspectServiceName = AspectService.class.getName () .class (".", "/") / / proxy class name String proxyServiceName = targetServiceName+ "Proxy"; / / create a classWriter that inherits ClassVisitor ClassWriter classWriter = new ClassWriter (0); / / access class specifies jdk version number 1.8, modifier public, parent class TargetService classWriter.visit (Opcodes.V1_8, Opcodes.ACC_PUBLIC, proxyServiceName, null, targetServiceName, null) / / access the target class member variable to add the aspect attribute "private TargetService targetService" classWriter.visitField (ACC_PRIVATE, "targetService", "L" + targetServiceName+ ";", null, null) to the class; / / access the aspect class member variable to add the target attribute "private AspectService aspectService" classWriter.visitField (ACC_PRIVATE, "aspectService", "L" + aspectServiceName+ ";", null, null) to the class / / access the default constructor TargetServiceProxy () / / define the function modifier as public method name, method descriptor () V means no parameter, no return parameter MethodVisitor initVisitor = classWriter.visitMethod (ACC_PUBLIC, "", "() V", null, null); / / take the 0th element "this" initVisitor.visitVarInsn (ALOAD, 0) from the local variable table / / calling super's constructor invokeSpecial here means calling the parent class method initVisitor.visitMethodInsn (INVOKESPECIAL, targetServiceName, "() V", false); / / the method returns initVisitor.visitInsn (RETURN); / / sets the maximum number of stacks, the maximum number of local variables initVisitor.visitMaxs (1,1) / / end of access initVisitor.visitEnd (); / / create parametric constructor TargetServiceProxy (TargetService var1, AspectService var2) / / define the function modifier as public method name, method descriptor (TargetService, AspectService) V means no parameter, no return parameter MethodVisitor methodVisitor = classWriter.visitMethod (ACC_PUBLIC, "(L" + targetServiceName + "; L" + aspectServiceName+ ") ) V ", null, null); / / take the 0th element" this "from the local variable table and press it into the top stack methodVisitor.visitVarInsn (ALOAD, 0); / / this out of the stack, calling super's constructor invokeSpecial here means calling the parent class method. Owner is AspectService, no parameter and no return type methodVisitor.visitMethodInsn (INVOKESPECIAL, targetServiceName, "", "() V", false); / / take the 0th element "this" from the local variables table and press it into the top stack methodVisitor.visitVarInsn (ALOAD, 0); / / take the first element "targetService" from the local variables table and press it into the top stack methodVisitor.visitVarInsn (ALOAD, 1) / / this and targetService out of the stack, call targetService put to assign values to this.targetService methodVisitor.visitFieldInsn (PUTFIELD, proxyServiceName, "targetService", "L" + targetServiceName + ";"); / / take the 0th element "this" from the local variable table and press it into the top stack methodVisitor.visitVarInsn (ALOAD, 0) / / take the second element "aspectService" from the local variables table and press it into the top stack methodVisitor.visitVarInsn (ALOAD, 2); / / this and aspectService exit the stack and assign targetService put to this.aspectService methodVisitor.visitFieldInsn (PUTFIELD, proxyServiceName, "aspectService", "L" + aspectServiceName + ";"); / / the method returns methodVisitor.visitInsn (RETURN) / / set the maximum number of stacks, the maximum number of local variables methodVisitor.visitMaxs (2,3); / / the method returns methodVisitor.visitEnd (); / / create the proxy method modifier public, and the method name is demoQuest MethodVisitor visitMethod = classWriter.visitMethod (ACC_PUBLIC, "demoQuest", "() I", null, null) / / take the 0th element "this" from the local variables table and press it into the top of the stack visitMethod.visitVarInsn (ALOAD, 0); / / this push the this.aspectService into the top of the stack visitMethod.visitFieldInsn (GETFIELD, proxyServiceName, "aspectService", "L" + aspectServiceName+ ";") / / take the top element out of the stack, that is, targetService calls its preOperation method. The owner of demoQuest is AspectService, and no parameter and no return type visitMethod.visitMethodInsn (INVOKEVIRTUAL, aspectServiceName, "preOperation", "() V", false); / / take the 0th element "this" from the local variable table and press it into the top of the stack visitMethod.visitVarInsn (ALOAD, 0). / / this out of the stack, take the this.targetService and press it into the top visitMethod.visitFieldInsn (GETFIELD, proxyServiceName, "targetService", "L" + targetServiceName+ ";"); / / take the top element out of the stack, that is, targetService calls its demoQuest method, the owner of demoQuest is TargetService, and there is no parameter and no return type visitMethod.visitMethodInsn (INVOKEVIRTUAL, targetServiceName, "demoQuest", "() I", false). / / method returns visitMethod.visitInsn (IRETURN); / / sets the maximum number of stacks, maximum number of local variables visitMethod.visitMaxs (1,1); / / method returns visitMethod.visitEnd (); / / generates bytecode binary stream byte [] code = classWriter.toByteArray () / / Custom classloader load class Class clazz = (new AsmProxy ()) .defineClass (TargetService.class.getName () + "Proxy", code, 0, code.length); / / take its constructor with parameters Constructor constructor = clazz.getConstructor (TargetService.class, AspectService.class); / / instantiate the object Object object = constructor.newInstance (new TargetService (), new AspectService ()) using the constructor / / use a reference of type TargetService to receive this object TargetService targetService; if (! (object instanceof TargetService)) {return;} targetService = (TargetService) object; System.out.println ("generate proxy class name:" + targetService.getClass (). GetName ()); / / call the proxied method targetService.demoQuest () / / you don't have to write here, but if you want to see what the final generated bytecode looks like, you can write "ascp-purchase-app/target/classes/" is my root directory, and readers need to replace it with their own String classPath = "/ Users/mr.l/cxuan-justdoit/"; String path = classPath + proxyServiceName + ".class"; FileOutputStream fos = new FileOutputStream (path) Fos.write (code); fos.close ();}}
The code for generating a dynamic proxy using ASM is relatively long. The meaning of the above code is to generate the class TargetServiceProxy, which is used for proxy TargetService, and calls the faceted method aspectService.preOperation () before calling the targetService.demoQuest () method.
The test class just calls the AsmProxy.createAsmProxy () method directly, which is relatively simple.
Here is the target class from which we generate TargetServiceProxy
So far, we have introduced four ways of dynamic agent, namely, JDK dynamic agent, CGLIB dynamic agent, Javaassist dynamic agent, ASM dynamic agent, so now we are thinking about a question, why is there a dynamic agent? Or what is the principle of dynamic agent?
In fact, as we have mentioned above, yes, dynamic agents use reflection mechanism, which is a basic function provided by Java language. Give the program the ability to dynamically modify properties and methods at run time. Through reflection, we can directly manipulate classes or objects, such as getting the definition of a class, getting the properties and methods of a class, and so on.
For more information about Java reflection, please refer to this article by Java Builder.
After telling the female colleague about the agency, the female colleague said, "you are great."
In addition, it should be noted that from a performance point of view, some people have concluded that Java dynamic agents are dozens of times slower than CGLIB and Javaassist. In fact, in mainstream JDK versions, Java dynamic agents can provide the same level of performance, and the gap of magnitude is not widespread. Moreover, reflection has been improved and optimized in modern JDK.
In our selection, performance considerations are not the main concern, reliability, maintainability and coding workload are equally important.
Thank you for your reading. the above is the content of "what is the dynamic agent mode". After the study of this article, I believe you have a deeper understanding of what is the dynamic agent mode. The specific use of the situation also needs to be verified by practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
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.