Moment For Technology

Java dynamic proxies, this is enough

Posted on Dec. 2, 2022, 5:37 p.m. by Andrew Forsyth
Category: The back-end Tag: The back-end java spring github

This article needs to use Java reflection knowledge, if you are not familiar with reflection, you can first go to Java Advanced - Reflection.

Programming ideas come from life, and "agents" are common in life. For example, when we buy something, we generally don't buy it directly from the factory, but from the store or other businesses, which are agents. Another example, do wechat business friends often promote goods in the circle of friends, they are also agents. The agent in Java program is realized by interface. The demand of the user to buy a commodity can be regarded as an interface, and the real seller of the commodity to the user is the agent. The agent and the manufacturer have a direct relationship with this demand, and the agent has a relationship with the manufacturer (the agent and the agent), which is the following picture.

In this case, the user in the diagram can be seen as the person buying something, the interface being represented can be seen as some kind of commodity, the agent can be seen as the intermediary agent, and the real object being represented can be seen as the factory or manufacturer producing the commodity. Let's use this as an example to implement the proxy pattern.

Note: we put all the classes in one folder, and make the inner class static for ease of use.

public class StaticProxy {
    // Commodity interface
    static interface Goods {
        public void trade(a);
    }

    // Origin of commodities
    static class ChangJia implements Goods {
        @Override
        public void trade(a) {
            System.out.println("Manufacturers produce goods."); }}// Commodity agency
    static class JingXiaoShang implements Goods {
        private ChangJia changJia;

        public JingXiaoShang(ChangJia changJia) {
            this.changJia = changJia;
        }

        @Override
        public void trade(a) {
            System.out.println("The manufacturer produces the product at a cost of 1000 yuan.");
            changJia.trade();
            System.out.println("The dealer makes a profit of $100 on the sale."); }}// The user buys something
    public static void main(String[] args) {
        Goods proxy = new JingXiaoShang(new ChangJia());
        proxy.trade(); // Commodities are traded}}Copy the code

The output is

The manufacturer produces the product at a cost of1000Yuan manufacturers produce goods dealers sell goods, profit for100yuanCopy the code

The above code is an implementation of the proxy pattern, which is a "static proxy" where the proxy class is determined at compile time, while in Java you can also implement a "dynamic proxy" where the proxy class is generated at run time.

There are two ways to implement dynamic proxy in Java. One is the built-in JDK dynamic proxy in Java, and the other is the CGLIB library dynamic proxy implemented using bytecode enhancement technology. The following is a detailed description of how to implement the above code with these two ways.

1. JDK dynamic proxy

The JDK dynamic proxy essentially uses reflection in Java. Methods are defined in an interface (in this case, the Goods interface), and the proxied class (in this case, the ChangJia class) implements the interface, in turn implementing the methods in the interface. When calling a method on an interface, intercepting the method to be executed, adding additional operations, you also need a handler class (GoodsHander in this case) that handles intercepting. This handler class implements the InvocationHandler interface, where the Invoke () method executes first when calling a method on the interface. So it's acting as an agent. Unlike the above proxy pattern implementation, the JDK dynamic proxy does not define a proxy class directly. Instead, it adds a new class for handling methods in the interface. When the program is running, a proxy class is generated dynamically or a proxy object is generated directly to execute the method.

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKDynamicProxy {
    // Commodity interface
    static interface Goods {
        public void trade(a);
    }

    // Origin of commodities
    static class ChangJia implements Goods {
        @Override
        public void trade(a) {
            System.out.println("Manufacturers produce goods."); }}// Commodity handling
    static class GoodsHander implements InvocationHandler {
        private Object object; // The object to be represented is commodity

        public GoodsHander(Object object) {
            this.object = object;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // Proxy is the generated proxy object
            System.out.println("The manufacturer produces the product at a cost of 1000 yuan.");
            Object result = method.invoke(object, args);
            System.out.println("The dealer makes a profit of $100 on the sale.");
            returnresult; }}public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        GoodsHander goodsHander = new GoodsHander(new ChangJia()); // The GoodHander class deals with ChangJia
        // First dynamically generate the proxy class, and then regenerate the proxy class object
// Class proxyClass = Proxy.getProxyClass(Goods.class.getClassLoader(),Goods.class.getInterfaces());
// Goods proxy = (Goods) proxyClass.getConstructor(GoodsHander.class).newInstance(goodsHander);
        // Dynamically generated proxy objects, one step in place
        Goods proxy = (Goods) Proxy.newProxyInstance(Goods.class.getClassLoader(), Goods.class.getInterfaces(), goodsHander);
        proxy.trade(); // The invoke() method in GoodsHander is executed, followed by the trade() method in ChangJia}}Copy the code

2. CGLIB dynamic proxy

The above JDK dynamic proxy needs to define an interface, and the implementation class implements the methods in the interface. If the implementation class does not implement the interface, we cannot use the above proxy. Instead, we can use the following CGLIB dynamic proxy. The CGLIB dynamic proxy uses bytecode enhancement techniques that generate subclasses of a compiled class (bytecode) file. With polymorphism, calling a method in the parent class is actually calling the corresponding method in the child class. Therefore, due to inheritance, the class or method being proxied should not be modified with the final keyword. This subclass is generated by running in the program, so using the CGLIB library is also a dynamic proxy. Using CGLIB involves importing the CGLIB JAR package and the dependency JAR package asm.jar, which is the core of the bytecode enhancement technology. The CGLIB library eliminates the need for interfaces and adds a method enhancement class that subclasses the proxied class using the Enhance class from the CGLIB library. For details, read the code and comments below.

Note: The CGLIB -*. Jar package and ASM -. Jar package can be imported separately, or only one CGLIB -nodep-*. Jar package (including ASM) can be imported before using the CGLIB dynamic proxy. Download at github.com/cglib/cglib...

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLIBDynamicProxy {
    // Origin of commodities
    static class ChangJia{
        public void trade(a) {
            System.out.println("Manufacturers produce goods."); }}// Methods enhance classes
    static class GoodsTrade implements MethodInterceptor {
        @Override
        // o represents the object to be enhanced, method represents the method to be intercepted, objects represents the parameters in the method, and methodProxy represents the proxy for the method
        public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("The manufacturer produces the product at a cost of 1000 yuan.");
            methodProxy.invokeSuper(object, objects);
            System.out.println("The dealer makes a profit of $100 on the sale.");
            return object;
        }

        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer(); // Enhance class objects
            enhancer.setSuperclass(ChangJia.class); // Set the class to be enhanced, in this case ChangJia
            GoodsTrade goodsTrade = new GoodsTrade();
            enhancer.setCallback(goodsTrade); // Set the method to be enhanced, in this case GoodsTrade
            ChangJia changJia = (ChangJia) enhancer.create(); // Generate enhanced subclass objects
            changJia.trade(); // The calling method is actually an enhanced method}}}Copy the code

Differences between the two dynamic proxy modes:

  • The JDK dynamic proxy uses reflection technology, and the class being proxied implements the method interface.
  • CGLIB dynamic proxies use bytecode enhancement techniques, and the proxied classes do not implement method interfaces.

Both are used in the well-known Spring framework, which dynamically switches between JDK dynamic proxies and CGLIB based on whether or not the classes being proxied implement interfaces. In addition, AOP for aspect programming is also the embodiment of the idea of dynamic proxy, which can enhance the effect of methods by weaving new methods before and after the execution of methods. Dynamic proxy is widely used in the framework.

Search
About
mo4tech.com (Moment For Technology) is a global community with thousands techies from across the global hang out!Passionate technologists, be it gadget freaks, tech enthusiasts, coders, technopreneurs, or CIOs, you would find them all here.