AOP
AOP聯盟標准
AOP聯盟將AOP體系分為三層,從三層結構可以看出,AOP實現方式有很多種,包括反射、元數據處理、程序處理、攔截器處理等,通過本節學習,你就會看到Spring AOP的實現使用的是Java語言本身的特性,即Java Proxy代理類、攔截器技術實現。
AOP簡介
概念
切面(Aspect) :官方的抽象定義為“一個關注點的模塊化,這個關注點可能會橫切多個對象”。
連接點(Joinpoint) :程序執行過程中的某一行為。
通知(Advice) :“切面”對於某個“連接點”所產生的動作。
切入點(Pointcut) :匹配連接點的斷言,在AOP中通知和一個切入點表達式關聯。
目標對象(Target Object) :被一個或者多個切面所通知的對象。
AOP代理(AOP Proxy) 在Spring AOP中有兩種代理方式,JDK動態代理和CGLIB代理。
通知(Advice)類型
前置通知(Before advice) :在某連接點(JoinPoint)之前執行的通知,但這個通知不能阻止連接點前的執行。ApplicationContext中在<aop:aspect>里面使用<aop:before>元素進行聲明。
后通知(After advice) :當某連接點退出的時候執行的通知(不論是正常返回還是異常退出)。ApplicationContext中在<aop:aspect>里面使用<aop:after>元素進行聲明。
返回后通知(After return advice) :在某連接點正常完成后執行的通知,不包括拋出異常的情況。ApplicationContext中在<aop:aspect>里面使用<after-returning>元素進行聲明。
環繞通知(Around advice) :包圍一個連接點的通知,類似Web中Servlet規范中的Filter的doFilter方法。可以在方法的調用前后完成自定義的行為,也可以選擇不執行。ApplicationContext中在<aop:aspect>里面使用<aop:around>元素進行聲明。
拋出異常后通知(After throwing advice) : 在方法拋出異常退出時執行的通知。 ApplicationContext中在<aop:aspect>里面使用<aop:after-throwing>元素進行聲明。
切入點表達式 :如execution(* com.spring.service.*.*(..))
特點
1、降低模塊之間的耦合度
2、使系統容易擴展
3、更好的代碼復用。
時序圖
流程說明
1)AOP標簽的定義解析劉徹骨肯定是從NamespaceHandlerSupport的實現類開始解析的,這個實現類就是AopNamespaceHandler。至於為什么會是從NamespaceHandlerSupport的實現類開始解析的,這個的話我想讀者可以去在回去看看Spring自定義標簽的解析流程,里面說的比較詳細。
2)要啟用AOP,我們一般會在Spring里面配置<aop:aspectj-autoproxy/> ,所以在配置文件中在遇到aspectj-autoproxy標簽的時候我們會采用AspectJAutoProxyBeanDefinitionParser解析器
3)進入AspectJAutoProxyBeanDefinitionParser解析器后,調用AspectJAutoProxyBeanDefinitionParser已覆蓋BeanDefinitionParser的parser方法,然后parser方法把請求轉交給了AopNamespaceUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary去處理
4)進入AopNamespaceUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法后,先調用AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,里面在轉發調用給registerOrEscalateApcAsRequired,注冊或者升級AnnotationAwareAspectJAutoProxyCreator類。對於AOP的實現,基本是靠AnnotationAwareAspectJAutoProxyCreator去完成的,它可以根據@point注解定義的切點來代理相匹配的bean。
5)AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法處理完成之后,接下來會調用useClassProxyingIfNecessary() 處理proxy-target-class以及expose-proxy屬性。如果將proxy-target-class設置為true的話,那么會強制使用CGLIB代理,否則使用jdk動態代理,expose-proxy屬性是為了解決有時候目標對象內部的自我調用無法實現切面增強。
6)最后的調用registerComponentIfNecessary 方法,注冊組建並且通知便於監聽器做進一步處理。
創建AOP代理
上面說到AOP的核心邏輯是在AnnotationAwareAspectJAutoProxyCreator類里面實現,那么我們先來看看這個類的層次關系
這個類實現了BeanPostProcessor接口,那就意味着這個類在spring加載實例化前會調用postProcessAfterInitialization方法,對於AOP的邏輯也是由此開始的。
時序圖
流程說明
1)spring 容器啟動,每個bean的實例化之前都會先經過AbstractAutoProxyCreator類的postProcessAfterInitialization()這個方法,然后接下來是調用wrapIfNecessary方法。
-
/**
-
* Create a proxy with the configured interceptors if the bean is
-
* identified as one to proxy by the subclass.
-
* @see #getAdvicesAndAdvisorsForBean
-
*/
-
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
-
if (bean != null) {
-
Object cacheKey = getCacheKey(bean.getClass(), beanName);
-
if (!this.earlyProxyReferences.containsKey(cacheKey)) {
-
return wrapIfNecessary(bean, beanName, cacheKey);
-
}
-
}
-
return bean;
-
}
2)進入wrapIfNecessary方法后,我們直接看重點實現邏輯的方法getAdvicesAndAdvisorsForBean,這個方法會提取當前bean 的所有增強方法,然后獲取到適合的當前bean 的增強方法,然后對增強方法進行排序,最后返回
-
/**
-
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
-
* @param bean the raw bean instance
-
* @param beanName the name of the bean
-
* @param cacheKey the cache key for metadata access
-
* @return a proxy wrapping the bean, or the raw bean instance as-is
-
*/
-
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
-
if (beanName != null && this.targetSourcedBeans.containsKey(beanName)) {
-
return bean;
-
}
-
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
-
return bean;
-
}
-
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
-
this.advisedBeans.put(cacheKey, Boolean.FALSE);
-
return bean;
-
}
-
-
// Create proxy if we have advice.
-
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
-
if (specificInterceptors != DO_NOT_PROXY) {
-
this.advisedBeans.put(cacheKey, Boolean.TRUE);
-
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
-
this.proxyTypes.put(cacheKey, proxy.getClass());
-
return proxy;
-
}
-
-
this.advisedBeans.put(cacheKey, Boolean.FALSE);
-
return bean;
-
}
3)獲取到當前bean的增強方法后,便調用createProxy方法,創建代理。先創建代理工廠proxyFactory,然后獲取當前bean 的增強器advisors,把當前獲取到的增強器添加到代理工廠proxyFactory,然后設置當前的代理工的代理目標對象為當前bean,最后根據配置創建JDK的動態代理工廠,或者CGLIB的動態代理工廠,然后返回proxyFactory
-
/**
-
* Create an AOP proxy for the given bean.
-
* @param beanClass the class of the bean
-
* @param beanName the name of the bean
-
* @param specificInterceptors the set of interceptors that is
-
* specific to this bean (may be empty, but not null)
-
* @param targetSource the TargetSource for the proxy,
-
* already pre-configured to access the bean
-
* @return the AOP proxy for the bean
-
* @see #buildAdvisors
-
*/
-
protected Object createProxy(
-
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
-
-
ProxyFactory proxyFactory = new ProxyFactory();
-
// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
-
proxyFactory.copyFrom( this);
-
-
if (!shouldProxyTargetClass(beanClass, beanName)) {
-
// Must allow for introductions; can't just set interfaces to
-
// the target's interfaces only.
-
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
-
for (Class<?> targetInterface : targetInterfaces) {
-
proxyFactory.addInterface(targetInterface);
-
}
-
}
-
-
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
-
for (Advisor advisor : advisors) {
-
proxyFactory.addAdvisor(advisor);
-
}
-
-
proxyFactory.<strong>setTargetSource</strong>(targetSource);
-
customizeProxyFactory(proxyFactory);
-
-
proxyFactory.setFrozen( this.freezeProxy);
-
if (advisorsPreFiltered()) {
-
proxyFactory.setPreFiltered( true);
-
}
-
-
return proxyFactory.getProxy(this.proxyClassLoader);
-
}
AOP動態代理執行
-
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 JDK動態代理實現
-
public Object invoke(Object proxy, Method method, Object[] args) throwsThrowable {
-
MethodInvocation invocation = null;
-
Object oldProxy = null;
-
boolean setProxyContext = false;
-
-
TargetSource targetSource = this.advised.targetSource;
-
Class targetClass = null;
-
Object target = null;
-
-
try {
-
//eqauls()方法,具目標對象未實現此方法
-
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)){
-
return (equals(args[0])? Boolean.TRUE : Boolean.FALSE);
-
}
-
-
//hashCode()方法,具目標對象未實現此方法
-
if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)){
-
return newInteger(hashCode());
-
}
-
-
//Advised接口或者其父接口中定義的方法,直接反射調用,不應用通知
-
if (!this.advised.opaque &&method.getDeclaringClass().isInterface()
-
&&method.getDeclaringClass().isAssignableFrom(Advised.class)) {
-
// Service invocations onProxyConfig with the proxy config...
-
return AopUtils.invokeJoinpointUsingReflection(this.advised,method, args);
-
}
-
-
Object retVal = null;
-
-
if (this.advised.exposeProxy) {
-
// Make invocation available ifnecessary.
-
oldProxy = AopContext.setCurrentProxy(proxy);
-
setProxyContext = true;
-
}
-
-
//獲得目標對象的類
-
target = targetSource.getTarget();
-
if (target != null) {
-
targetClass = target.getClass();
-
}
-
-
//獲取可以應用到此方法上的Interceptor列表
-
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);
-
-
//如果沒有可以應用到此方法的通知(Interceptor),此直接反射調用 method.invoke(target, args)
-
if (chain.isEmpty()) {
-
retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);
-
} else {
-
//創建MethodInvocation
-
invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
-
retVal = invocation.proceed();
-
}
-
-
// Massage return value if necessary.
-
if (retVal != null && retVal == target &&method.getReturnType().isInstance(proxy)
-
&&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
-
// Special case: it returned"this" and the return type of the method
-
// is type-compatible. Notethat we can't help if the target sets
-
// a reference to itself inanother returned object.
-
retVal = proxy;
-
}
-
return retVal;
-
} finally {
-
if (target != null && !targetSource.isStatic()) {
-
// Must have come fromTargetSource.
-
targetSource.releaseTarget(target);
-
}
-
if (setProxyContext) {
-
// Restore old proxy.
-
AopContext.setCurrentProxy(oldProxy);
-
}
-
}
-
}
-
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {
-
MethodCacheKeycacheKey = new MethodCacheKey(method);
-
List<Object>cached = this.methodCache.get(cacheKey);
-
if(cached == null) {
-
cached= this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
-
this,method, targetClass);
-
this.methodCache.put(cacheKey,cached);
-
}
-
returncached;
-
}
4)其實實際的獲取工作其實是由AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice()這個方法來完成的,獲取到的結果會被緩存,下面來分析下這個方法的實現:
-
/**
-
* 從提供的配置實例config中獲取advisor列表,遍歷處理這些advisor.如果是IntroductionAdvisor,
-
* 則判斷此Advisor能否應用到目標類targetClass上.如果是PointcutAdvisor,則判斷
-
* 此Advisor能否應用到目標方法method上.將滿足條件的Advisor通過AdvisorAdaptor轉化成Interceptor列表返回.
-
*/
-
publicList getInterceptorsAndDynamicInterceptionAdvice(Advised config, Methodmethod, Class targetClass) {
-
// This is somewhat tricky... we have to process introductions first,
-
// but we need to preserve order in the ultimate list.
-
List interceptorList = new ArrayList(config.getAdvisors().length);
-
-
//查看是否包含IntroductionAdvisor
-
boolean hasIntroductions = hasMatchingIntroductions(config,targetClass);
-
-
//這里實際上注冊一系列AdvisorAdapter,用於將Advisor轉化成MethodInterceptor
-
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
-
-
Advisor[] advisors = config.getAdvisors();
-
for (int i = 0; i <advisors.length; i++) {
-
Advisor advisor = advisors[i];
-
if (advisor instanceof PointcutAdvisor) {
-
// Add it conditionally.
-
PointcutAdvisor pointcutAdvisor= (PointcutAdvisor) advisor;
-
if(config.isPreFiltered() ||pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
-
//TODO: 這個地方這兩個方法的位置可以互換下
-
//將Advisor轉化成Interceptor
-
MethodInterceptor[]interceptors = registry.getInterceptors(advisor);
-
-
//檢查當前advisor的pointcut是否可以匹配當前方法
-
MethodMatcher mm =pointcutAdvisor.getPointcut().getMethodMatcher();
-
-
if (MethodMatchers.matches(mm,method, targetClass, hasIntroductions)) {
-
if(mm.isRuntime()) {
-
// Creating a newobject instance in the getInterceptors() method
-
// isn't a problemas we normally cache created chains.
-
for (intj = 0; j < interceptors.length; j++) {
-
interceptorList.add( new InterceptorAndDynamicMethodMatcher(interceptors[j],mm));
-
}
-
} else {
-
interceptorList.addAll(Arrays.asList(interceptors));
-
}
-
}
-
}
-
} else if (advisor instanceof IntroductionAdvisor){
-
IntroductionAdvisor ia =(IntroductionAdvisor) advisor;
-
if(config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {
-
Interceptor[] interceptors= registry.getInterceptors(advisor);
-
interceptorList.addAll(Arrays.asList(interceptors));
-
}
-
} else {
-
Interceptor[] interceptors =registry.getInterceptors(advisor);
-
interceptorList.addAll(Arrays.asList(interceptors));
-
}
-
}
-
return interceptorList;
-
}
5)這個方法執行完成后,Advised中配置能夠應用到連接點或者目標類的Advisor全部被轉化成了MethodInterceptor.
6)接下來貨到invoke方法中的proceed方法 ,我們再看下得到的攔截器鏈是怎么起作用的,也就是proceed方法的執行過程
-
public Object proceed() throws Throwable {
-
// We start with an index of -1and increment early.
-
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()- 1) {
-
//如果Interceptor執行完了,則執行joinPoint
-
return invokeJoinpoint();
-
}
-
-
Object interceptorOrInterceptionAdvice =
-
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
-
-
//如果要動態匹配joinPoint
-
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)) {
-
//執行當前Intercetpor
-
returndm.interceptor.invoke( this);
-
}
-
else {
-
//動態匹配失敗時,略過當前Intercetpor,調用下一個Interceptor
-
return proceed();
-
}
-
}
-
else {
-
// It's an interceptor, so we just invoke it: The pointcutwill have
-
// been evaluated statically before this object was constructed.
-
//執行當前Intercetpor
-
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
-
}
-
}