MST
星途 面试题库

面试题:Java中静态代理与动态代理的区别及实现方式

请阐述Java中静态代理和动态代理在原理、使用场景、实现过程方面的区别,并分别给出简单的代码示例来实现一个代理模式,假设已有一个接口`UserService`及其实现类`UserServiceImpl`,包含方法`login(String username, String password)`。
21.3万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

静态代理和动态代理的区别

原理区别

  • 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在。代理类和目标类实现相同的接口,代理类内部持有目标类的实例,通过构造函数传入,在代理类的方法中调用目标类的对应方法,同时可以在前后添加额外逻辑。
  • 动态代理:在程序运行时,运用反射机制动态创建代理类的实例。JDK动态代理是基于接口实现,通过Proxy类的newProxyInstance方法创建代理对象,该方法接收类加载器、接口数组和InvocationHandler对象作为参数。InvocationHandler接口的实现类负责处理代理对象的方法调用,在invoke方法中通过反射调用目标对象的方法并可添加额外逻辑。CGLIB动态代理是基于继承,为目标类创建一个子类,在子类中重写父类方法来实现代理功能。

使用场景区别

  • 静态代理:适用于代理类和目标类关系固定,业务场景简单且代理类数量有限的情况。比如,在一些小型项目中,对特定的几个服务类进行简单的日志记录、权限验证等功能。
  • 动态代理:适用于代理类和目标类关系不固定,需要根据不同情况动态创建代理的场景。例如,在大型框架中,如Spring AOP,需要对各种不同的业务对象进行切面逻辑处理,动态代理能够灵活地为不同对象创建代理。

实现过程区别

  • 静态代理:需要手动编写代理类,实现与目标类相同的接口,在代理类的方法中调用目标类的方法,并添加额外逻辑。
  • 动态代理:JDK动态代理通过Proxy类和InvocationHandler接口实现,无需手动编写代理类,只需要实现InvocationHandler接口的invoke方法;CGLIB动态代理通过继承目标类生成代理类,需要引入CGLIB库,通过Enhancer类来创建代理对象。

代码示例

静态代理示例

  1. 定义UserService接口
public interface UserService {
    boolean login(String username, String password);
}
  1. 实现UserServiceImpl
public class UserServiceImpl implements UserService {
    @Override
    public boolean login(String username, String password) {
        System.out.println("用户 " + username + " 尝试登录,密码:" + password);
        return "admin".equals(username) && "123456".equals(password);
    }
}
  1. 创建静态代理类UserServiceProxy
public class UserServiceProxy implements UserService {
    private UserService userService;

    public UserServiceProxy(UserService userService) {
        this.userService = userService;
    }

    @Override
    public boolean login(String username, String password) {
        System.out.println("开始验证权限...");
        boolean result = userService.login(username, password);
        System.out.println("登录结果:" + result);
        return result;
    }
}
  1. 测试静态代理
public class StaticProxyTest {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        UserServiceProxy proxy = new UserServiceProxy(userService);
        proxy.login("admin", "123456");
    }
}

动态代理示例(JDK动态代理)

  1. 定义UserService接口和UserServiceImpl类(同静态代理示例)
  2. 创建InvocationHandler实现类UserServiceInvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class UserServiceInvocationHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("开始验证权限...");
        Object result = method.invoke(target, args);
        System.out.println("登录结果:" + result);
        return result;
    }

    public Object getProxy() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }
}
  1. 测试JDK动态代理
public class JdkDynamicProxyTest {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        UserServiceInvocationHandler handler = new UserServiceInvocationHandler(userService);
        UserService proxy = (UserService) handler.getProxy();
        proxy.login("admin", "123456");
    }
}

动态代理示例(CGLIB动态代理)

  1. 引入CGLIB依赖(如果使用Maven,在pom.xml中添加如下依赖)
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
  1. 定义UserService接口和UserServiceImpl类(同静态代理示例),但这里UserServiceImpl不需要实现接口(CGLIB基于继承)
  2. 创建MethodInterceptor实现类UserServiceMethodInterceptor
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class UserServiceMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开始验证权限...");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("登录结果:" + result);
        return result;
    }
}
  1. 测试CGLIB动态代理
import net.sf.cglib.proxy.Enhancer;

public class CglibDynamicProxyTest {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserServiceImpl.class);
        enhancer.setCallback(new UserServiceMethodInterceptor());
        UserServiceImpl proxy = (UserServiceImpl) enhancer.create();
        proxy.login("admin", "123456");
    }
}