面试题答案
一键面试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动态代理无法直接实现。因为它主要是针对接口方法的代理,无法对类的构造过程进行干涉。