【一步一步学习spring】spring aop 底层实现
1. JDK的动态代理
注意一下:该动态代理只能对实现了接口的类进行代理!
使用动态代理的五大步骤
- 通过实现InvocationHandler接口来自定义自己的InvocationHandler;
- 通过Proxy.getProxyClass获得动态代理类
- 通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class)
- 通过构造函数获得代理对象并将自定义的InvocationHandler实例对象传为参数传入
- 通过代理对象调用目标方法
DEMO
package com.aop.demo1;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyTest {
public interface IHello {
void sayHello();
}
static class Hello implements IHello {
public void sayHello() {
System.out.println("Hello world!!");
}
}
// 自定义InvocationHandler
static class HWInvocationHandler implements InvocationHandler {
// 目标对象
private Object target;
public HWInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("------插入前置通知代码-------------");
// 执行相应的目标方法
Object rs = method.invoke(target, args);
System.out.println("------插入后置处理代码-------------");
return rs;
}
}
public static void main(String[] args)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
IHello ihello = (IHello) Proxy.newProxyInstance(Hello.class.getClassLoader(), Hello.class.getInterfaces(), // 一组接口
new HWInvocationHandler(new Hello())); // 自定义的InvocationHandler
ihello.sayHello();
}
}
2. CGLIB生成代理
- 对于不使用接口的业务类,无法使用JDK动态代理
- CGLIB采用非常底层字节码技术,可以为一个类创建子类,解决无接口的代理问题
使用CGlib代理的四大步骤
- 创建Enhancer核心类
- 设置父类为目标类
- 设置回调,其中实现具体要增强的逻辑
- 生成代理
DEMO
package com.aop.demo1;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class CglibTest {
public void test(){
System.out.println("hello world");
}
public static void main(String[] args) {
// 1.创建核心类
Enhancer enhancer = new Enhancer();
// 2.设置父类
enhancer.setSuperclass(CglibTest.class);
// 3.设置回调
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before method run...");
Object result = proxy.invokeSuper(obj, args);
System.out.println("after method run...");
return result;
}
});
// 4.生成代理
CglibTest sample = (CglibTest) enhancer.create();
sample.test();
}
}
3. 总结
- spring在运行期,生成动态代理对象,不需要特殊的编译器
- spring aop的底层就是通过jdk动态代理或cglib动态代理技术为目标Bean执行横向织入
- 若目标对象实现了接口,spring使用jdk的java.lang.reflect.Proxy类代理
- 若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
- 应该优先对接口创建代理,便于程序解耦维护
- 标记为final的方法不能被代理。
- spring只支持方法连接点,不提供属性连接点
- 优缺点:
- CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,
- 但是CGLib在创建代理对象时所花费的时间却比JDK多得多,
- 所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。