zoukankan      html  css  js  c++  java
  • SpringAOP源码分析

    学习java,肯定绕不开Spring,学习Spring,就一定要了解IOC和AOP。虽然Spring提供了很多注解让我们可以很方便的使用这些功能,但是我们不仅要知其然更要知其所以然。下面我们从源码层面简单的解析下Spring的AOP是如何工作的。

    @EnableAspectJAutoProxy作用

    要开启Spring的AOP,需要在配置主配置类上加上注解@EnableAspectJAutoProxy,我们就从这个注解入手,看看Spring基于这个注解做了什么。

    可以看到@EnableAspectJAutoProxy这个注解主要给容器导入了一个AspectJAutoProxyRegistrar,我们接着看AspectJAutoProxyRegistrar又做了什么。

    AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar,我们知道ImportBeanDefinitionRegistrar的作用是是为容器注册bean的定义信息,那么AspectJAutoProxyRegistrar为容器注册了什么bean的定义信息,我们可以看看AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);这行代码是怎么调用的

    最后在AopConfigUtils中我们看到了这样一段代码

    现在我们知道AspectJAutoProxyRegistrar其实就是为容器注册了一个名为internalAutoProxyCreator类型为AnnotationAwareAspectJAutoProxyCreator的BeanDefinition,也就是bean的定义信息。
    至此,就是注解@EnableAspectJAutoProxy所做的事情。接下来只需要搞清AnnotationAwareAspectJAutoProxyCreator在Spring中起了什么作用就可以搞清AOP了。

    AnnotationAwareAspectJAutoProxyCreator作用

    为了测试AnnotationAwareAspectJAutoProxyCreator所起的作用,我们创建一个被代理类以及切面类,并在主配置类上加上@EnableAspectJAutoProxy注解。

    • 被代理类
    public class Demo {
        public void printHello(){
            System.out.println("hello");
        }}
    
    
    • 切面类
    @Aspect // 表明这是一个切面类
    public class DemoAspect {
    
        /**     * 抽取公共的切点,本类以及其他切面类都可以使用     */
        @Pointcut("execution(public void com.atguigu.aop.Demo.printHello(..))")
        public void cut() {
        }
    
        /**     * 前置通知     */
        @Before("cut()")
        public void beforePrint() {
            System.out.println("开始执行");
        }
    
        /**     * 后置通知     */
        @After("cut()")
        public void afterPrint() {
            System.out.println("执行结束");
        }
    
    }
    
    • 主配置类
    @EnableAspectJAutoProxy // 开启自动代理
    @Configuration
    public class MyConfigOfAOP {
        /**
         * 被代理类加入容器
         * @return
         */
        @Bean
        public Demo demo(){
            return new Demo();
        }
    
        /**
         * 切面类加入容器
         * @return
         */
        @Bean
        public DemoAspect demoAspect(){
            return new DemoAspect();
        }
    }
    
    • 测试类
    public class IOCTest_AOP {
    
    	@Test
    	public void test(){
    		//创建IOC容器
    		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfigOfAOP.class);
    		//获取容器中的对象
    		Demo demo=applicationContext.getBean(Demo.class);
    		demo.printHello();
    	}
    }
    

    我们用debug模式运行该测试用例,发现demo对象的类型为一个代理对象类型,并不是我们定义的类型。我们猜测这个代理对象应该是AnnotationAwareAspectJAutoProxyCreator创建出来的,因为@EnableAspectJAutoProxy只往容器里注册了AnnotationAwareAspectJAutoProxyCreator这个对象。

    有了上面的猜想,我们先来看下AnnotationAwareAspectJAutoProxyCreator的继承关系:

    注意到AnnotationAwareAspectJAutoProxyCreator的继承关系中有实现了一个SmartInstantiationAwareBeanPostProcessor的接口,而这个接口是继承了 BeanPostProcessor,也就是说这个类是一个bean的后置处理器,我们知道bean的后置处理器会在bean对象初始化前后进行一些相应的处理。我们猜测代理对象就是在创建Demo对象时后置处理器进行创建的。在继承关系中找到与后置处理器有关的方法。有如下两个,且都位于AbstractAutoProxyCreator中。

    	
    	@Override
    	public Object postProcessBeforeInitialization(Object bean, String beanName) {
    		return bean;
    	}
    
    	/**
    	 * Create a proxy with the configured interceptors if the bean is
    	 * identified as one to proxy by the subclass.
    	 * @see #getAdvicesAndAdvisorsForBean
    	 */
    	@Override
    	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    		if (bean != null) {
    			Object cacheKey = getCacheKey(bean.getClass(), beanName);
    			if (!this.earlyProxyReferences.contains(cacheKey)) {
                    // 包装bean对象
    				return wrapIfNecessary(bean, beanName, cacheKey);
    			}
    		}
    		return bean;
    	}
    

    第一个方法没有对bean做什么处理,我们跳过。主要看第二个方法,打上断点。发现在创建Demo对象(被代理对象)时会进入org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary方法。该方法前面有一系列的判断,Demo对象都是false,暂时先不管。主要看Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

    /**
    	 * 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.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.
            // 判断当前bean是否需要切入的通知方法,有就返回并创建代理对象
    		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;
    	}
    

    dedug进入该方法,来到org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean方法。

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

    再进入来到org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors方法。该方法会首先找到所有的通知方法(标注@Before,@After,@AfterReturning,@AfterThrowing的方法),然后找到可以应用到传入类型beanClass的通知方法并进行排序返回。排序规则是@AfterThrowing-》@AfterReturning-》@After-》@Before,后面我们将会看到为什么这么排序。

    /**
    	 * Find all eligible Advisors for auto-proxying this class.
    	 * @param beanClass the clazz to find advisors for
    	 * @param beanName the name of the currently proxied bean
    	 * @return the empty List, not {@code null},
    	 * if there are no pointcuts or interceptors
    	 * @see #findCandidateAdvisors
    	 * @see #sortAdvisors
    	 * @see #extendAdvisors
    	 */
    	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
            // 找到所有候选的通知方法
    		List<Advisor> candidateAdvisors = findCandidateAdvisors();
            // 找到可以应用到当前bean的通知方法
    		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    		extendAdvisors(eligibleAdvisors);
    		if (!eligibleAdvisors.isEmpty()) {
                // 给通知方法排序,
    			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    		}
    		return eligibleAdvisors;
    	}
    

    回到org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary方法。现在可以知道getAdvicesAndAdvisorsForBean方法的作用就是找到能切入当前bean的排序过的通知方法。如果能找到就会创建一个代理对象返回。

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    		...
    		// Create proxy if we have advice.
    		// 找到能切入当前bean的增强器(通知方法)
    		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;
    	}
    

    我们再来看下代理对象的创建过程。该方法主要做了两件事:

    1. 创建一个ProxyFactory对象,并将排序好的通知方法保存在对象里。
    2. 创建代理对象,并将ProxyFactory对象保存在对象里。
    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 proxyFactory = new ProxyFactory();
    		proxyFactory.copyFrom(this);
    
    		if (!proxyFactory.isProxyTargetClass()) {
    			if (shouldProxyTargetClass(beanClass, beanName)) {
    				proxyFactory.setProxyTargetClass(true);
    			}
    			else {
    				evaluateProxyInterfaces(beanClass, proxyFactory);
    			}
    		}
    		
    		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        	// 将排序好的通知方法放入proxyFactory
    		proxyFactory.addAdvisors(advisors);
    		proxyFactory.setTargetSource(targetSource);
    		customizeProxyFactory(proxyFactory);
    
    		proxyFactory.setFrozen(this.freezeProxy);
    		if (advisorsPreFiltered()) {
    			proxyFactory.setPreFiltered(true);
    		}
    		// 创建代理对象
    		return proxyFactory.getProxy(getProxyClassLoader());
    	}
    

    至此,一个代理对象就创建完成。之后该对象会注入容器,后面从容器中获取该类型的对象会得到一个代理对象,执行代理对象的方法会执行性相关通知。

    代理方法的调用

    我们给代理对象调用方法打上断点,debug运行。

    可以看到代理对象中保存了之前创建的ProxyFactory对象,并且ProxyFactory对象中保存了能应用到该方法的通知方法。debug进入,来到org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept方法内。

    @Override
    		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.
    					Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
    					retVal = methodProxy.invoke(target, argsToUse);
    				}
    				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);
    				}
    			}
    		}
    

    其中获取拦截器chain链其实就是将通知方法转换成拦截器。我们可以来看下。

    转化操作位于org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry#getInterceptors方法中。对通知的转化主要分两种:

    1. 通知属于MethodInterceptor,直接强转为MethodInterceptor加入拦截链;
    2. 通知不属于MethodInterceptor,使用AdvisorAdapter包装成MethodInterceptor加入拦截器链。

    其中后置通知属于MethodInterceptor,前置通知、返回通知和异常通知需要AdvisorAdapter进行包装。

    @Override
    	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);
    		}
    		for (AdvisorAdapter adapter : this.adapters) {
                // 不属于MethodInterceptor,使用AdvisorAdapter包装成MethodInterceptor然后加入
    			if (adapter.supportsAdvice(advice)) {
    				interceptors.add(adapter.getInterceptor(advisor));
    			}
    		}
    		if (interceptors.isEmpty()) {
    			throw new UnknownAdviceTypeException(advisor.getAdvice());
    		}
    		return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
    	}
    

    回到org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept

    再拿到拦截器链之后,进行判断。如果拦截器链为空,则直接调用目标方法。否则新建CglibMethodInvocation对象调用procee方法。

    @Override
    		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    		...
                    // 获取目标对象
    				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.
    					Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);				 // 调用目标方法
    					retVal = methodProxy.invoke(target, argsToUse);
    				}
    				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);
    				}
    			}
    		}
    

    进入org.springframework.aop.framework.ReflectiveMethodInvocation#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();
    		}
    		// 当前拦截器计数加一(从-1开始),获取下一个拦截器(当前拦截器计数位置的拦截器)
    		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.
                // 执行对应拦截器的invoke方法
    			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    		}
    	}
    

    proceed方法主要工作是,判断当前拦截器是否是拦截器链中最后一个拦截器,如果是,则执行目标方法;否则获取下一个拦截器,执行拦截器的invoke方法。按照拦截器链的顺序,各个invoke方法执行顺序为异常-》返回-》后置-》前置

    而对于不同的拦截器,invoke方法的实现不同,具体如下

    • 异常拦截器org.springframework.aop.aspectj.AspectJAfterThrowingAdvice#invoke
    @Override
    	public Object invoke(MethodInvocation mi) throws Throwable {
    		try {
    			return mi.proceed();
    		}
    		catch (Throwable ex) {
    			if (shouldInvokeOnThrowing(ex)) {
                    // 执行异常通知方法
    				invokeAdviceMethod(getJoinPointMatch(), null, ex);
    			}
    			throw ex;
    		}
    	}
    
    • 返回拦截器org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor#invoke
    @Override
    	public Object invoke(MethodInvocation mi) throws Throwable {
    		Object retVal = mi.proceed();
            // 执行返回通知方法
    		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
    		return retVal;
    	}
    
    • 后置拦截器org.springframework.aop.aspectj.AspectJAfterAdvice#invoke
    @Override
    	public Object invoke(MethodInvocation mi) throws Throwable {
    		try {
    			return mi.proceed();
    		}
    		finally {
                // 执行后置通知方法
    			invokeAdviceMethod(getJoinPointMatch(), null, null);
    		}
    	}
    
    
    • 前置拦截器org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor#invoke
    @Override
    	public Object invoke(MethodInvocation mi) throws Throwable {
            // 执行前置通知方法
    		this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
    		return mi.proceed();
    	}
    

    这里Spring用了一个很巧妙的设计,类似于递归调用。每个拦截器的invoke方法都会调用proceed方法,proceed方法又会调用下一个拦截器链的invoke方法...直到当前拦截器是最后一个拦截器。调用最后一个拦截器的invoke方法,也会调用proceed方法。这时this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1返回true,会执行目标方法。目标方法执行完,proceed方法结束后会回到invoke方法。invoke方法结束后,又会回到proceed方法,proceed方法结束又会回到上一个拦截器的invoke方法...借由这样的机制可以让拦截器按顺序执行。顺序为前置-》后置-》返回-》异常,和拦截器链的顺序相反。

    另外,注意到前置拦截器的前置通知方法是在调用proceed方法之前执行,这就保证了前置通知会在目标方法调用之前执行。还有除了异常拦截器,其余拦截器都没有捕获异常。这就导致如果目标方法出现异常,除了前置通知方法会执行(前置通知会在目标方法调用之前执行),其余通知方法不会执行,直到来到异常拦截器执行异常通知方法。这样就实现了异常通知的功能。

    以上,就是SpringAOP从创建到调用的全部解析。只是浅略的分析,以后有机会再完善。

    Stay Hungry , Stay Foolish , Stay Patient , Stay Love !
  • 相关阅读:
    广义线性模型 GLM
    最大熵模型 Maximum Entropy Model
    Ensemble Learning 之 Bagging 与 Random Forest
    Ensemble Learning 之 Gradient Boosting 与 GBDT
    Ensemble Learning 之 Adaboost
    集成学习概述
    决策树之 CART
    用于分类的决策树(Decision Tree)-ID3 C4.5
    朴素贝叶斯(Naive Bayes)
    动态规划 Dynamic Programming
  • 原文地址:https://www.cnblogs.com/henryyao/p/11662812.html
Copyright © 2011-2022 走看看