MST

星途 面试题库

面试题:Java中代理模式在实际项目中的应用

在Java开发中,代理模式有静态代理和动态代理。请详细阐述动态代理(如JDK动态代理和CGLIB动态代理)的原理、应用场景以及它们之间的区别。假设你正在开发一个电商系统,用户登录模块需要添加日志记录功能,描述如何使用代理模式实现该功能,并给出关键代码片段。
25.5万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

动态代理原理

  1. JDK动态代理
    • JDK动态代理是基于接口实现的。它利用了Java反射机制,在运行时动态生成代理类。通过Proxy类的静态方法newProxyInstance,传入类加载器、被代理类实现的接口数组以及实现了InvocationHandler接口的处理器对象。当代理对象的方法被调用时,实际上是由InvocationHandler的invoke方法来处理,在invoke方法中可以实现对目标方法的增强逻辑。
  2. CGLIB动态代理
    • CGLIB动态代理是基于继承实现的。它通过字节码生成技术,在运行时动态生成被代理类的子类作为代理类。CGLIB通过Enhancer类来创建代理对象,设置父类为被代理类,然后通过回调机制,当代理对象方法被调用时,会回调MethodInterceptor的intercept方法,在这个方法中实现对目标方法的增强。

应用场景

  1. 日志记录:在方法调用前后记录日志信息,如记录用户登录时间、操作等。
  2. 事务管理:在方法调用前后开启和提交事务,保证数据的一致性。
  3. 权限控制:在方法调用前检查用户权限,决定是否允许执行该方法。

区别

  1. 代理实现方式
    • JDK动态代理基于接口,被代理类必须实现至少一个接口。
    • CGLIB动态代理基于继承,被代理类可以不实现接口,但不能是final类(因为无法继承)。
  2. 性能
    • JDK动态代理因为基于反射,在调用次数较少时性能较好。
    • CGLIB动态代理生成字节码,在调用次数较多时性能更好,因为减少了反射带来的开销。

电商系统用户登录模块使用代理模式实现日志记录功能

  1. JDK动态代理实现
    • 定义用户登录接口:
public interface UserLoginService {
    void login(String username, String password);
}
  • 实现用户登录接口:
public class UserLoginServiceImpl implements UserLoginService {
    @Override
    public void login(String username, String password) {
        System.out.println("用户 " + username + " 登录成功");
    }
}
  • 定义InvocationHandler实现类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class LoginInvocationHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("记录登录日志,开始时间:" + System.currentTimeMillis());
        Object result = method.invoke(target, args);
        System.out.println("记录登录日志,结束时间:" + System.currentTimeMillis());
        return result;
    }
}
  • 创建代理对象:
import java.lang.reflect.Proxy;

public class JDKProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new LoginInvocationHandler(target));
    }
}
  • 使用代理对象:
public class Main {
    public static void main(String[] args) {
        UserLoginService target = new UserLoginServiceImpl();
        UserLoginService proxy = (UserLoginService) JDKProxyFactory.getProxy(target);
        proxy.login("testUser", "123456");
    }
}
  1. CGLIB动态代理实现
    • 实现用户登录类(无需接口):
public class UserLoginService {
    public void login(String username, String password) {
        System.out.println("用户 " + username + " 登录成功");
    }
}
  • 定义MethodInterceptor实现类:
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class LoginMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("记录登录日志,开始时间:" + System.currentTimeMillis());
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("记录登录日志,结束时间:" + System.currentTimeMillis());
        return result;
    }
}
  • 创建代理对象:
import net.sf.cglib.proxy.Enhancer;

public class CGLIBProxyFactory {
    public static Object getProxy(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new LoginMethodInterceptor());
        return enhancer.create();
    }
}
  • 使用代理对象:
public class Main {
    public static void main(String[] args) {
        UserLoginService proxy = (UserLoginService) CGLIBProxyFactory.getProxy(UserLoginService.class);
        proxy.login("testUser", "123456");
    }
}