[Java essential] Java proxy mode (static proxy, JDK/Cglib dynamic proxy)

introduction

I believe that as a Java developer, you should have used or seen the proxy design pattern. Like Spring's AOP, it is the dynamic proxy mode used. The xxxMapper interface in Mybatis is also the dynamic proxy used to generate the corresponding Mapper proxy object. The importance of the dynamic agent model.

The proxy mode is generally divided into static proxy and dynamic proxy, the goal is to expand and enhance the original method function.

Static proxy

  • Abstract Object Role (AbstractObject): Generally, an interface or abstract class is used to declare a common interface between the target object and the proxy object, so that the proxy object can be used wherever the target object can be used.
  • Target object role (RealObject): The real role being proxied.
  • ProxyObject role (ProxyObject): It implements the same interface of the target object, and contains a reference to the target object, and can customize and expand the target method.

Define an order interface and add an order method

public interface IOrderService {
    // 提交订单
    void submitOrder();
}

Write order interface implementation class

public class OrderServiceImpl implements IOrderService{

    // 提交订单测试
    @Override
    public void submitOrder() {
        System.out.println("-------保存订单-------");
    }
}

Then, if the current submitOrder() method can no longer meet our needs, we need to do some special business processing before and after this method, but we do not want to modify the original order interface implementation class (open and close principle), so we You can add an order proxy class. This proxy class defines the member properties of a target interface object. Through construction injection, you can manipulate the target object. You can add custom logic before and after the target object method:

public class OrderStaticProxy implements IOrderService {

    private IOrderService orderService;

    // 构造注入
    public OrderStaticProxy(IOrderService orderService){
        this.orderService=orderService;
    }

    @Override
    public void submitOrder() {
        System.out.println("--------提交订单前,自定义逻辑");
        orderService.submitOrder();
        System.out.println("--------提交订单前,自定义逻辑");
    }
}

Test category:

public class Test {

    public static void main(String[] args) {
        IOrderService orderService = new OrderServiceImpl();
        OrderStaticProxy proxy = new OrderStaticProxy(orderService);
        proxy.submitOrder();
    }
}

The output is as follows:

Analysis: Without changing the original OrderServiceImpl implementation class, the function of the original order interface is expanded and enhanced

advantage:

  • It can expand the function of the target object without modifying the target object;
  • Public services are handled by agents, which is more convenient;
  • Comply with the principle of opening and closing and the principle of singleness.

Disadvantages:

  • The proxy class needs to implement the same interface as the target object, so when each class needs to write a proxy class, it causes too many classes and inconvenient maintenance;
  • If methods are added to the interface, both the target implementation class and the proxy class need to be modified.

Therefore, we lead to dynamic agents.

Dynamic proxy

The role of dynamic agents is the same as that of static agents.

The static proxy will provide a proxy class for each business enhancement, and the proxy class will create proxy objects; while the dynamic proxy does not have a proxy class, the proxy objects are directly generated dynamically by the proxy generation tool.

The proxy object of the dynamic proxy is dynamically generated at runtime. The proxy class of the static proxy needs to be written in advance, that is, the proxy class has been determined at compile time.

Dynamic agents are divided into:

  • Interface-based dynamic proxy (JDK dynamic proxy)
  • Class-based dynamic proxy (cglib)

JDK dynamic proxy

In the JDK dynamic proxy, the core is the InvocationHandler interface and the Proxy class. For details, please refer to the JDK help documentation.

package java.lang.reflect;

public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

Among them, we need to implement the InvocationHandler # invoke() method, the parameters are:

  • Object proxy: proxy object (basically useless)
  • Method method: the method called by the proxy object
  • Object[] args: parameters in the called method

Generate proxy class objects through Proxy#newProxyInstance()

    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        //。。。。
    }

Among them, the method Proxyis a static method in the class, and the parameters are:

  • ClassLoader loader: Specify the current target object to use the class loader, and the method of obtaining the loader is fixed.
  • Class<?>[] interfaces: The type of interface implemented by the target object, and the type is confirmed using a generic method.
  • InvocationHandler h: Event handler, when the method of the target object is executed, the invoke() method of the event handler will be triggered, and the method of the currently executed target object will be passed in as a parameter.

Code

The abstract object role and the target object role are the same as the static proxy (IOrderService-->OrderServiceImpl).

Customize the implementation of InvocationHandler # invoke() as follows

public class DynamicProxy implements InvocationHandler {


    private Object target;

    public DynamicProxy(Object target){
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("------动态代理,前置增强-----");
        Object invoke = method.invoke(target, args);
        System.out.println("------动态代理,后置增强-----");
        return invoke;
    }
    // 生成动态代理对象
    public static Object getProxy(Object target){
        DynamicProxy proxy = new DynamicProxy(target);
        return Proxy.newProxyInstance(proxy.getClass().getClassLoader(), target.getClass().getInterfaces(),proxy);
    }

}

Test category:

/**
 * @description: 测试
 * @author: stwen_gan
 * @date: 
 **/
public class Test {

    public static void main(String[] args) {

        // 为了在项目下生成代理类
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
        
        // 静态代理
//        IOrderService orderService = new OrderServiceImpl();
//        OrderStaticProxy proxy = new OrderStaticProxy(orderService);
//        proxy.submitOrder();

        // 动态代理
        IOrderService orderService = new OrderServiceImpl();
        IOrderService dynamicProxy = (IOrderService) DynamicProxy.getProxy(orderService);
        dynamicProxy.submitOrder();


    }
}

Output:

Among them, add the following configuration statement

//This setting is used to output the class generated by the cg1ib dynamic proxy

// System.setProperty(Debuggingclasswriter.DEBUG_LOCATION_PROPERTY,"D:\\class");

//This setting is used to output the class generated by jdk dynamic proxy

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

It will be in our current directory, the JDK will generate a proxy class by default, the name defaults to $Proxy0 , which inherits the Proxy class

Core : A dynamic agent, generally acting for a certain type of business, can act for multiple classes, and act on interfaces.

As can be seen from the above code, the dynamic proxy object does not need to implement the target object interface, but the target object must implement the interface, otherwise the JDK dynamic proxy cannot be used.

Improvement : The return object type is generic, and the abstract interface can have no implementation class. Similar to the xxxMapper interface in Mybatis, the bottom layer is also to generate mapper proxy objects through dynamic proxy to splice and execute SQL, we don’t need to write mapper implementation classes

Cglib proxy

The above static proxy and JDK dynamic proxy modes both require the target object to implement an interface, but sometimes, the target object may be just an independent class without implementing any interface. At this time, we can use the target object subclass The way to realize the proxy, this proxy method is: Cglib proxy, relatively less used. ( Quote )

The principle of Cglib dynamic proxy is to generate a subclass of the target class (that is, the subclass object is a proxy object), which constructs a subclass object in memory to realize the function expansion of the target object .

Note: Cglib dynamic proxy can be used regardless of whether the interface is implemented or not, not only when there is no interface.

  • The dynamic proxy of JDK has a limitation, that is, the target object that uses the dynamic proxy must implement at least one interface. Therefore, if the target object that does not implement the interface but wants to use the proxy, you can use the Cglib proxy.
  • Cglib is a powerful high-performance code generation package that can expand Java classes and implement Java interfaces during runtime. It is widely used by many AOP frameworks, such as Spring AOP and synaop, to provide them with methods interception(intercept).
  • The bottom layer of the Cglib package is to convert the bytecode and generate new classes by using a small and fast bytecode processing framework ASM. Direct use of ASM is discouraged because it requires you to understand the internal structure of the JVM, including the format of the class file. Familiar with the instruction set.

MethodInterceptor

public interface MethodInterceptor extends Callback {
    Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
}

Cglib tool class, we only need to implement MethodInterceptor#intercept() to enhance the target method

public class CglibProxy implements MethodInterceptor {

    //维护目标对象
    private Object target;
    public CglibProxy(Object target) {
        this.target = target;
    }

//    //获取目标对象的代理对象
//    public Object getProxy() {
//        //1. 实例化工具类
//        Enhancer en = new Enhancer();
//        //2. 设置父类对象
//        en.setSuperclass(this.target.getClass());
//        //3. 设置回调函数
//        en.setCallback(this);
//        //4. 创建子类,也就是代理对象
//        return en.create();
//    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("-------动态代理,前置增强-----");
        //执行目标对象的方法
        Object object = method.invoke(target, objects);
        System.out.println("-------动态代理,后置增强-----");
        return object;
    }
}

Among them, the commented method is to obtain the proxy object of the target class. We can extract it separately and add a cglib proxy factory:

/**
 * @description: cglib代理工厂
 * @author: stwen_gan
 * @date: 
 **/
public class CglibProxyFactory {
    //获取目标类的代理对象
    public static <T> T createProxy(final Class<?> targetClass, final MethodInterceptor methodInterceptor) {
        return (T) Enhancer.create(targetClass,methodInterceptor);
    }
}

Add a new OrderDao class, no need to implement any interface

public class OrderDao {
    
    void submitOrder(){
        System.out.println("--------保存订单-------");
    }
}

Test category:

Output:

to sum up

Compared with static agents, JDK dynamic agents are similar in that they are all interface-oriented programming, which enhances the target method function without modifying the original code, and dynamic agents do not need to write one-to-one corresponding agent classes for target objects like static agents. The use of the agent model conforms to the "opening and closing principle", and the division of functional responsibilities is clear. The goal is to expand and enhance the original method functions, making it easier to expand and maintain later.

●The strongest Tomcat8 performance optimization in history

Why can Alibaba resist 10 billion in 90 seconds? --The evolution of server-side high-concurrency distributed architecture

B2B e-commerce platform--ChinaPay UnionPay electronic payment function

Learn Zookeeper distributed lock, let interviewers look at you with admiration

SpringCloud e-commerce spike microservice-Redisson distributed lock solution

Check out more good articles, enter the official account--please me--excellent in the past

A deep and soulful public account 0.0

Guess you like

Origin blog.csdn.net/a1036645146/article/details/111881599
Recommended