AOP 的两种织入原理
1.静态AOP (AspectJ)
静态AOP:在编译期,切面直接以字节码的形式编译到目标字节码文件中。 AspectJ 属于静态AOP,是在编译时进行增强,会在编译的时候将AOP逻辑织入到代码中,需要专有的编译器和织入器。
优点:被织入的类性能不受影响。 缺点:不够灵活
javassist Java 字节码指令
2.动态AOP
JDK 动态代理
在正常开发中, 基于 依赖倒置原则(Dependence Inversion Principle), 通常需要接口解决 上层与类的耦合问题; 即A类调用B类, 为了解决A依赖与B, 抽取B做成C接口. 变成A依赖以C接口;
在运行期,目标类加载后,为接口动态生成代理类,将切面植入到代理类中。 Java从1.3引入动态代理。实现原理是为被代理的业务接口生成代理类,将AOP逻辑写入到代理类中,在运行时动态织入AOP,使用反射执行织入的逻辑。 主要实现方式依赖 java.lang.reflect 包下的InvocationHandler和Proxy类。
优点:Java标准库原生支持,使用简单,无需引用额外的包。相对于静态AOP更灵活。 缺点:带代理的类必须是接口,灵活性受到一些限制;使用反射会影响一些性能。
JDK 动态代理必须需要接口, 它的原理是动态的生成class, 类似实现原始类的接口
/**
* JDK 动态代理实现
*/
public static final void JDK_PROXY(){
/**
* 目标类
*/
ProductServiceInter carService = new CarService();
// 拦截回调处理器
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result ;
System.out.println("增强处理 前");
//调用方法
result = method.invoke(carService, args);
System.out.println("增强处理 后");
return result;
}
};
ProductServiceInter proxyInstance = (ProductServiceInter) Proxy.newProxyInstance(CarService.class.getClassLoader()
, CarService.class.getInterfaces(), handler);
proxyInstance.doProduct("白色");
}CGLIB 动态代理
CGLIB(Code Generation Library)是一个开源项目,是一个强大的, 高性能, 高质量的Code生成类库, 它可以在运行期扩展Java类与实现Java接口, 其实就是cglib可以在运行时动态生成字节码;
使用CGLib实现动态代理, 完全不受代理类必须实现接口的限制, 而且CGLib底层采用ASM字节码生成框架, 使用字节码技术生成代理类, 比使用Java反射效率要高; 唯一需要注意的是, CGLib不能对声明为final的方法进行代理, 因为CGLib原理是动态生成被代理类的子类;
它的原理是: 动态的创建一份class(代理类) 继承/实现被代理的 类/接口
-
net.sf.cglib.proxy.Enhancer– 主要的增强类 -
net.sf.cglib.proxy.MethodInterceptor– 主要的方法拦截类 它是Callback接口的子接口, 需要用户实现 -
net.sf.cglib.proxy.MethodProxyJDK的java.lang.reflect.Method类的代理类 可以方便的实现对源对象方法的调用,如使用:Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象, 也不会出现死循环的问题; -
net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型 它经常被基于代理的AOP用来实现拦截(intercept)方法的调用; 这个接口只定义了一个方法,public Object intercept(Object object, java.lang.reflect.Method method,Object[] args, MethodProxy proxy) throws Throwable;
第一个参数是代理对像, 第二和第三个参数分别是拦截的方法和方法的参数; 原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用, 或者使用net.sf.cglib.proxy.MethodProxy对象调用; -
sf.cglib.proxy.MethodProxy通常被首选使用, 因为它更快; -
一个动态代理例子
/**
* CGLIB 动态代理实现
*/
public static final void CJLIB_PROXY(){
Enhancer enhancer = new Enhancer();
/**
* 设置父类
*/
enhancer.setSuperclass(CarService.class);
// 拦截回调处理器
MethodInterceptor handler = new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
Object result ;
System.out.println("增强处理 前");
//调用父对象 方法
result = proxy.invokeSuper(obj, args);
System.out.println("增强处理 后");
return result;
}
};
// 设置 拦截回调处理器; 它可以是: * @see MethodInterceptor * @see NoOp * @see LazyLoader * @see Dispatcher * @see org.springframework.cglib.proxy.InvocationHandler * @see FixedValue
enhancer.setCallback(handler);
CarService proxyInstance = (CarService) enhancer.create();
proxyInstance.doProduct("粉色");
}