我对spring的认识是从《Spring 技术内幕》开始的,这本书也是极力推荐给大家的;它采用的是自底而上的一个种方式来为我们一步一步解读spring的设计原理,很清晰的展示了spring运行时方法调用的过程,同时也有上层实现的设计原理剖析。
一、概述AOP
AOP技术其实是解决了在面向对象设计上没有关联的几个类,却需要共同调用某一块代码逻辑的现象,最最常见的可就是日志的输出打印了。
AOP的实现技术
AspectJ,源代码和字节码级别的编织器,需要使用AspectJ语言和Acj编译器。
AspectWerkz AOP框架,使用字节码动态编织器和XML配置
JBoss-AOP, 基于拦截器和元数据的AOP框架,运行在JBoss应用服务器上
BCEL, Java字节码操作类库
Javassist, Java字节码操作类库
注:摘自《Spring 技术内幕》。只做一个简单的了解,实现AOP的技术方案其实有很多种
二、AspectJ、JDK_Proxy、CGLIB_Proxy使用
注:项目搭建、代码示例使用IntelliJ IDEA;项目源码地址统一写在了结尾
1. AspectJ
首先需要在https://www.eclipse.org/aspectj/downloads.php官网下载aspectj.jar安装包;我这里下载的是aspectj-1.9.1.jar,使用命令java -jar aspectj-xxx.jar 一直下一步安装就好了。
这里要记下具体安装的目录,因为后面我们需要配置编译环境
环境配置好后我们就可以进行代码的编写了。创建一个HellowWorld_AspectJ_Main
程序运行的入口
public class HellowWorld_AspectJ_Main {
public static void main(String[] args) {
System.out.println("mian method is print");
}
}
再创建一个定位切面和执行逻辑的类
HellowWorld_Pointcut
public aspect HellowWorld_Pointcut {
pointcut helloWorld(): execution(* HellowWorld_AspectJ_Main.main(..));
before(): helloWorld(){
System.out.println("enter the HellowWorld_AspectJ_Main aop");
}
}
直接运行就可以了
最后要指出的重点是编译后的两个类
HellowWorld_AspectJ_Main.class 反编译后
public class HellowWorld_AspectJ_Main {
public HellowWorld_AspectJ_Main() {
}
public static void main(String[] args) {
HellowWorld_Pointcut.aspectOf().ajc$before$HellowWorld_Pointcut$1$4303d2e1(); //编译后自动添加的代码
System.out.println("mian method is print");
}
}
HellowWorld_Pointcut.class 反编译后
import org.aspectj.lang.NoAspectBoundException;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class HellowWorld_Pointcut {
static {
try {
ajc$postClinit();
} catch (Throwable var1) {
ajc$initFailureCause = var1;
}
}
public HellowWorld_Pointcut() {
}
@Before(
value = "helloWorld()",
argNames = ""
)
public void ajc$before$HellowWorld_Pointcut$1$4303d2e1() {
System.out.println("enter the HellowWorld_AspectJ_Main aop");
}
public static HellowWorld_Pointcut aspectOf() {
if (ajc$perSingletonInstance == null) {
throw new NoAspectBoundException("HellowWorld_Pointcut", ajc$initFailureCause);
} else {
return ajc$perSingletonInstance;
}
}
public static boolean hasAspect() {
return ajc$perSingletonInstance != null;
}
}
2. JDK_Proxy
准备三个类,ProxyInstanceJDK定义方法的接口、ProxyInstanceJDKImpl实现类、ProxyHandlerJDK回调类,这三个类我都放在了main所在的类下面,所以没有用public修饰。
interface ProxyInstanceJDK {
void print();
}
class ProxyInstanceJDKImpl implements ProxyInstanceJDK {
@Override
public void print() {
System.out.println("target jdk class");
}
}
class ProxyHandlerJDK implements InvocationHandler {
private ProxyInstanceJDK targetClass;
public ProxyHandlerJDK(ProxyInstanceJDK targetClass) {
this.targetClass = targetClass;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("enter the jdk proxy invoke, proxy className is " + proxy.getClass().getSimpleName());
return method.invoke(targetClass, args);
}
}
main方法调用
public static void main(String[] args) throws Exception {
proxyJdk();
// printJDKProxyClassByte();
// proxyCglib();
}
具体生成代理类的过程可以查看java.lang.reflect.Proxy类的源码。我这里把最后动态生成代理类的字节码输出在了一个**.class文件中,反编译后就可以看到生成的代理类了。
/**
* 可以将生成好的代理类字节码打印出来
* @throws Exception
*/
public static void printJDKProxyClassByte() throws Exception {
byte[] classFile = ProxyGenerator.generateProxyClass("$ProxyMyTest", ProxyInstanceJDKImpl.class.getInterfaces());
FileOutputStream fos = new FileOutputStream(new File("***/example.class"));
fos.write(classFile);
fos.flush();
fos.close();
}
反编译后代理类的具体实现
import com.programmatic.springprogrammatic.ProxyInstanceJDK;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $ProxyMyTest extends Proxy implements ProxyInstanceJDK {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $ProxyMyTest(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void print() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.programmatic.springprogrammatic.ProxyInstanceJDK").getMethod("print");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
3. CGLIB_Proxy
只需要准备一个具体的方法实现类ProxyInstanceCglib
class ProxyInstanceCglib {
public void print() {
System.out.println("target cglib class");
}
}
生成代理类
public static void proxyCglib() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProxyInstanceCglib.class);
enhancer.setCallback(((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("enter the cglib proxy invoke");
return methodProxy.invokeSuper(o, objects);
}));
ProxyInstanceCglib instanceCglib = (ProxyInstanceCglib) enhancer.create();
instanceCglib.print();
System.out.println(instanceCglib.getClass().getSimpleName());
}
cglib没有获取代理类的字节码的类方法(!!确实没找到!!),不过可以通过设置
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "**/src/main/resources/templates");
将运行期间所有动态创建的类打印出来。其中ProxyInstanceCglib$$EnhancerByCGLIB$$366568d是cglib
是我们生成的代理类,源码有点长就不贴出来了,我把它上传到了github上,大家有兴趣的可以看一下。
三、结语
把这几种常用的AOP技术操作运行起来,会对我们理解spring AOP的时候起到一个支撑的作用。虽然并没有深入的研究,但或多或少的解决了“从哪里来”的问题!
aspect github项目地址:https://github.com/lotusfan/aspectj
proxy github项目地址:https://github.com/lotusfan/spring-programmatic
使用到的类:com.programmatic.springprogrammatic.ProxyTest
Cglib生成的代理类:resources/templates/com/programmatic.springprogrammatic/ProxyInstanceCglib$$EnhancerByCGLIB$$366568d