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/02 Report--
This article shares with you the content of the sample analysis of the principles of JDK and cglib dynamic agents in Spring. The editor thinks it is very practical, so share it with you as a reference and follow the editor to have a look.
Introduction to Java Agent
The implementation of agents in Java is generally divided into three types: JDK static agent, JDK dynamic agent and CGLIB dynamic agent. In the AOP implementation of Spring, JDK dynamic agent and CGLIB dynamic agent are mainly applied. But this article focuses on the JDK dynamic proxy mechanism, which will be explored later on by the CGLIB dynamic proxy.
The general pattern of proxy implementation is JDK static proxy: create an interface, then create the class being proxied to implement the interface and implement the abstract methods in the interface. Then create a proxy class that also implements this interface. Hold a reference to the proxied object in the proxy class, and then call the object's method in the proxy class method.
In fact, the proxy class preprocesses the message for the proxied class, filters the message, and then forwards the message to the proxied class, and then carries on the post-processing of the message. There is usually an association between the proxy class and the proxied class (that is, the reference to the object held above). The proxy class itself does not implement the service, but provides the service by calling the method in the proxied class.
Static agent
Interface
Proxied class
Proxy class
Test classes and output results
We can see that it is easy to proxy a class using JDK static proxies. However, the shortcomings of JDK static proxy are also exposed: because the proxy can only serve one class, if there are many classes that need to be proxied, then you need to write a large number of proxy classes, which is more tedious.
Let's use the JDK dynamic proxy to do the same thing.
JDK dynamic agent
Interface
Proxied class
Proxy class
Test classes and output results
Implementation principle of JDK dynamic Agent
The JDK dynamic proxy is actually implemented by the basic interface. Because the polymorphic way of pointing to the implementation class instance through the interface can effectively decouple the specific implementation from the call, which is convenient for later modification and maintenance.
Through the above introduction, we can find that there are some similarities between JDK static proxy and JDK dynamic proxy, such as creating proxy classes and implementing interfaces for proxy classes. But the difference is also obvious-in static proxies, we need to create a proxy class for which interface and which proxied class, so we need the proxy class to implement the same interface as the proxied class before compiling, and directly call the corresponding method of the proxied class in the implemented method. But the dynamic proxy is different. We don't know which interface and which proxied class to create for, because it is created at run time.
Let's summarize the differences between JDK static proxies and JDK dynamic proxies in one sentence, and then explore the underlying implementation mechanism of JDK dynamic proxies:
JDK static proxies are created by direct coding, while JDK dynamic proxies use reflection mechanisms to create proxy classes at run time.
In fact, in dynamic agents, the core is InvocationHandler. Each instance of the agent has an associated call handler (InvocationHandler). When a call is made to a proxy instance, the call to the method is encoded and assigned to the invoke method of its calling handler (InvocationHandler). So the call to the proxy object instance method is done through the invoke method in InvocationHandler, and the invoke method determines which method of the proxy is called based on the passed proxy object, method name, and parameters.
From the test class of the JDK dynamic proxy, we can see that the generation of the proxy class is done through the newProxyInstance in the Proxy class. Let's take a look at this function:
NewProxyInstance public static Object newProxyInstance (ClassLoader loader, Class [] interfaces, InvocationHandler h) throws IllegalArgumentException in the Proxy class {/ / throws an exception Objects.requireNonNull (h) if h is empty; final Class [] intfs = interfaces.clone () / / copy some of the interfaces implemented by the proxy class for subsequent permission checks final SecurityManager sm = System.getSecurityManager (); if (sm! = null) {/ / check some security permissions here to ensure that we have permission to proxy checkProxyAccess (Reflection.getCallerClass (), loader, intfs) to the expected proxy class. } / * * the following method will generate the proxy class * / Class cl = getProxyClass0 (loader, intfs) / * * get the constructor object of the proxy class using the specified call handler * / try {if (sm! = null) {checkNewProxyPermission (Reflection.getCallerClass (), cl);} final Constructor cons = cl.getConstructor (constructorParams); final InvocationHandler ih = h / / if the constructor of the proxy class is private, use reflection to set accessible if (! Modifier.isPublic (cl.getModifiers () {AccessController.doPrivileged (new PrivilegedAction ()) {public Void run () {cons.setAccessible (true); return null }});} / / generate an object of the proxy class according to the constructor of the proxy class and return return cons.newInstance (new Object [] {h});} catch (IllegalAccessException | InstantiationException e) {throw new InternalError (e.toString (), e) } catch (InvocationTargetException e) {Throwable t = e.getCause (); if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError (t.toString (), t);}} catch (NoSuchMethodException e) {throw new InternalError (e.toString (), e);}}
So the proxy class is actually generated through the getProxyClass method:
/ * generate a proxy class, but the permission must be checked * / private static Class getProxyClass0 (ClassLoader loader, Class...) before calling this method Interfaces) {/ / if the number of interfaces is greater than 65535, throw an illegal parameter error if (interfaces.length > 65535) {throw new IllegalArgumentException ("interface limit exceeded");} / / if there is a corresponding proxy class in the cache, then directly return / / otherwise the proxy class will have ProxyClassFactory to create return proxyClassCache.get (loader, interfaces);}
So what is ProxyClassFactory?
/ * there is a factory function that creates a proxy class based on a given ClassLoader and Interface * * / private static final class ProxyClassFactory implements BiFunction > {/ / the name of the proxy class is prefixed with "$Proxy" private static final String proxyClassNamePrefix = "$Proxy" / / each proxy class prefix is followed by a unique number, such as $Proxy0, $Proxy1, $Proxy2 private static final AtomicLong nextUniqueNumber = new AtomicLong (); @ Override public Class apply (ClassLoader loader, Class [] interfaces) {Map interfaceClass = null; try {interfaceClass = Class.forName (intf.getName (), false, loader) } catch (ClassNotFoundException e) {} if (interfaceClass! = intf) {throw new IllegalArgumentException (intf + "is not visible from class loader") } / * * verify that the Class object is an interface * / if (! interfaceClass.isInterface ()) {throw new IllegalArgumentException (interfaceClass.getName () + "is not an interface") } / * * verify whether the interface repeats * / if (interfaceSet.put (interfaceClass, Boolean.TRUE)! = null) {throw new IllegalArgumentException ("repeated interface:" + interfaceClass.getName ()) }} String proxyPkg = null; / / declare the package int accessFlags = Modifier.PUBLIC where the proxy class is located | Modifier.FINAL; / * * record a package of a non-public proxy interface to define the proxy class in the same package. Also verify that all non-public * proxy interfaces are in the same package * / for (Class intf: interfaces) {int flags = intf.getModifiers (); if (! Modifier.isPublic (flags)) {accessFlags = Modifier.FINAL; String name = intf.getName () Int n = name.lastIndexOf ('.'); String pkg = ((n =-1)? ": name.substring (0, n + 1)); if (proxyPkg = = null) {proxyPkg = pkg } else if (! pkg.equals (proxyPkg)) {throw new IllegalArgumentException ("non-public interfaces from different packages") } if (proxyPkg = = null) {/ / if they are all public proxy interfaces, then the generated proxy class is proxyPkg = ReflectUtil.PROXY_PACKAGE + "." under com.sun.proxy package. } / * * generate a name package name + prefix + unique number for the proxy class * such as com.sun.proxy.$Proxy0.class * / long num = nextUniqueNumber.getAndIncrement (); String proxyName = proxyPkg + proxyClassNamePrefix + num / * * generate the bytecode file of the specified proxy class * / byte [] proxyClassFile = ProxyGenerator.generateProxyClass (proxyName, interfaces, accessFlags); try {return defineClass0 (loader, proxyName, proxyClassFile, 0, proxyClassFile.length) } catch (ClassFormatError e) {/ * * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). * / throw new IllegalArgumentException (e.toString ());} bytecode generation
From the above code byte [] proxyClassFile = ProxyGenerator.generateProxyClass (proxyName, interfaces, accessFlags); you can see that the work of generating the proxy class bytecode file is actually done through the generateProxyClass method in the ProxyGenerate class.
Public static byte [] generateProxyClass (final String var0, Class [] var1, int var2) {ProxyGenerator var3 = new ProxyGenerator (var0, var1, var2); / / the method actually used to generate proxy class bytecode files is here final byte [] var4 = var3.generateClassFile () / / Save the bytecode file if (saveGeneratedFiles) {AccessController.doPrivileged (new PrivilegedAction ()) {public Void run () {try {int var1 = var0.lastIndexOf (46); Path var2 If (var1 > 0) {Path var3 = Paths.get (var0.substring (0, var1). Replace ('., File.separatorChar), new String [0]) Files.createDirectories (var3, new FileAttribute [0]); var2 = var3.resolve (var0.substring (var1 + 1, var0.length ()) + ".class");} else {var2 = Paths.get (var0 + ".class", new String [0]) } Files.write (var2, var4, new OpenOption [0]); return null;} catch (IOException var4x) {throw new InternalError ("I var4x O exception saving generated file:" + var4x) });} return var4;}
Let's take a look at the generateClassFile method that is actually used to generate the proxy class bytecode file:
Private byte [] generateClassFile () {/ / the following series of addProxyMethod methods add methods in the interface and methods in Object to the proxy method (proxyMethod) this.addProxyMethod (hashCodeMethod, Object.class); this.addProxyMethod (equalsMethod, Object.class); this.addProxyMethod (toStringMethod, Object.class); Class [] var1 = this.interfaces; int var2 = var1.length; int var3; Class var4 / / get all the methods in the interface and add for (var3 = 0; var3) to the proxy method
< var2; ++var3) { var4 = var1[var3]; Method[] var5 = var4.getMethods(); int var6 = var5.length; for(int var7 = 0; var7 < var6; ++var7) { Method var8 = var5[var7]; this.addProxyMethod(var8, var4); } } Iterator var11 = this.proxyMethods.values().iterator(); //验证具有相同方法签名的方法的返回类型是否一致 List var12; while(var11.hasNext()) { var12 = (List)var11.next(); checkReturnTypes(var12); } //后面一系列的步骤用于写代理类Class文件 Iterator var15; try { //生成代理类的构造函数 this.methods.add(this.generateConstructor()); var11 = this.proxyMethods.values().iterator(); while(var11.hasNext()) { var12 = (List)var11.next(); var15 = var12.iterator(); while(var15.hasNext()) { ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next(); //将代理类字段声明为Method,并且字段修饰符为 private static. //因为 10 是 ACC_PRIVATE和ACC_STATIC的与运算 故代理类的字段都是 private static Method *** this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10)); //生成代理类的方法 this.methods.add(var16.generateMethod()); } } //为代理类生成静态代码块对某些字段进行初始化 this.methods.add(this.generateStaticInitializer()); } catch (IOException var10) { throw new InternalError("unexpected I/O Exception", var10); } if(this.methods.size() >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.