Joinpoint
本文相关 代码(来自官方源码spring-test模块)请参见spring-framework org.springframework.mylearntest包下。
JAVA中AOP实现方式
统称能够实现AOP的语言为AOL,即(Aspect-Oriented Language),其他Aspectj
- AspectC
- AspectC++
- Aspect.Net
- AspectL(Lisp)
- AspectPHP
- ......
JAVA中AOP
- 动态代理
- 在运行期间,为相应的接口动态生成对应的代理对象,将横切关注点逻辑封装到动态代理的InvocationHandler中,然后在系统运行期间,根据横切关注点需要织入的模块位置,将横切逻辑织入到相应的代理类中。
- 动态字节码增强
- 使用ASM或者CGLIB等Java工具库,在程序运行期间,动态构建字节码的class文件。
- 在运行期间通过动态字节码增强技术织入横切逻辑,为这些系统模块类生成相应的子类,而将横切逻辑加到这些子类中,让应用程序的执行期间使用的是这些动态生成的子类,从而达到将横切逻辑织入系统的目的。
- 如果需要扩展的类以及类中的实例方法等声明为final的话,则无法对其进行子类化的扩展。Spring AOP在无法使用动态代理机制进行AOP功能的扩展的时候,会使用CGLIB库的动态字节码增强技术来实现AOP的扩展。
- java代码生成
- EJB容器根据部署描述符文件提 供了织入信息,会为相应的功能模块类根据描述符所提供的信息生成对应的java代码,然后通过部署工具或者部署接口编译java代码生成对应的java类。之后部署到EJB容器的功能模块类就可以正常工作了。
- 自定义类加载器
- 所有的java程序的class都要通过相应的类加载器(Classloader)加载到Java虚拟机之后才可以运行。默认的类加载器会读取class字节码文件,然后按照class字节码规范,解析并加载这些class 文件到虚拟机运行。如果我能够在这个class加载到虚拟机运行期间,将横切逻辑织入到class文件的话,是不是就完成了AOP和OPP的融合呢?
- 我们可以通过自定义类加载器的方式完成横切逻辑到系统的织入,自定义类加载器通过读取外部文件规定的织入规则和必要信息,在加载class文件期间就可以将横切逻辑添加到系统模块类的现有逻辑中,然后将改动后的class交给java 虚拟机运行。通过类加载器,我们基本可以对大部分类以及相应的实例进行织入,功能于之前的几种方式相比当然强大很多。不过这种方式最大的问题就是类加载器本身的使用。某些应用服务器会控制整个的类加载体系,所以,在这样的场景下会造成一定的问题。
- Jboss AOP 和已经并入AspectJ项目的AspectWerkz框架都是采用自定义类加载器的方式实现。
- AOL扩展
- AOL扩展是最强大、也是最难掌握的一种方式,我们之前提到AspectJ就属于这种方式。在这种方式中,AOP的各种概念在AOL中大都有一一对应的实体。我们可以使用扩展过的AOL,实现任何AOP概念实体甚至OPP 概念实体,比如Aspect以及Class。所有的AOP概念在AOL中得到了最完美的表达。
- 采用扩展的AOL,在AOP概念的表达上颇具实例,使得AOP涉及的所有横切关注点逻辑在进行织入之前,可以自由自在地存活在自己的“国度中”。而像“编译到静态类可以提升系统运行性能”,“java 虚拟机可以像加载平常类那种,加载已经织入相应逻辑的AO组件所在的文件并运行”等特点。使用这种方式,需要学习一门扩展的AOL语言。
Joinpoint
-
Joinpoint 切点
-
Pointcut 切点表达式
- 直接指定Joinpoint所在的方法名称
- 正则表达式:Jboss、Spring AOP、AspectWerkz等均支持
- 使用特定的Pointcut表达语言:Spring 2.0以后,借助于AspectJ的Pointcut表述语言解释器,支持使用AspectJ的Pointcut表述语言来指定Pointcut。
静态代理
IRequestable
package org.springframework.mylearntest.aop.staticproxy;
public interface IRequestable {
void request();
}
package org.springframework.mylearntest.aop.staticproxy;
public class RequestableImpl implements IRequestable{
@Override
public void request() {
System.out.println(" request process in RequestableImpl");
}
}
ServiceControlRequestableProxy
package org.springframework.mylearntest.aop.staticproxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ServiceControlRequestableProxy implements IRequestable{
private static final Logger logger = LoggerFactory.getLogger(ServiceControlRequestableProxy.class);
private IRequestable requestable;
public ServiceControlRequestableProxy(IRequestable target) {
this.requestable = target;
}
@Override
public void request() {
System.out.println("request process in ServiceControlRequestableProxy");
requestable.request();
}
public static void main(String[] args) {
IRequestable target = new RequestableImpl();// 需要被代理的对象
IRequestable proxy = new ServiceControlRequestableProxy(target); // 以构造方法形式将被代理对象传入代理者中
proxy.request();// 让代理者去处理请求
}
}
动态代理
动态代理机制主要由一个类和一个接口组成,即java.lang.reflect.Proxy类和java.lang.reflect.InvocationHadler接口。
RequestCtrlInvocationHandler
package org.springframework.mylearntest.aop.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class RequestCtrlInvocationHandler implements InvocationHandler {
private Object target;
public RequestCtrlInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("reflect invoke before target method");
if ("request".equals(method.getName())) {
System.out.println("dynamic proxy target method");
return method.invoke(target, args);
}
return null;
}
}
Test4DynamicProxy
package org.springframework.mylearntest.aop.dynamicproxy;
import org.springframework.mylearntest.aop.staticproxy.IRequestable;
import org.springframework.mylearntest.aop.staticproxy.RequestableImpl;
import java.lang.reflect.Proxy;
@SuppressWarnings("rawtypes")
public class Test4DynamicProxy {
public static void main(String[] args) {
// arg1:类加载器 arg2:接口信息 arg3:实现InvocationHandler的类 并传入需要代理的对象
IRequestable requestable = (IRequestable) Proxy.newProxyInstance(
Test4DynamicProxy.class.getClassLoader(),
new Class[]{IRequestable.class},
new RequestCtrlInvocationHandler(new RequestableImpl()));
requestable.request();
}
}
如果想深入了解动态代理,请阅读《java reflect in action》。
CGLIB字节码生成
需要使用CGLIB扩展子类,首先需要实现一个net.sf.cglib.proxy.Callback,不过更多的时候,我们会直接使用net.sf.cglib.proxy.MethodInterceptor接口(MethodInterceptor扩展了Callback接口)。
Requestable
package org.springframework.mylearntest.aop.CGLIBClassGenerate;
public class Requestable {
public void request(){
System.out.println("req in requestable without implement any interface");
}
}
RequestCtrlCallback
package org.springframework.mylearntest.aop.CGLIBClassGenerate;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class RequestCtrlCallback implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if (method.getName().equals("request")) {
System.out.println("proxy generated by cglib intercept method request");
return methodProxy.invokeSuper(o, objects);
}
return null;
}
}
Test4CGLIB
package org.springframework.mylearntest.aop.CGLIBClassGenerate;
import org.springframework.cglib.proxy.Enhancer;
public class Test4CGLIB {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Requestable.class);
enhancer.setCallback(new RequestCtrlCallback());
Requestable proxy = (Requestable) enhancer.create();
proxy.request();
}
}