MST

星途 面试题库

面试题:Java反射实现动态代理的深度剖析

Java反射机制可以用于实现动态代理。请详细说明如何利用反射来实现一个基于JDK动态代理的通用AOP(面向切面编程)框架。包括如何定义切面、切点,以及如何在代理对象的方法调用前后织入自定义逻辑,同时分析JDK动态代理在实际应用中的局限性。
35.2万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

1. 定义切面

切面是横切关注点的模块化,包含了在切点处要执行的逻辑。在Java中,可以定义一个接口来表示切面,例如:

public interface Advice {
    void before();
    void after();
}

然后实现具体的切面逻辑:

public class LoggingAdvice implements Advice {
    @Override
    public void before() {
        System.out.println("Before method execution");
    }

    @Override
    public void after() {
        System.out.println("After method execution");
    }
}

2. 定义切点

切点用于定义切面逻辑应该应用到哪些方法上。可以通过方法签名、注解等方式定义切点。例如,基于方法名定义切点:

public class MethodNamePointcut {
    private String methodName;

    public MethodNamePointcut(String methodName) {
        this.methodName = methodName;
    }

    public boolean matches(Method method) {
        return method.getName().equals(methodName);
    }
}

3. 利用反射实现JDK动态代理

JDK动态代理通过Proxy类和InvocationHandler接口来实现。InvocationHandler接口的实现类负责处理代理对象的方法调用。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

public class AopProxyFactory {
    private List<Advice> advices = new ArrayList<>();
    private List<MethodNamePointcut> pointcuts = new ArrayList<>();

    public void addAdvice(Advice advice) {
        advices.add(advice);
    }

    public void addPointcut(MethodNamePointcut pointcut) {
        pointcuts.add(pointcut);
    }

    public Object createProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        for (int i = 0; i < pointcuts.size(); i++) {
                            if (pointcuts.get(i).matches(method)) {
                                Advice advice = advices.get(i);
                                advice.before();
                                Object result = method.invoke(target, args);
                                advice.after();
                                return result;
                            }
                        }
                        return method.invoke(target, args);
                    }
                });
    }
}

4. 使用示例

public interface UserService {
    void sayHello();
}

public class UserServiceImpl implements UserService {
    @Override
    public void sayHello() {
        System.out.println("Hello, World!");
    }
}

public class Main {
    public static void main(String[] args) {
        AopProxyFactory factory = new AopProxyFactory();
        factory.addAdvice(new LoggingAdvice());
        factory.addPointcut(new MethodNamePointcut("sayHello"));

        UserService userService = new UserServiceImpl();
        UserService proxy = (UserService) factory.createProxy(userService);
        proxy.sayHello();
    }
}

5. JDK动态代理的局限性

  • 仅支持接口代理:JDK动态代理要求被代理的对象必须实现至少一个接口。如果一个类没有实现接口,就无法使用JDK动态代理。
  • 性能问题:JDK动态代理生成的代理类在方法调用时会涉及反射操作,相较于直接调用方法,性能会有所损耗。在高并发、对性能要求极高的场景下,这种性能损耗可能变得不可接受。
  • 功能局限性:对于一些复杂的AOP需求,如对构造函数的增强,JDK动态代理无法直接实现。因为它主要是针对接口方法的代理,无法对类的构造过程进行干涉。