對於代理類的創建及處理,spring委託給了ProxyFactory去處理,而在此函數中主要是對ProxyFactory的初始化操作,進而對真正的創建代理做準備,這些初始化操作包括如下內容:
其中,封裝Advisor並進入到ProxyFactory中以及創建代理是兩個相對繁瑣的過程,可以通過ProxyFactory提供的addAdvisor方法直接將增強器置入代理創建工廠中,但是將攔截器封裝為增強器還是需要一定的邏輯的
由於spring中涉及過多的攔截器、增強器、增強方法等方式來對邏輯進行增強,所以非常有必要統一封裝成Advisor來進行代理的創建,完成了增強的封裝過程,那麼解析最重要的一步就是代理的創建與獲取了
有創建代理的方法,用到了抽象工廠模式,有註冊、刪除監聽,這個是觀察者模式,最後創建代理用到了jdk動態代理和cglib動態代理模式。
/**
* Subclasses should call this to get a new AOP proxy. They should <b>not</b>
* create an AOP proxy with {@code this} as an argument.
*/
protected final synchronized AopProxy createAopProxy () {
if (! this . active ) {
activate ();
}
return getAopProxyFactory (). createAopProxy ( this );
}
public AopProxy createAopProxy ( AdvisedSupport config ) throws AopConfigException {
if ( config . isOptimize () || config . isProxyTargetClass () || hasNoUserSuppliedProxyInterfaces ( config )) {
Class targetClass = config . getTargetClass ();
if ( targetClass == null ) {
throw new AopConfigException ( "TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation." );
}
if ( targetClass . isInterface ()) {
return new JdkDynamicAopProxy ( config );
}
return CglibProxyFactory . createCglibProxy ( config );
}
else {
return new JdkDynamicAopProxy ( config );
}
}
關於代理的創建spring是如何選擇的呢
optimize:用來控制通過CGLIB創建的代理是否使用激進的優化策略。除非完全瞭解AOP代理如何處理優化,否則不推薦用戶使用這個設置。目前這個屬性僅用於CGLIB代理,對於JDK動態代理無效
proxyTargetClass:這個屬性為true時,目標類本身被代理而不是目標類的介面。如果這個屬性值被設為true,CGLIB代理將被創建,設置方式<aop:aspectj-autoproxy proxy-target-class="true">
hasNoUserSuppliedProxyInterfaces:是否存在代理介面
下面是對JDK與CGLIB方式的總結
如果目標對象實現了介面,默認情況下會採用JDK的動態代理實現AOP
如果目標對象實現了介面,可以強制使用CGLIB實現AOP
如果目標對象沒有實現了介面,必須採用CGLIB庫,Spring會自動在JDK動態代理和CGLIB直接的轉換
如何強制使用CGLIB實現AOP
添加CGLIB庫,spring_HOME/CGLIB/*.jar
在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
JDK動態代理和CGLIB位元組碼生成的區別
JDK動態代理只能對實現了介面的類生成代理,而不能針對類
CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法,因為是繼承,所以該類或方法最好不要聲明成final
獲取代理
先分析AOP的JDK動態代理實現過程,我們知道JDK動態代理需要實現InvocationHandle介面,並且核心邏輯都在invoke裡面,我們跟進代碼
/**
* Implementation of {@code InvocationHandler.invoke}.
* <p>Callers will see exactly the exception thrown by the target,
* unless a hook method throws an exception.
*/
public Object invoke ( Object proxy , Method method , Object [] args ) throws Throwable {
MethodInvocation invocation ;
Object oldProxy = null ;
boolean setProxyContext = false ;
TargetSource targetSource = this . advised . targetSource ;
Class <?> targetClass = null ;
Object target = null ;
try {
//equals方法的處理
if (! this . equalsDefined && AopUtils . isEqualsMethod ( method )) {
// The target does not implement the equals(Object) method itself.
return equals ( args [ 0 ]);
}
//hash方法的處理
if (! this . hashCodeDefined && AopUtils . isHashCodeMethod ( method )) {
// The target does not implement the hashCode() method itself.
return hashCode ();
}
/**
* class類的isAssingableFrom(Class cls)方法:如果調用這個方法的class
* 或介面與參數cls標識的類或介面相同,或者是參數cls表示的類或介面的父類,則返回true
* 形象地:自身類。class.isAssignableFrom(自身類或子類.class)返回true
* 例:System.out.println(ArrayList.class.isAssignableFrom(Object.class)); false
* System.out.println(Object.class.isAssignableFrom(ArrayList.class)); true
*/
if (! this . advised . opaque && method . getDeclaringClass (). isInterface () &&
method . getDeclaringClass (). isAssignableFrom ( Advised . class )) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils . invokeJoinpointUsingReflection ( this . advised , method , args );
}
Object retVal ;
//有時候目標對象內部的自我調用將無法實施切面中的增強則需要通過此屬性暴露代理
if ( this . advised . exposeProxy ) {
// Make invocation available if necessary.
oldProxy = AopContext . setCurrentProxy ( proxy );
setProxyContext = true ;
}
// May be null. Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource . getTarget ();
if ( target != null ) {
targetClass = target . getClass ();
}
// Get the interception chain for this method.
//獲取當前方法的攔截器鏈
List < Object > chain = this . advised . getInterceptorsAndDynamicInterceptionAdvice ( method , targetClass );
// Check whether we have any advice. If we dont, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if ( chain . isEmpty ()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
//如果沒有發現任何攔截器那麼直接調用切點方法
retVal = AopUtils . invokeJoinpointUsingReflection ( target , method , args );
}
else {
// We need to create a method invocation...
//將攔截器封裝在ReflectiveMethodInvocation,以便於使用其proceed進行鏈接表用攔截器
invocation = new ReflectiveMethodInvocation ( proxy , target , method , args , targetClass , chain );
// Proceed to the joinpoint through the interceptor chain.
//執行攔截器鏈
retVal = invocation . proceed ();
}
// Massage return value if necessary.
Class <?> returnType = method . getReturnType ();
//返回結果
if ( retVal != null && retVal == target && returnType . isInstance ( proxy ) &&
! RawTargetAccess . class . isAssignableFrom ( method . getDeclaringClass ())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we cant help if the target sets
// a reference to itself in another returned object.
retVal = proxy ;
}
else if ( retVal == null && returnType != Void . TYPE && returnType . isPrimitive ()) {
throw new AopInvocationException (
"Null return value from advice does not match primitive return type for: " + method );
}
return retVal ;
}
finally {
if ( target != null && ! targetSource . isStatic ()) {
// Must have come from TargetSource.
targetSource . releaseTarget ( target );
}
if ( setProxyContext ) {
// Restore old proxy.
AopContext . setCurrentProxy ( oldProxy );
}
}
}
上面最主要的工作就是創建了攔截器鏈,並使用ReflectiveMethodInvocation類進行了鏈的封裝,而在proceed方法中實現了攔截器的逐一調用,proceed增強如何調用的呢
public Object proceed () throws Throwable {
// We start with an index of -1 and increment early.
//執行完所有增強後執行切點方法
if ( this . currentInterceptorIndex == this . interceptorsAndDynamicMethodMatchers . size () - 1 ) {
return invokeJoinpoint ();
}
//獲取下一個要執行的攔截器
Object interceptorOrInterceptionAdvice =
this . interceptorsAndDynamicMethodMatchers . get (++ this . currentInterceptorIndex );
if ( interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher ) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
//動態匹配
InterceptorAndDynamicMethodMatcher dm =
( InterceptorAndDynamicMethodMatcher ) interceptorOrInterceptionAdvice ;
if ( dm . methodMatcher . matches ( this . method , this . targetClass , this . arguments )) {
return dm . interceptor . invoke ( this );
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
//不匹配則不執行攔截器
return proceed ();
}
}
else {
// Its an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
//普通攔截器,直接調用攔截器,如ExposeInvocationInterceptor
//DelegatePerTargetObjectIntroductionInterceptor
//MethodBeforeAdviceInterceptor、AspectJAroundAdvice、AspectJAfterAdvice
//將this作為參數傳遞以保證當前實例中調用鏈的執行
return (( MethodInterceptor ) interceptorOrInterceptionAdvice ). invoke ( this );
}
}
ReflectiveMethodInvocation中的主要職責是維護了鏈接調用的計數器,記錄著當前調用鏈接的位置,以便鏈可以有序地進行下去,那麼在這個方法中並沒有我們之前設想的維護各種增強的順序,而是將此工作委託給了各個增強器,使各個增強器在內部進行邏輯的實現
調用鏈的順序,ExposeInvocationInterceptor、AfterReturningAdviceInterceptor、AspectJAfterAdvice、AspectJAroundAdvice、MethodBeforeAdviceInterceptor
2個after類型放在最前面為什麼他們最後列印出來呢,看看方法
他是先繼續往下調用下個調用鏈,真正代理的方法是在finally裡面也就是最後等前面方法都調用完了再執行的,所以才能保證他們最後執行
CGLIB動態代理
CglibAopOroxy類的getProxy方法中實現了Enhancer的創建及介面封裝
public Object getProxy ( ClassLoader classLoader ) {
if ( logger . isDebugEnabled ()) {
logger . debug ( "Creating CGLIB proxy: target source is " + this . advised . getTargetSource ());
}
try {
Class <?> rootClass = this . advised . getTargetClass ();
Assert . state ( rootClass != null , "Target class must be available for creating a CGLIB proxy" );
Class <?> proxySuperClass = rootClass ;
if ( ClassUtils . isCglibProxyClass ( rootClass )) {
proxySuperClass = rootClass . getSuperclass ();
Class <?>[] additionalInterfaces = rootClass . getInterfaces ();
for ( Class <?> additionalInterface : additionalInterfaces ) {
this . advised . addInterface ( additionalInterface );
}
}
// Validate the class, writing log messages as necessary.
//驗證class
validateClassIfNecessary ( proxySuperClass );
// Configure CGLIB Enhancer...
//創建及配置Enhancer
Enhancer enhancer = createEnhancer ();
if ( classLoader != null ) {
enhancer . setClassLoader ( classLoader );
if ( classLoader instanceof SmartClassLoader &&
(( SmartClassLoader ) classLoader ). isClassReloadable ( proxySuperClass )) {
enhancer . setUseCache ( false );
}
}
enhancer . setSuperclass ( proxySuperClass );
enhancer . setInterfaces ( AopProxyUtils . completeProxiedInterfaces ( this . advised ));
enhancer . setNamingPolicy ( SpringNamingPolicy . INSTANCE );
enhancer . setStrategy ( new MemorySafeUndeclaredThrowableStrategy ( UndeclaredThrowableException . class ));
enhancer . setInterceptDuringConstruction ( false );
//設置攔截器
Callback [] callbacks = getCallbacks ( rootClass );
Class <?>[] types = new Class <?>[ callbacks . length ];
for ( int x = 0 ; x < types . length ; x ++) {
types [ x ] = callbacks [ x ]. getClass ();
}
enhancer . setCallbackFilter ( new ProxyCallbackFilter (
this . advised . getConfigurationOnlyCopy (), this . fixedInterceptorMap , this . fixedInterceptorOffset ));
enhancer . setCallbackTypes ( types );
enhancer . setCallbacks ( callbacks );
// Generate the proxy class and create a proxy instance.
//生成代理類以及創建代理
Object proxy ;
if ( this . constructorArgs != null ) {
proxy = enhancer . create ( this . constructorArgTypes , this . constructorArgs );
}
else {
proxy = enhancer . create ();
}
return proxy ;
}
catch ( CodeGenerationException ex ) {
throw new AopConfigException ( "Could not generate CGLIB subclass of class [" +
this . advised . getTargetClass () + "]: " +
"Common causes of this problem include using a final class or a non-visible class" ,
ex );
}
catch ( IllegalArgumentException ex ) {
throw new AopConfigException ( "Could not generate CGLIB subclass of class [" +
this . advised . getTargetClass () + "]: " +
"Common causes of this problem include using a final class or a non-visible class" ,
ex );
}
catch ( Exception ex ) {
// TargetSource.getTarget() failed
throw new AopConfigException ( "Unexpected AOP exception" , ex );
}
}
上面闡述了一個創建spring中的Enhancer的過程,最重要的是通過getCallbacks方法設置攔截器鏈
private Callback [] getCallbacks ( Class <?> rootClass ) throws Exception {
// Parameters used for optimisation choices...
boolean exposeProxy = this . advised . isExposeProxy ();
boolean isFrozen = this . advised . isFrozen ();
boolean isStatic = this . advised . getTargetSource (). isStatic ();
// Choose an "aop" interceptor (used for AOP calls).
//將攔截器封裝在DynamicAdvisedInterceptor中
Callback aopInterceptor = new DynamicAdvisedInterceptor ( this . advised );
// Choose a "straight to target" interceptor. (used for calls that are
// unadvised but can return this). May be required to expose the proxy.
Callback targetInterceptor ;
if ( exposeProxy ) {
targetInterceptor = isStatic ?
new StaticUnadvisedExposedInterceptor ( this . advised . getTargetSource (). getTarget ()) :
new DynamicUnadvisedExposedInterceptor ( this . advised . getTargetSource ());
}
else {
targetInterceptor = isStatic ?
new StaticUnadvisedInterceptor ( this . advised . getTargetSource (). getTarget ()) :
new DynamicUnadvisedInterceptor ( this . advised . getTargetSource ());
}
// Choose a "direct to target" dispatcher (used for
// unadvised calls to static targets that cannot return this).
Callback targetDispatcher = isStatic ?
new StaticDispatcher ( this . advised . getTargetSource (). getTarget ()) : new SerializableNoOp ();
Callback [] mainCallbacks = new Callback [] {
//將攔截器鏈假如Callback中
aopInterceptor , // for normal advice
targetInterceptor , // invoke target without considering advice, if optimized
new SerializableNoOp (), // no override for methods mapped to this
targetDispatcher , this . advisedDispatcher ,
new EqualsInterceptor ( this . advised ),
new HashCodeInterceptor ( this . advised )
};
Callback [] callbacks ;
// If the target is a static one and the advice chain is frozen,
// then we can make some optimisations by sending the AOP calls
// direct to the target using the fixed chain for that method.
if ( isStatic && isFrozen ) {
Method [] methods = rootClass . getMethods ();
Callback [] fixedCallbacks = new Callback [ methods . length ];
this . fixedInterceptorMap = new HashMap < String , Integer >( methods . length );
// TODO: small memory optimisation here (can skip creation for methods with no advice)
for ( int x = 0 ; x < methods . length ; x ++) {
List < Object > chain = this . advised . getInterceptorsAndDynamicInterceptionAdvice ( methods [ x ], rootClass );
fixedCallbacks [ x ] = new FixedChainStaticTargetInterceptor (
chain , this . advised . getTargetSource (). getTarget (), this . advised . getTargetClass ());
this . fixedInterceptorMap . put ( methods [ x ]. toString (), x );
}
// Now copy both the callbacks from mainCallbacks
// and fixedCallbacks into the callbacks array.
callbacks = new Callback [ mainCallbacks . length + fixedCallbacks . length ];
System . arraycopy ( mainCallbacks , 0 , callbacks , 0 , mainCallbacks . length );
System . arraycopy ( fixedCallbacks , 0 , callbacks , mainCallbacks . length , fixedCallbacks . length );
this . fixedInterceptorOffset = mainCallbacks . length ;
}
else {
callbacks = mainCallbacks ;
}
return callbacks ;
}
這裡面spring考慮了很多情況,對我們來說,只要理解常用的,如advised屬性封裝在DynamicAdvisedInterceptor並加入在callbacks中。根據cglib動態代理的特性,其核心的邏輯在intercept中
public Object intercept ( Object proxy , Method method , Object [] args , MethodProxy methodProxy ) throws Throwable {
Object oldProxy = null ;
boolean setProxyContext = false ;
Class <?> targetClass = null ;
Object target = null ;
try {
if ( this . advised . exposeProxy ) {
// Make invocation available if necessary.
oldProxy = AopContext . setCurrentProxy ( proxy );
setProxyContext = true ;
}
// May be null. Get as late as possible to minimize the time we
// "own" the target, in case it comes from a pool...
target = getTarget ();
if ( target != null ) {
targetClass = target . getClass ();
}
//獲取攔截器鏈
List < Object > chain = this . advised . getInterceptorsAndDynamicInterceptionAdvice ( method , targetClass );
Object retVal ;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if ( chain . isEmpty () && Modifier . isPublic ( method . getModifiers ())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
//如果攔截器鏈為空則直接激活原方法
retVal = methodProxy . invoke ( target , args );
}
else {
// We need to create a method invocation...
//進入鏈
retVal = new CglibMethodInvocation ( proxy , target , method , args , targetClass , chain , methodProxy ). proceed ();
}
retVal = processReturnType ( proxy , target , method , retVal );
return retVal ;
}
finally {
if ( target != null ) {
releaseTarget ( target );
}
if ( setProxyContext ) {
// Restore old proxy.
AopContext . setCurrentProxy ( oldProxy );
}
}
}
上述實現與JDK方式大同小異,都是先構建鏈,然後封裝此鏈進行串聯調用,稍有些區別就是JDK中直接構造ReflectiveMethodInvocation,而在cglib中使用CglibMethodInvocation,這個繼承自ReflectiveMethodInvocation,但proceed方法並沒有重寫
靜態AOP
載入時織入(Load-Time Weaving,LTW)指的是在虛擬機載入位元組碼文件時動態織入AspectJ切面,spring框架的值添加為AspectJ LTW在動態織入過程中提供了更細粒度的控制。
如果想從動態代理的方式改成靜態代理的方式需要做如下改動
spring全局配置文件的修改,假如LWT開關
2. 加入aop.xml。在class目錄下的META-INF文件夾下簡歷aop.xml內容如下
<!DOCTYPE aspectj PUBLIC "-//AspectJ/DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<include within="test.*"/>
</weaver>
<aspects>
<aspect name="test.AspectJTest"/>
</aspects>
</aspectj>
主要是告訴AspectJ需要對哪個包進行織入,並使用哪些增強器
3. 加入啟動參數。如果是在Eclipse中啟動的話需要加上啟動參數
測試,於之前的結果沒有差別
創建AOP靜態代理
AOP的靜態代理主要是在虛擬機啟動時通過改變模板對象位元組碼的方式來完成對目標對象的增強,它與動態代理相比具有更高的效率,因為在動態代理調用的過程中,還需要一個動態創建代理類並代理目標對象的步驟,而靜態代理則是在啟動時便完成了位元組碼增強,當系統再次調用模板類時與調用正常的類並無差別,所以在效率上會相對高些
Instrumentation使用
自定義標籤
在spring中如果需要使用AspectJ的功能,首先要做的是第一步就是在配置文件中加入<context:load-time-weaver/>,通過搜索找到了ContextNamespaceHandler類
public void init() {
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}
繼續跟進LoadTimeWeaverBeanDefinitionParser,作為BeanDefinitionParser介面的實現類,他的核心邏輯是從parse函數開始的,而經過父類的封裝,核心實現在doParse函數中
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
if (isAspectJWeavingEnabled(element.getAttribute(ASPECTJ_WEAVING_ATTRIBUTE), parserContext)) {
RootBeanDefinition weavingEnablerDef = new RootBeanDefinition();
weavingEnablerDef.setBeanClassName(ASPECTJ_WEAVING_ENABLER_CLASS_NAME);
parserContext.getReaderContext().registerWithGeneratedName(weavingEnablerDef);
if (isBeanConfigurerAspectEnabled(parserContext.getReaderContext().getBeanClassLoader())) {
new SpringConfiguredBeanDefinitionParser().parse(element, parserContext);
}
}
}
流程如下
是否開啟AspectJ,<context:load-time-weaver>有個屬性aspectj-weaving,這個屬性有3個備選值,on、off和autodetect,默認為autodetect,也就是說,spring會檢測是否可以使用aspectJ功能,而堅持依據便是META-INF/aop.xml是否存在
2. 將org.Springframework.context.weaving.AspectJWeavingEnable封裝在BeanDefinition中註冊,當通過AspectJ功能驗證後便可以進行AspectJWeavingEnabler的註冊了,註冊方式很簡單,無非將類路徑註冊在新初始化的RootBeanDefinition中,在RootBeanDefinition的獲取時會轉換城對應的class
織入
當完成了所有的AspectJ的準備工作後便可以進行織入分析了,首先還是從
推薦閱讀: