当前位置: 首页 > 图灵资讯 > java面试题> 金三银四精选java面试题-怎么实现动态代理?

金三银四精选java面试题-怎么实现动态代理?

来源:图灵教育
时间:2023-11-28 10:28:40
 

怎么实现动态代理?

代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。

举个例子:

在租房的时候,有的人会通过房东直租,有的人会通过中介租房。中介一般是不是会提供一些额外的服务,这里的中介就相当于代理。

动态代理实现方式:

JDK实现:JDK Proxy基于反射

第三方类实现:CGLIB基于ASM(一个 Java 字节码操作框架)

如何实现动态代理:

  • 基于接口的JDK动态代理
    • 定义目标类,即被代理的类。
    • 通过实现InvocationHandler接口来自定义自己的InvocationHandler;重写invoke方法,在此方法中定义增强逻辑。
    • 通过Proxy.newProxyInstance方法获得代理对象。
    • 通过代理对象调用目标方法;
/**
 * @author 百里
 */
public interface UserService {
    void readBook();
}

/**
 * @author 百里
 */
public class UserServiceImpl implements UserService {
    @Override
    public void readBook() {
        System.out.println("阅读三国演义!!!");
    }
}

/**
 * @author 百里
 */
public class UserProxy implements InvocationHandler {

    private final Object object;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置处理");
        Object invoke = method.invoke(object, args);
        System.out.println("后置处理");
        return invoke;
    }
}

/**
 * JDK Proxy
 */
@Test
public void proxyDemo(){
    UserService userService = (UserService) Proxy.newProxyInstance(  //创建代理对象
            UserServiceImpl.class.getClassLoader(),     // 加载接口的类加载器
            UserServiceImpl.class.getInterfaces(),     // 接口
            new UserProxy(new UserServiceImpl()));  // 代理类,传入需要被代理类的实现类
    userService.rentingHouse();
}
  • 基于类的CGLIB动态代理
    • 引入CGLIB的相关依赖。
    • 定义目标类,即被代理的类。
    • 创建代理类实现CGLIB的MethodInterceptor接口,并重写intercept方法,在此方法中定义增强逻辑。
    • 使用Enhancer类创建代理对象,设置目标类、回调对象等参数。
    • 调用代理对象的方法,实现代理行为。
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm</artifactId>
    <version>9.1</version>
</dependency>
/**
 * @author 百里
 */
public class UserCgLibProxy implements MethodInterceptor {
    private final Objec object;

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

    public Object createProxy() {
        // 通过CGLIB动态代理获取代理对象的过程
        Enhancer enhancer = new Enhancer();
        // 设置enhancer对象的父类
        enhancer.setSuperclass(object.getClass());
        // 设置enhancer的回调对象
        enhancer.setCallback(new UserCgLibProxy(object.getClass()));
        // 返回代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("前置处理");
        Object invoke = methodProxy.invokeSuper(o, objects);
        System.out.println("后置处理");
        return invoke;
    }
}

/**
 * CGLIB
 */
@Test
public void cglibProxyDemo(){
    UserCgLibProxy userCgLibProxy = new UserCgLibProxy(new UserServiceImpl());
    UserServiceImpl proxy = (UserServiceImpl)userCgLibProxy.createProxy();
    proxy.rentingHouse();
}

JDK Proxy与CGLIB的区别:

  • 基于接口 vs. 基于类:
    • JDK Proxy 只能代理接口类型,它通过实现指定接口并生成代理对象来实现代理功能。
    • CGLIB 可以代理普通的类,它通过继承目标类,并在子类中重写方法来实现代理。
  • 实现方式:
    • JDK Proxy 是基于反射机制实现的,它利用 Java 的反射 API 动态生成代理对象。
    • CGLIB 使用了字节码生成库,直接操作字节码生成代理类。相比于 JDK 代理的反射调用,CGLIB 的方法调用更快速。
  • 性能:
    • 由于 CGLIB 是直接对字节码进行操作,所以在创建和执行代理对象时通常比 JDK 代理更快速。
    • JDK Proxy 的性能略低,因为它涉及到反射调用的开销。JDK8 版本已经优化,性能与 CGLIB 差不多
  • 库依赖:
    • JDK Proxy 是 Java 标准库的一部分,无需额外的依赖。
    • CGLIB 需要引入相关的第三方库。