Skip to main content

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

  1. 动态代理
  • 在运行期间,为相应的接口动态生成对应的代理对象,将横切关注点逻辑封装到动态代理的InvocationHandler中,然后在系统运行期间,根据横切关注点需要织入的模块位置,将横切逻辑织入到相应的代理类中。
  1. 动态字节码增强
  • 使用ASM或者CGLIB等Java工具库,在程序运行期间,动态构建字节码的class文件。
  • 在运行期间通过动态字节码增强技术织入横切逻辑,为这些系统模块类生成相应的子类,而将横切逻辑加到这些子类中,让应用程序的执行期间使用的是这些动态生成的子类,从而达到将横切逻辑织入系统的目的。
  • 如果需要扩展的类以及类中的实例方法等声明为final的话,则无法对其进行子类化的扩展。Spring AOP在无法使用动态代理机制进行AOP功能的扩展的时候,会使用CGLIB库的动态字节码增强技术来实现AOP的扩展。
  1. java代码生成
  • EJB容器根据部署描述符文件提供了织入信息,会为相应的功能模块类根据描述符所提供的信息生成对应的java代码,然后通过部署工具或者部署接口编译java代码生成对应的java类。之后部署到EJB容器的功能模块类就可以正常工作了。
  1. 自定义类加载器
  • 所有的java程序的class都要通过相应的类加载器(Classloader)加载到Java虚拟机之后才可以运行。默认的类加载器会读取class字节码文件,然后按照class字节码规范,解析并加载这些class 文件到虚拟机运行。如果我能够在这个class加载到虚拟机运行期间,将横切逻辑织入到class文件的话,是不是就完成了AOP和OPP的融合呢?
  • 我们可以通过自定义类加载器的方式完成横切逻辑到系统的织入,自定义类加载器通过读取外部文件规定的织入规则和必要信息,在加载class文件期间就可以将横切逻辑添加到系统模块类的现有逻辑中,然后将改动后的class交给java 虚拟机运行。通过类加载器,我们基本可以对大部分类以及相应的实例进行织入,功能于之前的几种方式相比当然强大很多。不过这种方式最大的问题就是类加载器本身的使用。某些应用服务器会控制整个的类加载体系,所以,在这样的场景下会造成一定的问题。
  • Jboss AOP 和已经并入AspectJ项目的AspectWerkz框架都是采用自定义类加载器的方式实现。
  1. 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();
}
}