动态代理
Spring容器在启动时,会检测到 dishFlavorMapper 需要被AOP增强(假设它已经被标记为需要增强,比如通过注解或配置)。然后,Spring容器不会直接注入原始的 dishFlavorMapper 实例,而是注入一个代理对象。
这个代理对象实现了与 原对象相同的接口,因此可以在不改变代码的情况下替换原始对象。当你通过代理对象调用方法时,比如 dishFlavorMapper.getByDishId(d.getId()),以下步骤会发生:
拦截调用:代理对象会拦截这个方法调用。
查找切面:代理对象会检查是否有配置的切面(比如 AutoFillAspect)与当前方法调用匹配。
执行通知:如果找到匹配的切面和通知,代理对象会先执行对应的通知逻辑。例如,如果有一个 @Before 通知,它会在目标方法执行之前执行。
Spring更常用的是动态代理,主要有两种实现方式:JDK动态代理和CGLIB。
JDK动态代理
JDK动态代理是基于Java反射机制实现的,只能为实现了接口的类创建代理。
实现:通过实现java.lang.reflect.InvocationHandler接口和java.lang.reflect.Proxy类来创建代理对象。
限制:被代理的类必须实现至少一个接口。
CGLIB
CGLIB(Code Generation Library)通过底层的字节码技术创建代理类的子类,因此它不需要接口。
代码举例
实现:通过扩展目标类并覆盖其非final方法来创建代理。
特点:可以代理没有实现接口的类,但无法代理final类和方法。
下面是一个使用JDK动态代理的简单例子。这个例子中,我们将创建一个简单的Hello接口,一个实现了这个接口的类HelloImpl,以及一个代理类HelloProxyHandler,它将作为我们的代理处理器。
首先,定义一个简单的接口:
public interface Hello {
void sayHello(String name);
}
然后,实现这个接口:
public class HelloImpl implements Hello {
@Override
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
}
接下来,创建代理处理器,它实现了InvocationHandler接口:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class HelloProxyHandler implements InvocationHandler {
private final Object target; // 被代理的对象
public HelloProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在调用目标方法之前,可以添加一些预处理逻辑
System.out.println("Before method call: " + method.getName());
// 调用目标对象的方法
Object result = method.invoke(target, args);
// 在调用目标方法之后,可以添加一些后处理逻辑
System.out.println("After method call: " + method.getName());
return result;
}
}
最后,创建代理对象并使用它:
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
// 创建被代理的对象
Hello hello = new HelloImpl();
// 创建代理处理器
InvocationHandler handler = new HelloProxyHandler(hello);
// 使用Proxy类创建代理对象
Hello proxyHello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(), // 类加载器
new Class<?>[]{Hello.class}, // 代理需要实现的接口
handler // 代理处理器
);
// 使用代理对象调用方法
proxyHello.sayHello("World");
}
}
在这个例子中,当proxyHello.sayHello(“World”)被调用时,实际上会调用HelloProxyHandler的invoke方法。invoke方法内部会执行以下步骤:
输出调用方法之前的信息。
调用实际对象hello的sayHello方法。
输出调用方法之后的信息。
运行ProxyTest类的main方法,你会看到以下输出:
Before method call: sayHello
Hello, World!
After method call: sayHello
下面是一个使用CGLIB动态代理的简单例子。在这个例子中,我们将创建一个简单的Hello类,一个实现了Hello接口的HelloImpl类,以及一个代理类HelloProxy,它将作为我们的代理处理器。
首先,定义一个简单的接口:
public interface Hello {
void sayHello(String name);
}
然后,实现这个接口:
public class HelloImpl implements Hello {
@Override
public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}
}
接下来,创建代理处理器,它实现了MethodInterceptor接口:
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class HelloProxy implements MethodInterceptor {
private final Object target; // 被代理的对象
public HelloProxy(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 在调用目标方法之前,可以添加一些预处理逻辑
System.out.println("Before method call: " + method.getName());
// 调用目标对象的方法
Object result = proxy.invokeSuper(obj, args);
// 在调用目标方法之后,可以添加一些后处理逻辑
System.out.println("After method call: " + method.getName());
return result;
}
}
最后,创建代理对象并使用它:
import net.sf.cglib.proxy.Enhancer;
public class CGLIBProxyTest {
public static void main(String[] args) {
// 创建被代理的对象
Hello hello = new HelloImpl();
// 创建代理处理器
HelloProxy handler = new HelloProxy(hello);
// 使用CGLIB创建代理对象
Hello proxyHello = (Hello) Enhancer.create(hello.getClass(), handler);
// 使用代理对象调用方法
proxyHello.sayHello("World");
}
}
在这个例子中,当proxyHello.sayHello(“World”)被调用时,实际上会调用HelloProxy的intercept方法。intercept方法内部会执行以下步骤:
输出调用方法之前的信息。
调用目标对象的方法。
输出调用方法之后的信息。
运行CGLIBProxyTest类的main方法,你会看到以下输出:
Before method call: sayHello
Hello, World!
After method call: sayHello