42.动态代理

动态代理

文章目录

  • 动态代理
    • JDK动态代理
    • cglib动态代理
    • jdk动态代理和cglib动态代理的区别
      • 区别:
      • CGlib动态代理示例:

JDK动态代理

1.我们需要定义一个接口,作为代理和目标对象共同实现的约束:

package com.kang.spring.service;

/**
 * @Author Emperor Kang
 * @ClassName UserService
 * @Description 首先,我们需要定义一个接口,作为代理和目标对象共同实现的约束:
 * @Date 2023/12/26 16:14
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
public interface UserService {
    void addUser(String name);
    void deleteUser(String name);
}

2.我们定义一个目标对象,实现该接口

package com.kang.spring.service.impl;

import com.kang.spring.service.UserService;

/**
 * @Author Emperor Kang
 * @ClassName UserServiceImpl
 * @Description TODO
 * @Date 2023/12/26 16:14
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String name) {
        System.out.println("Add user: " + name);
    }

    @Override
    public void deleteUser(String name) {
        System.out.println("Delete user: " + name);
    }
}

3.我们定义一个动态代理类

package com.kang.spring.proxy;

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

/**
 * @Author Emperor Kang
 * @ClassName JdkProxy
 * @Description 我们定义一个动态代理类
 * @Date 2023/12/26 16:16
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
public class JdkProxy implements InvocationHandler {
    /**
     * 目标对象
     */
    private Object target;

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


    /**
     * 方法执行
     * @param proxy        调用方法的代理实例(代理类)
     * @param method       对应于在代理实例上调用的接口方法的方法实例。方法对象的声明类将是声明方法的接口,这个接口可能是代理接口的父接口,代理类通过代理接口继承方法。
     * @param args         一个对象数组,包含在代理实例上的方法调用中传递的参数值;如果接口方法没有参数,则为null。基本类型的参数包装在适当的基本包装类的实例中,如java.lang.Integer或java.lang.Boolean。
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method " + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("After method " + method.getName());
        return result;
    }
}

在该代理类中,我们实现了InvocationHandler接口,并重写了invoke方法,该方法中实现了目标对象方法的前置和后置处理,即在目标对象方法执行前输出"Before method",在方法执行后输出"After method",并返回执行结果。

最后,在main方法中,我们首先创建了一个目标对象userService,然后创建了一个代理对象proxy,并将userService作为参数传递给代理对象的构造函数中。通过调用Proxy类的静态方法newProxyInstance,我们可以动态地创建代理对象。该方法的第一个参数是目标对象的类加载器,第二个参数是目标对象实现的接口,第三个参数是InvocationHandler对象。最后,我们调用代理对象的addUser和deleteUser方法,代理对象会在执行目标对象方法前后输出相应的信息。

Proxy.invoke

42.动态代理_第1张图片

 /**
     * 方法执行
     * @param proxy        调用方法的代理实例(代理类)
     * @param method       对应于在代理实例上调用的接口方法的方法实例。方法对象的声明类将是声明方法的接口,这个接口可能是代理接口的父接口,代理类通过代理接口继承方法。
     * @param args         一个对象数组,包含在代理实例上的方法调用中传递的参数值;如果接口方法没有参数,则为null。基本类型的参数包装在适当的基本包装类的实例中,如java.lang.Integer或java.lang.Boolean。
     * @return
     * @throws Throwable
     */

Proxy.newProxyInstance

       /**
         * ClassLoader loader:定义代理类的类加载器
         * Class[] interfaces:代理类要实现的接口列表
         * InvocationHandler h:将方法调用分派给它的调用处理程序(就是调用目标对象方法本身)
         */
        UserService proxy = (UserService) Proxy.newProxyInstance(
                UserService.class.getClassLoader(),  // 类加载器
                new Class[] { UserService.class },   // class类
                handler);                            // 执行的方法
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {}

执行结果

Before method addUser
Add user: Alice
After method addUser

Before method deleteUser
Delete user: Bob
After method deleteUser

cglib动态代理

CGlib动态代理是一种基于字节码技术实现的动态代理方式,它的原理是通过生成被代理类的子类来实现代理的功能,因此,它不需要像JDK动态代理那样必须实现一个接口,可以代理任何类。

1.定义一个计算器类

package com.kang.spring.pojo;

/**
 * @Author Emperor Kang
 * @ClassName Calculator
 * @Description 定义一个计算器类
 * @Date 2023/12/27 9:47
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }

    public int sub(int a, int b) {
        return a - b;
    }
}

2.CalculatorInterceptor是一个方法拦截器,用于在调用被代理对象的方法前后打印一些日志。

package com.kang.spring.cglib;

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

import java.lang.reflect.Method;

/**
 * @Author Emperor Kang
 * @ClassName CalculatorInterceptor
 * @Description  定义方法拦截器
 * @Date 2023/12/27 10:03
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
public class CalculatorInterceptor implements MethodInterceptor {

    /**
     * 被代理的目标对象
     */
    private Object target;

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

    /**
     * 拦截
     * @param o             生成的代理对象
     * @param method        要执行的方法对象
     * @param args          执行方法的参数
     * @param methodProxy   生成的代理方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("执行方法 " + method.getName() + " 前...");
        Object result = methodProxy.invoke(target, args); // 通过代理对象调用方法
        System.out.println("执行方法 " + method.getName() + " 后...");
        return result;
    }
}

42.动态代理_第2张图片

3.main

package com.kang.spring;

import com.kang.spring.cglib.CalculatorInterceptor;
import com.kang.spring.pojo.Calculator;
import net.sf.cglib.proxy.Enhancer;

/**
 * @Author Emperor Kang
 * @ClassName CglibMain
 * @Description TODO
 * @Date 2023/12/27 10:07
 * @Version 1.0
 * @Motto 让营地比你来时更干净
 */
public class CglibMain {
    public static void main(String[] args) {
        Calculator calculator = new Calculator();

        // 创建一个Enhancer对象,用于生成代理类   enhancer:增强器
        Enhancer enhancer = new Enhancer();
        /**
         * 需要注意的是,由于CGlib动态代理是通过生成被代理类的子类来实现代理的功能,因此,如果被代理类是final类,那么就无法生成其子类,也就无法使用CGlib动态代理。
         */
        enhancer.setSuperclass(Calculator.class); // 设置父类
        enhancer.setCallback(new CalculatorInterceptor(calculator)); // 设置方法拦截器

        // 生成代理类并创建代理对象
        Calculator proxy = (Calculator) enhancer.create();

        // 调用代理对象的方法
        int result = proxy.add(1, 2);
        System.out.println("result = " + result);

        result = proxy.sub(3, 2);
        System.out.println("result = " + result);
    }
}

执行结果

执行方法 toString 前...
执行方法 toString 后...
执行方法 add 前...
执行方法 toString 前...
执行方法 toString 后...
执行方法 add 后...
result = 3
执行方法 sub 前...
执行方法 toString 前...
执行方法 toString 后...
执行方法 sub 后...
result = 1

jdk动态代理和cglib动态代理的区别

区别:

  1. JDK动态代理只能代理实现了接口的类,而不能代理普通的类。而CGlib动态代理可以代理普通的类。
  2. JDK动态代理使用Java的反射机制来生成代理类,而CGlib动态代理使用ASM框架来生成代理类,因此CGlib动态代理的效率比JDK动态代理要高。
  3. JDK动态代理生成的代理类是在内存中动态生成的类,而CGlib动态代理生成的代理类是在磁盘上写入文件,然后再加载到内存中的类。因此,CGlib动态代理的代理类在生成时需要更多的时间和空间。

CGlib动态代理示例:

首先,我们需要引入cglib的依赖,比如在Maven中可以添加以下依赖:

<dependency>
    <groupId>cglibgroupId>
    <artifactId>cglibartifactId>
    <version>3.3.0version>
dependency>

接下来,我们定义一个接口:

public interface UserService {
    void addUser(String username, String password);
}

然后,我们实现这个接口:

public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username, String password) {
        System.out.println("Add user: " + username + " with password: " + password);
    }
}

现在,我们要使用CGlib动态代理来代理这个实现类。首先,我们定义一个代理类:

public class UserServiceProxy implements MethodInterceptor {
    private Object target;

    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before method " + method.getName());
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("After method " + method.getName());
        return result;
    }
}

在这个代理类中,我们实现了MethodInterceptor接口,这个接口有一个方法intercept,这个方法就是我们要在代理方法执行前后添加的逻辑。在这个方法中,我们使用了MethodProxy类来执行实际的方法调用。

在getInstance方法中,我们使用Enhancer类来创建代理对象。其中,setSuperclass方法用来设置代理对象的父类,setCallback方法用来设置代理对象的拦截器。

现在,我们可以使用代理对象来调用addUser方法了:

public class Main {
    public static void main(String[] args) {
        UserService userService = (UserService) new UserServiceProxy().getInstance(new UserServiceImpl());
        userService.addUser("John", "123456");
    }
}

这个示例中,我们通过代理类UserServiceProxy来代理实现类UserServiceImpl,并且在代理方法执行前后添加了逻辑,最终成功输出了方法调用前后的信息。

你可能感兴趣的:(JAVA知识回顾,java)