spring mvc攔截器,spring攔截器以及AOP切面的區別和原始碼

尋煙的衣袖發表於2020-09-28

SpringMVC 攔截器執行時機

對於springmvc,有兩種方式配置攔截器。

一是實現HandlerInterceptor介面,如

public class MyInterceptor1 implements HandlerInterceptor {
    //該方法在action執行前執行,可以實現對資料的預處理,
    // 比如:編碼、安全控制等。如果方法返回true,則繼續執行action。
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
            Exception {
        System.out.println("MyInterceptor1 action之前執行!!!");
        return true;  //繼續執行action
    }
 
    該方法在action執行後,生成檢視前執行。在這裡,我們有機會修改檢視層資料。
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView
            modelAndView) throws Exception {
        System.out.println("MyInterceptor1 action執行之後,生成檢視之前執行!!");
    }
 
    //最後執行,通常用於釋放資源,處理異常。我們可以根據ex是否為空,來進行相關的異常處理。
    //因為我們在平時處理異常時,都是從底層向上丟擲異常,最後到了spring框架從而到了這個方法中。
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor1 最後執行!!!一般用於釋放資源!!");
    }
}

二是extends HandlerInterceptorAdapter類(是HandlerInterceptor的子類),如

public class MyInterceptor2  extends HandlerInterceptorAdapter{
 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
            Exception {
        System.out.println("MyInterceptor2.preHandle()");
        return true;  //繼續執行action
    }
}

1、執行doDispatcher做請求分發處理裡getHandler() 方法 (關鍵類 RequestMappingHandlerMapping )
在這裡插入圖片描述
mappedHandler.applyPreHandle () 執行所有攔截器的前置方法 → preHandle(),
在這裡插入圖片描述
在applyPreHandle()方法中,將與目標處理器關聯的所有攔截器進行正序遍歷,遍歷執行所有攔截器的preHandle()方法
在這裡插入圖片描述
執行完後回到dispatch()方法繼續執行,然後執行所有攔截器的後置方法
在這裡插入圖片描述
在這裡插入圖片描述
applyPostHandle()方法中,倒序遍歷執行所有攔截器的postHandle()方法
在這裡插入圖片描述
然後執行 processDispatchResult()方法 處理模型和檢視
在這裡插入圖片描述
進入processDispatchResult()方法中,執行render()方法進行渲染
在這裡插入圖片描述
渲染完後,最後在render()方法的最後,執行triggerAfterCompletion()方法
在這裡插入圖片描述
在triggerAfterCompletion()方法中,和前面的applyPostHandle()方法一樣,這裡也是倒序遍歷執行攔截器的afterCompletion()方法
在這裡插入圖片描述

spring 攔截器與Aop
spring aop功能的繼承關係圖
在這裡插入圖片描述

MethodInterceptor是AOP專案中的攔截器(注:不是動態代理攔截器),區別與HandlerInterceptor攔截目標時請求,它攔截的目標是方法。

實現MethodInterceptor攔截器大致也分為兩種:
(1)MethodInterceptor介面;
(2)利用AspectJ的註解配置;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MethodInvokeInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("before method invoke....");
        Object object = methodInvocation.proceed();
        System.out.println("after method invoke.....");
        return object;
    }
}

Aspect的註解

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AutoAspectJInterceptor {

    @Around("execution (* com.paic.phssp.springtest.controller..*.*(..))")
    public Object around(ProceedingJoinPoint point) throws Throwable{
        System.out.println("AutoAspectJInterceptor begin around......");
        Object object = point.proceed();
        System.out.println("AutoAspectJInterceptor end around......");
        return object;
    }
}

織入配置類

@Configuration
public class InterceptorConfig {
 
    public static final String traceExecution = "execution(* com.hfi.aop..*.*(..))";
 
 
    @Bean
    public DefaultPointcutAdvisor defaultPointcutAdvisor() {
        MethodInvokeInterceptor interceptor = new MethodInvokeInterceptor ();
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(traceExecution);
 
        // 配置增強類advisor
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setPointcut(pointcut);
        advisor.setAdvice(interceptor);
        return advisor;
    }
}

在這裡插入圖片描述
1.切面(Aspect):切面就是一個關注點的模組化,如事務管理、日誌管理、許可權管理等;

2.連線點(Joinpoint):程式執行時的某個特定的點,在Spring中就是一個方法的執行;

3.通知(Advice):通知就是在切面的某個連線點上執行的操作,也就是事務管理、日誌管理等;

4.切入點(Pointcut):切入點就是描述某一類選定的連線點,也就是指定某一類要織入通知的方法;

5.目標物件(Target):就是被AOP動態代理的目標物件;

在這裡插入圖片描述

spring如何判斷bean是否需要被代理
spring在建立完bean後,需要判斷是否需要給當前bean建立代理物件。org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory類中的doCreateBean方法裡

try {
            this.populateBean(beanName, mbd, instanceWrapper);
            exposedObject = this.initializeBean(beanName, exposedObject, mbd);
        } catch (Throwable var18) {
            if (var18 instanceof BeanCreationException && beanName.equals(((BeanCreationException)var18).getBeanName())) {
                throw (BeanCreationException)var18;
            }

            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", var18);
        }

initializeBean方法會呼叫applyBeanPostProcessorsAfterInitialization方法,一直往裡走該方法會呼叫所有BeanPostProcessor的postProcessAfterInitialization。

 protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged(() -> {
                this.invokeAwareMethods(beanName, bean);
                return null;
            }, this.getAccessControlContext());
        } else {
            this.invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
        }

        try {
            this.invokeInitMethods(beanName, wrappedBean, mbd);
        } catch (Throwable var6) {
            throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
        }

        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }

進入applyBeanPostProcessorsAfterInitialization方法

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
        Object result = existingBean;

        Object current;
        for(Iterator var4 = this.getBeanPostProcessors().iterator(); var4.hasNext(); result = current) {
            BeanPostProcessor processor = (BeanPostProcessor)var4.next();
            current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
        }

        return result;
    }

進入postProcessAfterInitialization方法

public Object postProcessAfterInitialization(@Nullable Object bean, 
       String beanName) throws BeansException {
    if (bean != null) {
        // 獲取當前bean的key:如果beanName不為空,則以beanName為key,如果為FactoryBean型別,
        // 前面還會新增&符號,如果beanName為空,則以當前bean對應的class為key
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // 判斷當前bean是否正在被代理,如果正在被代理則不進行封裝
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            // 對當前bean進行封裝
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

然後org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator就是一個BeanPostProcessor,該類的postProcessAfterInitialization會呼叫wrapIfNecessary方法,即該bean是否需要被包裝成代理物件。是否需要代理原則就是該bean有沒有與之相關的Advisor,有就代理。

spring如何查詢所有Advisor原始碼及如何判斷某個bean與之關聯的Advisor有哪些

wrapIfNecessary部分原始碼

	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(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);//要代理的就新增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;
	}

進入getAdvicesAndAdvisorsForBean方法,由AbstractAdvisorAutoProxyCreator子類實現

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
        List<Advisor> advisors = this.findEligibleAdvisors(beanClass, beanName);
        return advisors.isEmpty() ? DO_NOT_PROXY : advisors.toArray();
    }

進入findEligibleAdvisors方法
這裡首先就是獲取所有切面裡的Advisor,因為在bean例項化之前就解析出來了,所以這個時候一般就是直接從快取裡獲取了。然後找出適用於beanClass的Advisor,然後進行擴充套件,會增加一個ExposeInvocationInterceptor的內部例項DefaultPointcutAdvisor,為了暴露AOP呼叫,以便於一些AspectJ型別的切點匹配,最後進行排序,這裡的排序很重要,直接影響後面通知的方法呼叫順序,然後返回。

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    //1. 查詢所有的增強器
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    //2. 找到匹配當前bean的增強器
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}
 protected List<Advisor> findCandidateAdvisors() {
        Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
        return this.advisorRetrievalHelper.findAdvisorBeans();
    }

advisorRetrievalHelper指的是BeanFactoryAdvisorRetrievalHelper,實現如下:

public List<Advisor> findAdvisorBeans() {
    // Determine list of advisor bean names, if not cached already.
    String[] advisorNames = null;
    synchronized (this) {
        advisorNames = this.cachedAdvisorBeanNames;
        if (advisorNames == null) {
            // Do not initialize FactoryBeans here: We need to leave all regular beans
            // uninitialized to let the auto-proxy creator apply to them!
            //1. 查詢所有型別是Advisor介面的bean
            advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                    this.beanFactory, Advisor.class, true, false);
            this.cachedAdvisorBeanNames = advisorNames;
        }
    }
    if (advisorNames.length == 0) {
        return new LinkedList<Advisor>();
    }

    List<Advisor> advisors = new LinkedList<Advisor>();
    for (String name : advisorNames) {
        //isEligibleBean(name)返回true,最終是呼叫AbstractAdvisorAutoProxyCreator#isEligibleAdvisorBean(name)方法 
        if (isEligibleBean(name)) {
            //如果當前增強器還沒建立完,則跳過
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipping currently created advisor '" + name + "'");
                }
            }
            else {
                try {
                    //新增Advisor
                    advisors.add(this.beanFactory.getBean(name, Advisor.class));
                }
                catch (BeanCreationException ex) {
                    Throwable rootCause = ex.getMostSpecificCause();
                    if (rootCause instanceof BeanCurrentlyInCreationException) {
                        BeanCreationException bce = (BeanCreationException) rootCause;
                        if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("Skipping advisor '" + name +
                                        "' with dependency on currently created bean: " + ex.getMessage());
                            }
                            // Ignore: indicates a reference back to the bean we're trying to advise.
                            // We want to find advisors other than the currently created bean itself.
                            continue;
                        }
                    }
                    throw ex;
                }
            }
        }
    }
    return advisors;
}

實現的邏輯:

1、查詢所有型別是Advisor介面的bean
2、判斷當前Advisor是否正在建立,如果是則跳過,否則新增Advisor。

再接著進入前一個步驟的findAdvisorsThatCanApply方法:

protected List<Advisor> findAdvisorsThatCanApply(
        List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

    ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    try {
        //過濾已經得到的Advisors
        return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    }
    finally {
        ProxyCreationContext.setCurrentProxiedBeanName(null);
    }
}

AopUtils.findAdvisorsThatCanApply()實現邏輯:

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
            eligibleAdvisors.add(candidate);
        }
    }
    boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor) {
            // already processed
            continue;
        }
       
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
}

findAdvisorsThatCanApply方法的主要功能是尋找所有Advisor(增強器)中適用於當前class的Advisor,而對於真正的匹配在canApply方法中實現。

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
    if (advisor instanceof IntroductionAdvisor) {
        return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    }
    //spring aop生成的增強器是InstantiationModelAwarePointcutAdvisorImpl物件,實現了PointcutAdvisor
    else if (advisor instanceof PointcutAdvisor) {
        PointcutAdvisor pca = (PointcutAdvisor) advisor;
        return canApply(pca.getPointcut(), targetClass, hasIntroductions);
    }
    else {
        // It doesn,t have a pointcut so we assume it applies.
        return true;
    }
}

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    //匹配類
    if (!pc.getClassFilter().matches(targetClass)) {
        return false;
    }

    //匹配方法
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    if (methodMatcher == MethodMatcher.TRUE) {
        // No need to iterate the methods if we're matching any method anyway...
        return true;
    }

    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
        introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }

    Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
    classes.add(targetClass);
    for (Class<?> clazz : classes) {
        //遍歷所有方法,找到匹配的方法就返回
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            if ((introductionAwareMethodMatcher != null &&
                    introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
                    methodMatcher.matches(method, targetClass)) {
                return true;
            }
        }
    }

    return false;
}

匹配規則是,先匹配類,再遍歷方法是否有匹配的。canApply方法是一個短路操作,只要找到一個匹配就返回true,說明bean是需要被代理的。這裡不分析spring是如何執行匹配的,閱讀原始碼得有個度,我們只需要知道spring給我們規定的規則就好,我們切點匹配使用最多的是execution。接下來分析
execution規則
execution格式如下

//execution(<修飾符模式>?<返回型別模式><方法名模式>(<引數模式>)<異常模式>?)
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)

//匹配所有public修飾的方法
execution(public * *(..))
//匹配所有set開頭的方法
execution(* set*(..))
//匹配AccountService介面所有的方法
execution(* com.xyz.service.AccountService.*(..))
//匹配service包下所有的方法
execution(* com.xyz.service.*.*(..))
//匹配service包以及子包所有方法
execution(* com.xyz.service..*.*(..))

再回到我們wrapIfNecessary主步驟的createProxy方法

protected Object createProxy(
        Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }

    ProxyFactory proxyFactory = new ProxyFactory();
    //獲取當前類中相關屬性
    proxyFactory.copyFrom(this);

    //決定對於給定的bean是否應該使用targetClass而不是它的介面代理
    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }

    //攔截器和增強器結合
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors);
    //設定目標類
    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }
    //建立代理
    return proxyFactory.getProxy(getProxyClassLoader());
}

對於代理類的建立及處理,Spring委拖給了ProxyFacotry去處理,而在createProxy方法中主要是對ProxyFactory的初始化操作,進而對真正的建立代理做準備,這些初始化操作包括如下內容:
(1)獲取當前類的屬性

public void copyFrom(ProxyConfig other) {
    Assert.notNull(other, "Other ProxyConfig object must not be null");
    this.proxyTargetClass = other.proxyTargetClass;
    this.optimize = other.optimize;
    this.exposeProxy = other.exposeProxy;
    this.frozen = other.frozen;
    this.opaque = other.opaque;
}

(2) 決定對於給定的bean是否應該使用targetClass而不是它的介面代理,這個主要是在@EnableAspectJAutoProxy(proxyTargetClass = true)配置,如果proxyTargetClass=true,表示使用cglib代理,否則就得看代理類是否實現了介面。
(3) 結合攔截器和增強器

protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors) {
    // Handle prototypes correctly...
    //解析攔截器,這個攔截器的值我現在還不知道怎麼設定
    Advisor[] commonInterceptors = resolveInterceptorNames();

    List<Object> allInterceptors = new ArrayList<Object>();
    if (specificInterceptors != null) {
        //增強器加入到攔截器中
        allInterceptors.addAll(Arrays.asList(specificInterceptors));
        if (commonInterceptors.length > 0) {
            if (this.applyCommonInterceptorsFirst) {
                allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
            }
            else {
                allInterceptors.addAll(Arrays.asList(commonInterceptors));
            }
        }
    }
    if (logger.isDebugEnabled()) {
        int nrOfCommonInterceptors = commonInterceptors.length;
        int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
        logger.debug("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +
                " common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");
    }

    Advisor[] advisors = new Advisor[allInterceptors.size()];
    //遍歷所有的攔截器,並將攔截器轉換成Advisor
    for (int i = 0; i < allInterceptors.size(); i++) {
        advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
    }
    return advisors;
}

@Override
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
    //如果要封裝的物件本身就是Advisor型別的那麼無需再包裝
    if (adviceObject instanceof Advisor) {
        return (Advisor) adviceObject;
    }
    //只能封裝Advice和Advisor型別
    if (!(adviceObject instanceof Advice)) {
        throw new UnknownAdviceTypeException(adviceObject);
    }
    Advice advice = (Advice) adviceObject;
    if (advice instanceof MethodInterceptor) {
        // So well-known it doesn't even need an adapter.
        return new DefaultPointcutAdvisor(advice);
    }
    for (AdvisorAdapter adapter : this.adapters) {
        // Check that it is supported.
        if (adapter.supportsAdvice(advice)) {
            return new DefaultPointcutAdvisor(advice);
        }
    }
    throw new UnknownAdviceTypeException(advice);
}

(4) 設定目標類
(5) 進行獲取代理操作
建立代理
ProxyCreatorSupport#createAopProxy

protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
        activate();
    }
    //建立代理
    return getAopProxyFactory().createAopProxy(this);
}

@Override
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() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        //cglib動態代理
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        //jdk動態代理
        return new JdkDynamicAopProxy(config);
    }
}

從if中的判斷條件可以看到使用cglib需要滿足三個條件中的一個:

Optimize:用來控制通過CGLIB建立的代理是否使用激化的優化策略。除非完全瞭解AOP代理如何處理優化,否則不推薦使用者使用這個設定。目前這個屬性僅用於CGLIB代理,對於JDK動態代理無效。(我也沒找到在哪設定)。
ProxyTargetClass:這個屬性為true時,目標類本身被代理而不是目標類的介面(直接繼承目標類而不是實現目標類的介面),如果這個屬性值設定為true,CGLIB代理將被建立。
hasNoUserSuppliedProxyInterfaces:是否存在代理介面.

下面對JDK與CGLIB方式的總結:

如果目標類實現了介面,預設情況下會採用JDK的動態代理實現AOP。
如果目標物件實現了介面,可以通過ProxyTargetClass強制使用CGLIB實現AOP。
如果目標物件沒有實現了介面,必須採用CGLIB,Spring會自動在JDK動態代理和CGLIB之間切換。

關於動態代理更多詳情可以看這篇文章動態代理詳解

好的,我們要以JDK動態為例(CGLIB其實差不多一樣)說明實現JDK動態代理要做兩件事(new JdkDynamicAopProxy(config)):

1、實現InvocationHandler,重寫invoke方法實現代理邏輯
2、使用Proxy.newProxyInstance生成代理物件

那麼spring中的jdk代理是如何實現的呢?我們檢視JdkDynamicAopProxy類。
JdkDynamicAopProxy實現了InvocationHandler介面。

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable

spring的代理物件通過呼叫AopPorxy#getProxy方法獲取,而JdkDynamicAopProxy實現了AopPorxy,看下getProxy方法的實現邏輯,呼叫了Proxy.newProxyInstance建立代理物件。

@Override
public Object getProxy(ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
    }
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    //生成代理物件
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

再看invoke方法。

@Override
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]);
        }
        //hashCode方法處理
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // The target does not implement the hashCode() method itself.
            return hashCode();
        }
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // There is only getDecoratedClass() declared -> dispatch to proxy config.
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        else 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.
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        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 != Object.class && 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 can't 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類進行了鏈的封裝,cglib的話他的intercept方法和jdk動態代理的invoke方法實現邏輯是一模一樣,也是先封裝呼叫鏈,然後逐一呼叫呼叫鏈,不過jdk動態代理使用的是ReflectiveMethodInvocation物件,而cglib使用的是CglibMethodInvocation。不過ReflectiveMethodInvocation和CglibMethodInvocation中的proceed方法實現邏輯是一樣的。

spring aop之呼叫鏈

接前面spring aop之建立代理,我們繼續分析spring是如何把Advisor(增強器)封裝成呼叫鏈,並且是如何逐一呼叫的,這裡我們以JDk動態代理為例,這塊邏輯jdk和cglib是一樣的。程式碼在JdkDynamicAopProxy#invoke方法中。

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
    MethodCacheKey cacheKey = 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);
    }
    return cached;
}

getInterceptorsAndDynamicInterceptionAdvice方法只分析其中的一部分,就是把Advisor轉換成MethodInterceptor。

MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
    Advice advice = advisor.getAdvice();
    //通知本身就是MethodInterceptor物件,就不需要再轉換
    if (advice instanceof MethodInterceptor) {
        interceptors.add((MethodInterceptor) advice);
    }
    //如果通知不是MethodInterceptor物件物件,使用介面卡轉換
    for (AdvisorAdapter adapter : this.adapters) {
        if (adapter.supportsAdvice(advice)) {
            interceptors.add(adapter.getInterceptor(advisor));
        }
    }
    if (interceptors.isEmpty()) {
        throw new UnknownAdviceTypeException(advisor.getAdvice());
    }
    return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}

adapters物件有三個值

private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3);
public DefaultAdvisorAdapterRegistry() {
    //對前置通知的代理
    registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
    //返回通知代理
    registerAdvisorAdapter(new AfterReturningAdviceAdapter());
    //異常通知代理
    registerAdvisorAdapter(new ThrowsAdviceAdapter());
}

這裡以MethodBeforeAdviceAdapter作為例子分析

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof MethodBeforeAdvice);
    }

    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }

}

MethodBeforeAdviceAdapter把前置通知轉換成MethodBeforeAdviceInterceptor物件。
所以我們再來看下幾種通知的類圖:
在這裡插入圖片描述
從類圖可以看出
AspectJAfterAdvice、AspectJAfterThrowingAdvice、AspectJAroundAdvice是實現了MethodInterceptor介面,AspectJMethodBeforeAdvice和AspectJAfterReturningAdvice是需要使用介面卡適配,才能生成MethodInterceptor物件。
把攔截器生成MethodInterceptor攔截器鏈後,接下來又如何去呼叫呢?這個邏輯在ReflectiveMethodInvocation的proceed()上。

@Override
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 {
        // It's an interceptor, so we just invoke it: The pointcut will have
        // been evaluated statically before this object was constructed.
        //呼叫攔截器中方法
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

在proceed方法中,或許程式碼邏輯並沒有我們想象的那麼複雜,ReflectiveMethodInvocation中的主要職責是維護了連結呼叫的計數器,記錄著當前呼叫連結的位置,以便連結可以有序地進行下去,在這個方法中並沒有維護呼叫鏈的順序,而是將此工作委拖給各個增強器,在各個增強器的內部進行邏輯實現。
我們再來分析MethodBeforeAdviceInterceptor和AspectJAfterAdvice執行邏輯。

public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
    private MethodBeforeAdvice advice;
    public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
        Assert.notNull(advice, "Advice must not be null");
        this.advice = advice;
    }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
        return mi.proceed();
    }
}

@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
    invokeAdviceMethod(getJoinPointMatch(), null, null);
}

protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
    Object[] actualArgs = args;
    if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
        actualArgs = null;
    }
    try {
        ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
        // TODO AopUtils.invokeJoinpointUsingReflection
        //呼叫通知的方法
        return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
    }
    catch (IllegalArgumentException ex) {
        throw new AopInvocationException("Mismatch on arguments to advice method [" +
                this.aspectJAdviceMethod + "]; pointcut expression [" +
                this.pointcut.getPointcutExpression() + "]", ex);
    }
    catch (InvocationTargetException ex) {
        throw ex.getTargetException();
    }
}

MethodBeforeAdviceInterceptor的invoke方法是呼叫了前置通知的before方法,前置通知before通過反射呼叫通知方法,然後再呼叫proceed()執行呼叫鏈。

最後補充Advisor類的類架構

Advisor類架構

在這裡插入圖片描述

aopalliance定義了AOP的通知Advice和連線點Joinpoint介面,並且還有繼承上述介面的MethodInterceptor和MethodInvocation。這兩個類相信大家都很熟悉。

然後我們來看一下Spring AOP中Advisor相關的類圖。Advisor是Spring AOP獨有的概念,比較重要的類有AbstractPointcutAdvisor和InstantiationModelAwarePointcutAdvisor。相關的講解都在圖中表明瞭,如果這張圖中的概念和類同學們都熟識,那麼對AOP的瞭解就已經很深入了。
在這裡插入圖片描述
更加詳細的類架構問題可以參考這篇文章Spring AOP(三) Advisor類架構

相關文章