Static proxy, JDK dynamic proxy, Cglib dynamic proxy

The proxy mode can be used to enhance the proxy class without changing the proxy class.
For example: there is a div() method in the proxy class, which only calculates division, but wants to enhance it, such as adding exception capture, etc.

class CalculateImp {
    
    
    public void div(int x, int y) {
    
    
        System.out.println("x/y="+(x/y));
    }
}

static proxy

Usually, the static proxy class and the proxy class need to implement a unified business interface.

public class Application {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        CalculateImp calculateImp = new CalculateImp();
        AgencyCalculate calculate = new AgencyCalculate(calculateImp); 
        calculate.div(1,0);
    }
}
class CalculateImp implements Calculate{
    
    

    @Override
    public void div(int x, int y) {
    
    
        System.out.println("x/y="+(x/y));
    }
}
interface Calculate{
    
    
    void div(int x,int y);
}
class AgencyCalculate implements Calculate{
    
    

    private Calculate calculate;

    public AgencyCalculate(Calculate calculate) {
    
    
        this.calculate=calculate;
    }

    @Override
    public void div(int x, int y) {
    
    
        try{
    
    
            this.before();
            calculate.div(x,y);
            this.after();
        }
        catch (Exception e){
    
    
            System.out.println("出现错误");
        }
    }
    public void before(){
    
    
        System.out.println("前置增强");
    }
    public void after(){
    
    
        System.out.println("后置增强");
    }
}

Effect:
insert image description here

This method is that although the object of the interface type is passed in the static proxy class, as long as the object that implements this interface can be used as the object of the proxy class. However, objects that do not implement this interface cannot be proxied through this proxy class. For this reason, we need to write a static proxy class for it. What if there are multiple unrelated classes that need proxy enhancement? And using the dynamic proxy class can automatically generate a proxy class for each class and load it into the memory for direct use, so that we don't have to write the proxy class ourselves, we only need to know who the proxy object is and what enhanced logic to do. Can.

JDK dynamic proxy

The core of dynamic proxy is two parts:
1. Which class needs to be proxied?
Use the static method of Proxy newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) to specify which class. There are three parameters in this method:

  • ClassLoader: The loader of the proxied class, through which the generated proxy class can be loaded into memory. The class loader can be obtained through the reflection of the proxied class.
  • Class<?>[] interfaces: All implementation interfaces of the proxied class can make the generated proxy object also implement these interfaces. All implemented interfaces can be obtained through the reflection of the proxy class.
  • InvocationHandler: This is an interface that needs to pass its implementation class. The invoke() method is rewritten in the class, which is the specific enhanced logic. The implementation of this interface is the second problem to be considered below.

2. Specific enhancement logic?
By implementing InvocationHandlerthe interface and then overriding the invoke(Object proxy, Method method, Object[] args) method. As long as the generated proxy class object calls the method, it will be intercepted by the invoke() method, and the logic will be enhanced in this method, and then the method of the proxy class will be called. This method takes three parameters:

  • Proxy: automatically generated proxy object
  • method: the proxy method
  • args: the array of parameters passed to the proxy class method

Then InvocationHandlerpass the generated object that implements the interface to newProxyInstance()the third instance of the above method.

Note: This method of obtaining dynamic proxy objects through JDK must implement the interface.
Next, I will use JDK dynamic proxy to proxy the Calculate class above to achieve the same effect.

public class Application {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
       // 被代理对象
        CalculateImp calculateImp = new CalculateImp();
	
        ProxyFactory proxyFactory = new ProxyFactory();
       
       // 通过这个类生成代理对象
        proxyFactory.setObject(calculateImp);
		// 返回的代理类类型不确定,但可以代理类一定也实现了Calculate接口,利用多态引用
        Calculate c1 = (Calculate) proxyFactory.getProxy();
		// 调用方法,这是就会被生成的代理类对象的invoke方法拦截,然后实现增强。思考:上面为什么不使用Object或其他类接收
        c1.div(1,1);
    }
}

class CalculateImp implements Calculate{
    
    
    @Override
    public void div(int x, int y) {
    
    
        System.out.println("x/y="+(x/y));
    }
}

// 要想通过JDK这种方式实现动态代理,就必须有接口
interface Calculate{
    
    
    void div(int x,int y);
}
class ProxyFactory{
    
    
    // 通过set的方式获取被代理对象 ,使用Object可以生成任意对象代理
    private Object object;

    public void setObject(Object object) {
    
    
        this.object = object;
    }
    
    // 抽象出;两个增强的方法
    private void before(){
    
    
        System.out.println("前置增强");
    }
    private void after(){
    
    
        System.out.println("后置增强");
    }
    Object getProxy(){
    
    
        InvocationHandler invocationHandler = new InvocationHandler() {
    
      // 使用匿名内部类的方式直接实现接口,也可以使用外部类单独实现
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
                before();
                try{
    
    
                    // 调用被代理对象的方法 ,代理对象调用哪个方法就会被invoke拦截,然后在这里调用被代理类的该方法
                    method.invoke(object,args);
                }
                catch (Exception e){
    
    
                    System.out.println("出现异常");
                }
                after();
               return null;  // 若代理类不为 void ,可以在这里返回最终的结果
            }
        };
        // 被代理类的类加载器,被代理类实现的接口,代理逻辑
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), 	
        							  object.getClass().getInterfaces(),
        							  invocationHandler);
    }

}

The advantage of using a dynamic proxy class is that it does not need to write a proxy class for a certain class separately, and a dynamic class can be used to implement the proxy of any class. If the proxy logic is different, you can expose the InvocationHandler interface object of the ProxyFactory class in the code as a property.

The above is a demonstration that using a dynamic proxy class can achieve the same effect as a static proxy class. The following will demonstrate that a dynamic proxy class can automatically generate proxy objects of different classes. For example: Add a new House class, which has the same proxy logic as the Calculate class. Just add on top of the proxy above:
insert image description here

Effect:
insert image description here
The use of JDK dynamic proxy is to automatically generate a proxy class at runtime. This proxy class actually exists in memory, but its name is not fixed. It implements all the interfaces of the proxy class. After the bottom layer of JDK is generated, it will pass the specified The class loader is automatically placed in memory. When using, you only need to get it from the memory. Although you don’t know the class name, this proxy class implements all the interfaces of the proxy class, and you can use polymorphic references.

Thinking: What is the class name of the dynamically generated proxy class?
The generated proxy class object obtains the full class name of the class through reflection: 代理类对象.getClass().getName(), and the final format is 包名.$Proxy序号, for example:
insert image description here

Cglib dynamic proxy

Cglib is not a proxy method that comes with JDK. To use it, you need to introduce dependencies first.

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.5</version>
</dependency>

The way to use Cglib dynamic proxy is to use inheritance. Therefore, the proxy class does not need to implement the interface, but the proxy class must not be set to final. Because once the final bottom layer is added, the proxied class cannot be inherited, causing the proxy to fail.

There are still two issues to consider when using Cblib:
1. Which class is to be proxied?
You need to create an Enhancerobject, and then call the setSuperclass (Class superclass) method. The parameter of the method is the proxy class

2. Specific enhancement logic?
Need to implement MethodInterceptorthe interface and rewrite the intercept (Object o, Method method, Object[] objects, MethodProxy methodProxy) method, which has 4 parameters

  • Object o: the generated proxy class object, the proxy class is automatically generated and the class name is not fixed
  • Method method: the method of the proxy class
  • Object[] objects: the parameter list of the method
    The above three correspond to the three parameters of the invoke() method of the InvokerHander implementation class on the JDK
  • MethodProxy methodProxy: The bottom layer dynamically creates the object of the proxy class

Next, I will use Cglib dynamic proxy to proxy the Calculate class above to achieve the same effect.

public class Application {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        //被代理类的对象
        CalculateImp calculateImp = new CalculateImp();
        // 生成代理类的对象
        ProxyFactory proxyFactory = new ProxyFactory();

        proxyFactory.setObject(calculateImp);

        // 同样生成的类不固定名,但这个类继承了被代理类,所以利用多态即可引用即可
        CalculateImp proxy = (CalculateImp) proxyFactory.getProxy();

        // 代理类调用此方法,会转到enhancer指定的回调对象的invoke方法,这个方法就是写代理逻辑的方法
        proxy.div(1,2);
    }
}
// 注意 一定不能为final,否则生成的代理类无法继承。至于接口,与Cglib动态代理无关,这里特意去掉接口
class CalculateImp {
    
    
 
    public void div(int x, int y) {
    
    
        System.out.println("x/y="+(x/y));
    }
}

// 要想通过JDK这种方式实现动态代理,就必须有接口
interface Calculate{
    
    
   public void div(int x,int y);
}

class ProxyFactory implements MethodInterceptor {
    
    
    private Object object;

    public void setObject(Object object) {
    
    
        this.object = object;
    }

    // 抽象出;两个增强的方法
    private void before(){
    
    
        System.out.println("前置增强");
    }
    private void after(){
    
    
        System.out.println("后置增强");
    }

    // 具体增强的逻辑
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
    
        this.before();
        // 通过反射调用代理类的方法
        method.invoke(this.object,objects);
        System.out.println(methodProxy.getClass().getName());
        this.after();
        return null;
    }
    public Object getProxy(){
    
    

        //Enhancer :增强者
        Enhancer enhancer = new Enhancer();
        // 设置要代理哪个类,因为生成的代理类是继承被代理类,所以这个接口参数名为superclass
        enhancer.setSuperclass(CalculateImp.class);

        //设置回调 就是实现了MethodInterceptor接口的实现类对象,由于这里将enhancer写在了这类内部,所以this就可
        enhancer.setCallback(this);
        
        //最后返回生成的代理类对象
        return enhancer.create();
    }
}

Thinking: What is the class name of the proxy class dynamically generated by Cglib?
The generated proxy class object obtains the full class name of the class through reflection: 代理类对象.getClass().getName(), and the final format is 被代理类的全类名$$EnhancerByCGLIB$$编号, for example:
insert image description here

summary

Summary of use
For example: enhance the proxy of the method in the class, and supplement the execution time of the method in the proxy class.

The way to use JDK dynamic proxy

public class Application {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
       	
        ProxyFactory proxyFactory = new ProxyFactory(被代理对象);
      
       	业务接口 proxy = (业务接口) proxyFactory.getProxy();
        proxy.业务方法;
    }
}
class ProxyFactory{
    
    
    private Object object;

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

    private InvocationHandler getInvocationHandler(){
    
    
        InvocationHandler invocationHandler = new InvocationHandler() {
    
    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
                long before = System.currentTimeMillis();
                method.invoke(object,args);
                long after = System.currentTimeMillis();
                System.out.println("方法执行完毕。共耗时"+(after-before)+"毫秒");
                return null;
            }
        };
        return invocationHandler;
    }
    public Object getProxy() {
    
    
       return Proxy.newProxyInstance(object.getClass().getClassLoader(), 
       								 object.getClass().getInterfaces(),
       								 getInvocationHandler());
    }
}

Use Cglib dynamic proxy method

public class Application {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        
        ProxyFactory proxyFactory = new ProxyFactory(被代理对象);
        被代理类 proxy = (被代理类) proxyFactory.getProxy();
      	proxy.业务方法;
    }
}
class ProxyFactory implements MethodInterceptor {
    
    
    private Object object;

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


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
    
        long before = System.currentTimeMillis();
        method.invoke(object,objects);
        long after = System.currentTimeMillis();
        System.out.println("方法执行完毕。共耗时"+(after-before)+"毫秒");
        return null;
    }
    public Object getProxy(){
    
    

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Test.class);
        enhancer.setCallback(this);
        return enhancer.create();
    }
}

Cglib dynamic proxy compared to JDK dynamic proxy 优点:

  • Cglib is more performant
  • Cglib does not need to be implemented by the proxy class, and the code intrusion is less

Cglib dynamic proxy compared to JDK dynamic proxy 缺点:

  • Cglib dynamic needs to import dependencies, and JDK dynamic proxy supports natively, which can be smoothly upgraded with the upgrade of JDK version. And Cglib may also introduce a new version of the Jar package
  • The proxy class cannot be modified by final

The Java reflection mechanism used by the JDK dynamic proxy, the proxy class must implement the business interface; Cglib is implemented based on the ASM mechanism, and the proxy class cannot be modified by final. Using a dynamic proxy can automatically generate a proxy class for the proxy class and load the proxy class object into memory.

Guess you like

Origin blog.csdn.net/m0_52889702/article/details/128941930