spring的啓動過程02.1-aop命名空間解讀

概述:

aop是spring框架的核心思想之一,系統開發中會採用aop的方式進行業務邏輯的動態植入,如登錄授權、日誌統計等。該篇文章首先會講解aop的基礎概念,然後講解spring通過命名空間方式如何實現aop的功能。

  • AOP基礎:

AOP核心概念

1、橫切關注點
對哪些方法進行攔截,攔截後怎麼處理,這些關注點稱之爲橫切關注點
2、切面(aspect)
類是對物體特徵的抽象,切面就是對橫切關注點的抽象
3、連接點(joinpoint)
被攔截到的點,因爲Spring只支持方法類型的連接點,所以在Spring中連接點指的就是被攔截到的方法,實際上連接點還可以是字段或者構造器
4、切入點(pointcut)
對連接點進行攔截的定義
5、通知(advice)
所謂通知指的就是指攔截到連接點之後要執行的代碼,通知分爲前置、後置、異常、最終、環繞通知五類
6、目標對象
代理的目標對象
7、織入(weave)
將切面應用到目標對象並導致代理對象創建的過程
8、引入(introduction)
在不修改代碼的前提下,引入可以在運行期爲類動態地添加一些方法或字段

Spring對AOP的支持
Spring中AOP代理由Spring的IOC容器負責生成、管理,其依賴關係也由IOC容器負責管理。因此,AOP代理可以直接使用容器中的其它bean實例作爲目標,這種關係可由IOC容器的依賴注入提供。Spring創建代理的規則爲:
1、默認使用Java動態代理來創建AOP代理,這樣就可以爲任何接口實例創建代理了
2、當需要代理的類不是代理接口的時候,Spring會切換爲使用CGLIB代理,也可強制使用CGLIB
AOP編程其實是很簡單的事情,縱觀AOP編程,程序員只需要參與三個部分:
1、定義普通業務組件
2、定義切入點,一個切入點可能橫切多個業務組件
3、定義增強處理,增強處理就是在AOP框架爲普通業務組件織入的處理動作
所以進行AOP編程的關鍵就是定義切入點和定義增強處理,一旦定義了合適的切入點和增強處理,AOP框架將自動生成AOP代理,即:代理對象的方法=增強處理+被代理對象的方法。

當需要使用CGLIB代理和@AspectJ自動代理支持,請按照如下的方式設置 |aop:aspectj-autoproxy| 的 proxy-target-class 屬性:
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true" />
Spring是依靠什麼來判斷採用哪種代理策略來生成AOP代理呢?以下代碼就是Spring的判斷邏輯  
    //org.springframework.aop.framework.DefaultAopProxyFactory   
    //參數AdvisedSupport 是Spring AOP配置相關類   
    public AopProxy createAopProxy(AdvisedSupport advisedSupport)   
            throws AopConfigException {   
        //在此判斷使用JDK動態代理還是CGLIB代理   
        if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass()   
                || hasNoUserSuppliedProxyInterfaces(advisedSupport)) {   
            if (!cglibAvailable) {   
                throw new AopConfigException(   
                        "Cannot proxy target class because CGLIB2 is not available. "  
                                + "Add CGLIB to the class path or specify proxy interfaces.");   
              }   
            return CglibProxyFactory.createCglibProxy(advisedSupport);   
          } else {   
            return new JdkDynamicAopProxy(advisedSupport);   
          }   
      } 
對比 效率 共同依賴 依賴 實現方式
Cglib 較高效 aopalliance
(標準接口包)
aspectjweaver
ASM.jar(java字節碼操縱框架)
cglib.jar
加載目標類的字節碼,植入需要增強處理
然後動態生成目標類的子類
生成的類與目標類屬於繼承關係
JDK   採用JDK自帶動態代理方式,獲取目標類的
接口,動態生成接口的實現類
生產的類與目標類屬於接口的不同實現類
不能進行強制轉換操作

@AspectJ的詳細用法
一些常見的切入點的例子 
execution(public * * (. .))    任意公共方法被執行時,執行切入點函數。 
execution( * set* (. .))   任何以一個“set”開始的方法被執行時,執行切入點函數。 
execution( * com.demo.service.AccountService.* (. .))  當接口AccountService 中的任意方法被執行時,執行切入點函數。 
execution( * com.demo.service.*.* (. .))  當service 包中的任意方法被執行時,執行切入點函數。 within(com.demo.service.*)  在service 包裏的任意連接點。 within(com.demo.service. .*)  在service 包或子包的任意連接點。
this(com.demo.service.AccountService) 實現了AccountService 接口的代理對象的任意連接點。 
target(com.demo.service.AccountService) 實現了AccountService 接口的目標對象的任意連接點。 
args(Java.io.Serializable)  任何一個只接受一個參數,且在運行時傳入參數實現了 Serializable 接口的連接點
增強的方式:
@Before:方法前執行
@AfterReturning:運行方法後執行
@AfterThrowing:Throw後執行
@After:無論方法以何種方式結束,都會執行(類似於finally)
@Around:環繞執行
  • spring如何實現AOP功能
看下aop命名空間的定義:
spring.handlers文件中
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
spring.schemas文件中
http\://www.springframework.org/schema/aop/spring-aop.xsd=org/springframework/aop/config/spring-aop-4.2.xsd
看下AopNamespaceHandler類內容
public class AopNamespaceHandler extends NamespaceHandlerSupport {
	/**
	 * Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
	 * '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
	 * and '{@code scoped-proxy}' tags.
	 */
	@Override
	public void init() {
		// In 2.0 XSD as well as in 2.1 XSD.
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
		// Only in 2.0 XSD: moved to context namespace as of 2.1
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}
}
看下AspectJAutoProxyBeanDefinitionParser類:
	@Override
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
		extendBeanDefinition(element, parserContext);
		return null;
	}
	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}
	private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
		if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
			BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
			if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
				int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
				int requiredPriority = findPriorityForClass(cls);
				if (currentPriority < requiredPriority) {
					apcDefinition.setBeanClassName(cls.getName());
				}
			}
			return null;
		}
		// 創建一個名稱爲org.springframework.aop.config.internalAutoProxyCreator的bean定義
		RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
		beanDefinition.setSource(source);
		// 並設置優先級爲最高
		beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
		beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		// 註冊到上下文
		registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
		return beanDefinition;
	}
創建好bean定義後設置proxy-target-class、expose-proxy兩個參數值
proxy-target-class:強制spring內部使用CGLIB生成代理對象。
expose-proxy:當前代理是否爲可暴露狀態,值爲true則爲可訪問。

總結:
主要介紹spring如何用註解方式具有aop功能,後續會講解AnnotationAwareAspectJAutoProxyCreator類的具體功能。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章