我對(duì)spring的認(rèn)識(shí)是從《Spring 技術(shù)內(nèi)幕》開(kāi)始的申尼,這本書(shū)也是極力推薦給大家的;它采用的是自底而上的一個(gè)種方式來(lái)為我們一步一步解讀spring的設(shè)計(jì)原理垫桂,很清晰的展示了spring運(yùn)行時(shí)方法調(diào)用的過(guò)程师幕,同時(shí)也有上層實(shí)現(xiàn)的設(shè)計(jì)原理剖析。
一诬滩、概述AOP
AOP技術(shù)其實(shí)是解決了在面向?qū)ο笤O(shè)計(jì)上沒(méi)有關(guān)聯(lián)的幾個(gè)類(lèi)霹粥,卻需要共同調(diào)用某一塊代碼邏輯的現(xiàn)象,最最常見(jiàn)的可就是日志的輸出打印了疼鸟。
AOP的實(shí)現(xiàn)技術(shù)
AspectJ后控,源代碼和字節(jié)碼級(jí)別的編織器,需要使用AspectJ語(yǔ)言和Acj編譯器空镜。
AspectWerkz AOP框架浩淘,使用字節(jié)碼動(dòng)態(tài)編織器和XML配置
JBoss-AOP矾利, 基于攔截器和元數(shù)據(jù)的AOP框架,運(yùn)行在JBoss應(yīng)用服務(wù)器上
BCEL馋袜, Java字節(jié)碼操作類(lèi)庫(kù)
Javassist, Java字節(jié)碼操作類(lèi)庫(kù)
注:摘自《Spring 技術(shù)內(nèi)幕》舶斧。只做一個(gè)簡(jiǎn)單的了解欣鳖,實(shí)現(xiàn)AOP的技術(shù)方案其實(shí)有很多種
二、AspectJ茴厉、JDK_Proxy泽台、CGLIB_Proxy使用
注:項(xiàng)目搭建、代碼示例使用IntelliJ IDEA矾缓;項(xiàng)目源碼地址統(tǒng)一寫(xiě)在了結(jié)尾
1. AspectJ
首先需要在https://www.eclipse.org/aspectj/downloads.php官網(wǎng)下載aspectj.jar安裝包怀酷;我這里下載的是aspectj-1.9.1.jar,使用命令java -jar aspectj-xxx.jar 一直下一步安裝就好了嗜闻。
這里要記下具體安裝的目錄蜕依,因?yàn)楹竺嫖覀冃枰渲镁幾g環(huán)境
環(huán)境配置好后我們就可以進(jìn)行代碼的編寫(xiě)了。創(chuàng)建一個(gè)HellowWorld_AspectJ_Main
程序運(yùn)行的入口
public class HellowWorld_AspectJ_Main {
public static void main(String[] args) {
System.out.println("mian method is print");
}
}
再創(chuàng)建一個(gè)定位切面和執(zhí)行邏輯的類(lèi)
HellowWorld_Pointcut
public aspect HellowWorld_Pointcut {
pointcut helloWorld(): execution(* HellowWorld_AspectJ_Main.main(..));
before(): helloWorld(){
System.out.println("enter the HellowWorld_AspectJ_Main aop");
}
}
直接運(yùn)行就可以了
最后要指出的重點(diǎn)是編譯后的兩個(gè)類(lèi)
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(); //編譯后自動(dòng)添加的代碼
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
準(zhǔn)備三個(gè)類(lèi)琉雳,ProxyInstanceJDK定義方法的接口样眠、ProxyInstanceJDKImpl實(shí)現(xiàn)類(lèi)、ProxyHandlerJDK回調(diào)類(lèi)翠肘,這三個(gè)類(lèi)我都放在了main所在的類(lèi)下面檐束,所以沒(méi)有用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方法調(diào)用
public static void main(String[] args) throws Exception {
proxyJdk();
// printJDKProxyClassByte();
// proxyCglib();
}
具體生成代理類(lèi)的過(guò)程可以查看java.lang.reflect.Proxy類(lèi)的源碼束倍。我這里把最后動(dòng)態(tài)生成代理類(lèi)的字節(jié)碼輸出在了一個(gè)**.class文件中被丧,反編譯后就可以看到生成的代理類(lèi)了。
/**
* 可以將生成好的代理類(lèi)字節(jié)碼打印出來(lái)
* @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();
}
反編譯后代理類(lèi)的具體實(shí)現(xiàn)
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
只需要準(zhǔn)備一個(gè)具體的方法實(shí)現(xiàn)類(lèi)ProxyInstanceCglib
class ProxyInstanceCglib {
public void print() {
System.out.println("target cglib class");
}
}
生成代理類(lèi)
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沒(méi)有獲取代理類(lèi)的字節(jié)碼的類(lèi)方法(P髅谩甥桂!確實(shí)沒(méi)找到!S士酢)格嘁,不過(guò)可以通過(guò)設(shè)置
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "**/src/main/resources/templates");
將運(yùn)行期間所有動(dòng)態(tài)創(chuàng)建的類(lèi)打印出來(lái)廊移。其中ProxyInstanceCglib$$EnhancerByCGLIB$$366568d是cglib
是我們生成的代理類(lèi)糕簿,源碼有點(diǎn)長(zhǎng)就不貼出來(lái)了,我把它上傳到了github上狡孔,大家有興趣的可以看一下懂诗。
三、結(jié)語(yǔ)
把這幾種常用的AOP技術(shù)操作運(yùn)行起來(lái)苗膝,會(huì)對(duì)我們理解spring AOP的時(shí)候起到一個(gè)支撐的作用殃恒。雖然并沒(méi)有深入的研究,但或多或少的解決了“從哪里來(lái)”的問(wèn)題!
aspect github項(xiàng)目地址:https://github.com/lotusfan/aspectj
proxy github項(xiàng)目地址:https://github.com/lotusfan/spring-programmatic
使用到的類(lèi):com.programmatic.springprogrammatic.ProxyTest
Cglib生成的代理類(lèi):resources/templates/com/programmatic.springprogrammatic/ProxyInstanceCglib$$EnhancerByCGLIB$$366568d