Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to implement static Agent and dynamic Agent in Java Agent Design pattern

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

Today, I will talk to you about how to implement static proxy and dynamic proxy of Java proxy design pattern. Many people may not know much about it. In order to make you understand better, the editor summarizes the following content for you. I hope you can get something according to this article.

Interview question: how many ways can the agent design pattern in Java be implemented? This topic is very much like Kong Yiji asking, "what are the ways to write the word fennel for fennel beans?"

The so-called proxy mode means that the client (Client) does not directly call the actual object (RealSubject in the lower right corner of the following figure), but indirectly calls the actual object by calling the proxy (Proxy).

The use of proxy mode is generally because the client does not want to access the actual object directly, or there are technical obstacles to access the actual object, so the proxy object is used as a bridge to complete indirect access.

Implementation method 1: static proxy

Develop an interface IDeveloper that contains a method writeCode to write code.

Public interface IDeveloper {public void writeCode ();}

Create a Developer class that implements the interface.

Public class Developer implements IDeveloper {private String name; public Developer (String name) {this.name = name;} @ Override public void writeCode () {System.out.println ("Developer" + name + "writes code");}}

Test the code: create an instance of Developer called Jerry to write the code!

Public class DeveloperTest {public static void main (String [] args) {IDeveloper jerry = new Developer ("Jerry"); jerry.writeCode ();}}

Now here's the problem. The project manager at Jerry is unhappy with Jerry writing code without maintaining any documentation. Suppose that one day Jerry goes on vacation and other programmers take over Jerry's work, with a question mark on the strange code. After a group-wide discussion, it was decided that each developer must update the document synchronously when writing code.

In order to force every programmer to remember to write documentation during development without affecting the action of writing code, we do not modify the original Developer class, but create a new class that also implements the IDeveloper interface. The new class DeveloperProxy maintains a member variable internally that points to the original IDeveloper instance:

Public class DeveloperProxy implements IDeveloper {private IDeveloper developer; public DeveloperProxy (IDeveloper developer) {this.developer = developer;} @ Override public void writeCode () {System.out.println ("Write documentation..."); this.developer.writeCode ();}}

In the writeCode method implemented by this proxy class, a call to write documentation is added before the actual programmer's writeCode method is called, which ensures that programmers write code with documentation updates.

Test the code:

Advantages of static proxy

1. Easy to understand and implement

two。 The relationship between the proxy class and the real class is statically determined at compile time, and there is no additional overhead at execution time compared to the dynamic proxies described below.

The shortcomings of static agent mode

Each real class needs a new proxy class to create. Or take the above document update as an example, suppose the boss also put forward new requirements for the test engineer, so that the test engineer should update the corresponding test document every time he tests out the bug. Then using the static proxy approach, the test engineer's implementation class ITester also has to create a corresponding ITesterProxy class.

Public interface ITester {public void doTesting ();} Original tester implementation class:public class Tester implements ITester {private String name; public Tester (String name) {this.name = name;} @ Override public void doTesting () {System.out.println ("Tester" + name + "is testing code");}} public class TesterProxy implements ITester {private ITester tester; public TesterProxy (ITester tester) {this.tester = tester @ Override public void doTesting () {System.out.println ("Tester is preparing test documentation..."); tester.doTesting ();}}

It is precisely because of this disadvantage of static code that the dynamic proxy implementation of Java is born.

The first way to realize Java dynamic agent: InvocationHandler

I have written a special article about the principle of InvocationHandler: the simplest introduction to InvocationHandler of Java dynamic Agent.

With InvocationHandler, I can use an EnginnerProxy proxy class to proxy both Developer and Tester behavior.

Public class EnginnerProxy implements InvocationHandler {Object obj; public Object bind (Object obj) {this.obj = obj; return Proxy.newProxyInstance (obj.getClass (). GetClassLoader (), obj.getClass (). GetInterfaces (), this);} @ Override public Object invoke (Object proxy, Method method, Object [] args) throws Throwable {System.out.println ("Enginner writes document"); Object res = method.invoke (obj, args) Return res;}}

The writeCode and doTesting methods of the real class are executed by reflection in the dynamic proxy class.

Test output:

The limitation of realizing dynamic Agent through InvocationHandler

Suppose there is a product manager class (ProductOwner) that does not implement any interfaces.

Public class ProductOwner {private String name; public ProductOwner (String name) {this.name = name;} public void defineBackLog () {System.out.println ("PO:" + name + "defines Backlog.");}}

We still use the EnginnerProxy proxy class to proxy it, and there are no errors at compile time. What happens at run time?

ProductOwner po = new ProductOwner ("Ross"); ProductOwner poProxy = (ProductOwner) new EnginnerProxy (). Bind (po); poProxy.defineBackLog ()

An error is reported at runtime. So the limitation is that if the proxied class does not implement any interface, you cannot proxy its behavior through an InvocationHandler dynamic proxy.

The second way to realize Java dynamic agent: CGLIB

CGLIB is a Java bytecode generation library that provides easy-to-use API to create and modify Java bytecode. For more details about this open source library, please go to CGLIB's warehouse on github: https://github.com/cglib/cglib

We now try to use CGLIB to proxy the ProductOwner class that InvocationHandler did not successfully proxy (this class does not implement any interfaces).

Now I use CGLIB API to create the proxy class instead:

Public class EnginnerCGLibProxy {Object obj; public Object bind (final Object target) {this.obj = target; Enhancer enhancer = new Enhancer (); enhancer.setSuperclass (obj.getClass ()) Enhancer.setCallback (new MethodInterceptor () {@ Override public Object intercept (Object obj, Method method, Object [] args, MethodProxy proxy) throws Throwable {System.out.println ("Enginner 2 writes document"); Object res = method.invoke (target, args); return res;}}) Return enhancer.create ();}}

Test the code:

ProductOwner ross = new ProductOwner ("Ross"); ProductOwner rossProxy = (ProductOwner) new EnginnerCGLibProxy (). Bind (ross); rossProxy.defineBackLog ()

Although ProductOwner does not implement any code, it is successfully proxied:

The limitation of implementing Java dynamic Agent with CGLIB

If we understand how CGLIB creates proxy classes, its limitations are clear at a glance. Let's now experiment with adding the final modifier to the ProductOwner class to make it uninheritable:

Execute the test code again, and this time you get an error: Cannot subclass final class XXXX.

So the dynamic proxy successfully created by CGLIB is actually a subclass of the proxied class. Then if the proxied class is marked as final, it is not possible to create a dynamic proxy through CGLIB.

The third way to implement Java dynamic proxy is to dynamically create proxy classes through API provided at compile time.

Suppose we do need to create dynamic code for a ProductOwner class that is both final and does not implement any interfaces. In addition to InvocationHandler and CGLIB, we have one last resort:

I directly spell out the source code of a proxy class with a string, then call JDK's Compiler (compile time) API based on this string, dynamically create a new .java file, and then compile the .java file dynamically, so that I can get a new proxy class.

The test succeeded:

I have assembled the source code of the code class and dynamically created the .java file of the proxy class. I can open the .java file created by the code in Eclipse.

The following figure shows how to create a ProductPwnerSCProxy.java file dynamically:

The following figure shows how to dynamically compile the .java file created in the previous step with JavaCompiler API to generate a .class file:

The following figure shows how to use the class loader to load compiled .class files into memory:

After reading the above, do you have any further understanding of how to implement static and dynamic proxies in the Java proxy design pattern? If you want to know more knowledge or related content, please follow the industry information channel, thank you for your support.

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.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report