This is the 8th day of my participation in the August More Text Challenge

The agent

In Java, a proxy is a representation of a concrete instance object. For example, we find wechat business to buy things, the specific example object is “manufacturer”, this “representative” is “wechat business”. So like purchase, sell goods this kind of business is by “wechat business” to do, “manufacturers” to do is to produce goods. However, if you want to buy something produced by the “manufacturer”, you must pass the “wechat business” on behalf of it. That is, the proxy is the entry and exit of a concrete instance object.

The proxy pattern

📢 Proxy mode is a design mode, through the proxy mode, provides additional access to the proxy class, through the proxy class access to the proxy class, we can provide additional functional operations (enhancement) without modifying the proxy class, so as to extend the proxy class function. In short, the proxy pattern is to set up an intermediate proxy to control all access to the proxied class to enhance the proxied class. There are two proxy modes: static proxy and dynamic proxy.

Static agent

If the class already exists before the program is run, it is static. Static proxy classes and proxied classes need to implement the same interface or inherit from the same class. InterfaceProxy represents the interface that both the proxy class and the proxied class need to implement, Proxy_ represents the proxy class, InterfaceProxyImpl represents the proxied class, we create a proxied class object in the proxy class, In this way, we can access the proxied class through the proxy class or make functional enhancements to the proxied class.

  • interface
public interface InterfaceProxy {

    Integer method(Integer price);

}
Copy the code
  • By the proxy class
public class InterfaceProxyImpl implements InterfaceProxy { @Override public Integer method(Integer price) { return price; }}Copy the code
  • The proxy class
Public class proxy_implements InterfaceProxy {// InterfaceProxyImpl = new InterfaceProxyImpl(); @override public Integer method(Integer price) {// Call the proxied class's method Integer p = pip.method (price); // Enhancements to proxied classes return p*2; }}Copy the code
  • test
Public static void main(String[] args) {public static void main(String[] args) {// Access Proxy_ proxyDemo = new Proxy_(); System.out.println(" proxydemo.pii.method (10) "); Integer proxyPrice = proxydemo.method (10); Integer proxyPrice = proxydemo.method (10); System.out.println(" proxyPrice: "+proxyPrice); // Agent price: 20}Copy the code

A dynamic proxy

There is no proxy class before the program runs, and the proxy class is generated when the program runs, which is called dynamic proxy. Assume that static agent example above we need to realize such a function, all methods by proxy classes in the implementation of the output before “before”, after the execution is the proxy class method output “after”, if the proxy class implements the interface of multiple methods, so we need to modify each method of the proxy class (as shown in the following program). With dynamic proxies, we can treat all methods of the propped class uniformly, as described below. Dynamic proxies can enhance methods without modifying them. Dynamic proxy can be implemented in two ways: interface-based dynamic proxy 👉JDK dynamic proxy, subclass-based dynamic proxy (generate a subclass of the specified target class and enhance the subclass) 👉CGLib dynamic proxy

Public class proxy_implements InterfaceProxy {// InterfaceProxyImpl piI = new InterfaceProxyImpl(); @Override public Integer method(Integer price) { System.out.println("before"); // Execute target method Integer p = pii.method(price); System.out.println("after"); // Enhancements to proxied classes return p*2; } @Override public Integer method2(Integer price) { System.out.println("before"); // Execute target method method Integer p = pii.method(price); System.out.println("after"); // Enhancements to proxied classes return p*2; } @Override public Integer method3(Integer price) { System.out.println("before"); // Execute target method Integer p = pii.method(price); System.out.println("after"); // Enhancements to proxied classes return p*2; }}Copy the code

JDK dynamic proxy

JDK dynamic proxies are implemented based on interfaces, and therefore need to be implemented by a proxying class at least one interface, that is, only methods defined in the interface implemented by the class can be proxied. JDK dynamic Proxy uses the reflection class Proxy to generate Proxy objects, which are intercepted by InvocationHandler before executing the target method. We write our enhancement logic inside the Invoke method to enhance the target method without modifying the method.

Parameter description of newProxyInstance

Object newProxyInstance(ClassLoader loader, Class<? >[] interfaces, InvocationHandler h)Copy the code
  • ClassLoader: a ClassLoader used to load the bytecode of a proxy object. But this parameter needs to write to the ClassLoader of the propped class, the ClassLoader that writes to whoever the propped class is. Such as proxy InterfaceProxyImpl class, then to InterfaceProxyImpl getClass (). GetClassLoader ()

  • Class
    [] interfaces: An array of bytecode Interfaces that hold the Interfaces implemented by the proxying class and are used to make the proxying object have the same methods as the proxying object. Interfaces that write to the proxying object, such as the proxying InterfaceProxyImpl class, For InterfaceProxyImpl. GetClass (). GetInterfaces ()

  • InvocationHandler h: To provide the enhanced code, which lets us write how to delegate, we need to override the invoke() method in InvocationHandler, 📢 when the target method is executed, it is intercepted by the InvocationHandler, and then execute the invoke method, Our enhanced logic for methods is written in the Invoke method,

Parameters to the invoke method

public Object invoke(Object proxy, Method method, Object[] args){
    return null;
}
Copy the code
  • Object proxyRepresents a reference to a proxy object (normally not used)
  • Method methodRepresents the method object being invoked (that is, the method in the proxied class),Method.invoke (Target object, method parameter)Represents the execution of the target method
  • Object[] argsRepresents the parameters required by the invoked method to execute

Using the example

  • Preparing an interface
public interface InterfaceProxy {

    void method(Integer price);

    void method2(String string);

}
Copy the code
  • The proxied class needs to implement the InterfaceProxy interface above
public class InterfaceProxyImpl implements InterfaceProxy { @Override public void method(Integer price) { System.out.println(" after: "+price); } @override public void method2(String String) {system.out.println (" after: "+ String); }}Copy the code
  • Generating proxy classes
Public class test {// Public static void main(String[] args) {// Final InterfaceProxyImpl proxyed = new InterfaceProxyImpl(); InterfaceProxy proxy = (InterfaceProxy)Proxy.newProxyInstance(proxyed.getClass().getClassLoader(), proxyed.getClass().getInterfaces(), New InvocationHandler() {Override public Object invoke(Object proxy) @override public Object invoke(Object proxy, Method Method, Object[] args) throws Throwable {// Get the Method execution parameter Object arg = args[0]; // If there are multiple methods in the interface, it is necessary to determine which method to enhance. If all methods need to be enhanced, it is impossible to determine which method to enhance. Augmented if(method.getName().equals("method")){system.out.println ("before1"); // Execute the target method Integer returnVal = (Integer) method.invoke(proxyed,(Integer)arg*2); System.out.println("after1"); // The invoke method returns the return value of the method executed by the proxy class. If the target object method type is void, return null return null; }else{ System.out.println("before2"); // Execute the target method String returnVal = (String) method.invoke(proxyed," like + attention: "+(String)arg); System.out.println("after2"); return returnVal; }}}); // Call the target method proxy.method(8) from the proxy object; System.out.println("==============================="); Proxy. method2(" Programmer without milk tea "); }} program output: before1 agent: after 16 after1 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = before2 agent: after thumb up + attention: do not drink milk tea Programmer after2Copy the code

😋 From the above examples, we can see that the dynamic Proxy method does not require us to manually generate the Proxy class, but the newProxyinstance() method of Proxy. After running, the bytecode file of the Proxy class is not generated, that is, the.class file, but dynamically generates the bytecode of the Proxy class at run time. And loaded into the JVM. At the same time, we can uniformly enhance methods without modifying them through dynamic proxy use.

CGLib dynamic proxy

After the above study, we already have some knowledge of the JDK dynamic proxy, the JDK dynamic proxy can only be to implement the interface class generation agent, but not for class, if you want to proxy class for a general class, don’t implement any interface, they can’t use the JDK dynamic proxy, so additional dynamic proxy arises at the historic moment 🤳. CGLib is a subclass-based dynamic proxy. CGLib dynamically generates subclasses of the propped class by modifying the bytecode file of the propped class through the ASM open source package. Subclasses override all non-final methods of the propped class (final modified methods do not allow subclass overwriting). We then use method interception techniques in our subclasses to intercept all calls to the proided methods and weave our enhanced code into them.

CGLib generates the proxy object using the Enhancer static method create

Create () uses the following method:

public static Object create(Class type, Callback callback) {
    Enhancer e = new Enhancer();
    e.setSuperclass(type);
    e.setCallback(callback);
    return e.create();
}
Copy the code

⏩ Each parameter indicates:

  • Class type: bytecode. GetClass (), the bytecode used to specify the propped object
  • Callback callbackMethodInterceptor: Defines an interceptor that provides enhanced code, usually written as the implementation class of the interface’s subinterface, MethodInterceptor, or directly implemented. After implementing the MethodInterceptor interface, you need to override itintercept()Method, that is, every time the target method is executed, CGLib will call back the MethodInterceptor interface method and then execute our overwritten methodintercept()Method, our proxy logic is written inintercept()Methods.

The various parameters of the Intercept () method

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    return null;
}
Copy the code
  • Object o: a reference to a dynamically generated proxy object
  • Method method: represents the method object being called (that is, the method in the proxied class),Method.invoke (Target object, method parameter)Represents the execution of the target method

– Object[] objects: parameters required for the execution of the called method

  • MethodProxy methodProxy: The proxy object that is currently executing the methodMethodproxy.invokesuper (proxy object, parameter)To execute the target method

Use of CGLib dynamic proxies

  • CGLib is a third-party tool, so we need to import third-party CGLib libraries to use it
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version> 2._3 </version> </dependency>Copy the code
  • Prepare the proxyed classproxy_Without implementing any interface
public class proxy_ { public void method1(String str){ System.out.println(str); }}Copy the code
  • Generating proxy objectsProxy
public static void main(String[] args) { final proxy_ proxyed = new proxy_(); proxy_ Proxy = (proxy_)Enhancer.create(proxyed.getClass(), new MethodInterceptor() { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before"); Invoke (proxyed, "like + focus: "+objects[0]); System.out.println("after"); return null; }}); // Execute the target method proxy.method1 (" don't drink milk tea Programmer") through the Proxy object; } Programmer output: before + attention: don't drink milk tea Programmer afterCopy the code

🥨 Can I use a CGLib dynamic proxy if my proxyed class implements an interface? ➤ ➤ You can come to 🥠.

Summary (THE difference between JDK and CGLib dynamic proxy)

  • The JDK dynamic proxy is an interface based dynamic proxy that implements an interface. The propped class must implement at least one interface. CGLib is a subclass based dynamic proxy that implements a class.
  • JDK dynamic proxy generates proxy objects through reflection, while CGlib uses bytecode technology to generate proxy objects through ASM bytecode generation framework, which is more efficient than Java reflection.

🏁🏁 dynamic proxy in a lot of frameworks will be used, such as Spring AOP, Mybatis have a lot of places to use the dynamic proxy, so a good grasp of dynamic proxy is very helpful to our learning behind, knowledge is a bit of nesting, is the so-called short step without a thousand miles. Ok, the above is the introduction of static proxy and dynamic proxy, if there is any error, please leave a comment, if you think this article is helpful to you, then click a like 👍 😻😍