• Spring注解开发(四)AOP原理与源码分析


    先来看一个简单的例子:

    先建一个Person类作为业务类:

    public class Person {
        public int doDivision(int a, int b) {
            return (a / b);
        }
    }

    新建切面类LogWriter,此处只加了两个通知:

    @Aspect //指定此类为一个切面类
    public class LogWriter {
        //切点及切入点表达式
        @Pointcut("execution(public int com.practice.bean.Person.*(..))")
        public void pointCut() {
        }
    
        @Before("pointCut()") //前置通知
        public void beforeDivision(JoinPoint joinPoint) {
            Object[] args = joinPoint.getArgs();
            System.out.println("LogWriter....+before" + joinPoint.getSignature().getName() + Arrays.asList(args));
        }
    
        //返回通知, 在方法返回结果之后执行
        @AfterReturning(value="pointCut()",returning="result")
        public void divisionReturn(JoinPoint joinPoint,Object result){
            System.out.println(""+joinPoint.getSignature().getName()+"正常返回,结果为:"+result);
        }
    }

    配置类如下,为注入业务类和启用自动代理:

    @Configuration
    @EnableAspectJAutoProxy//启用切面自动代理
    public class MainConfig {
        @Bean
        public Person person() {
            return new Person();
        }
        @Bean
        public LogWriter logWriter() {
            return new LogWriter();
        }
    }

    执行测试类:

        @Test
        public void test01() {
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
            Person person = applicationContext.getBean(Person.class);
            person.doDivision(1, 2);
        }

    结果如下:

    LogWriter....+beforedoDivision[1, 2]
    doDivision正常返回,结果为:0

    Tips:AspectJ支持五种切面类型的通知注解:

    • @Before: 前置通知, 在方法执行之前执行
    • @After: 后置通知, 在方法执行之后执行 。
    • @AfterRunning: 返回通知, 在方法返回结果之后执行
    • @AfterThrowing: 异常通知, 在方法抛出异常之后,(方法参数中加上Exception可以接收到返回的异常)
    • @Around: 环绕通知, 围绕着方法执行

    @Around方法做一个测试,还是以上代码,切面类中的方法为:

     @Around("pointCut()")
        public Object aroundMethod(ProceedingJoinPoint jointPoint) throws Throwable{
             //前置方法
            long startTime=System.currentTimeMillis();
            System.out.println("-->开始时间为:"+startTime);
            //调用执行目标方法(result为目标方法执行结果)
            Object result=jointPoint.proceed();
            System.out.println("执行结果为:"+result);
            long endTime=System.currentTimeMillis();
            //后置方法。记录结束时间和执行时长
            System.out.println("-->结束时间为:"+endTime+"。执行时长为:"+(endTime-startTime));
            //获取业务方法签名
            System.out.println(jointPoint.getSignature().getName());
            //获取执行参数
            System.out.println(Arrays.asList(jointPoint.getArgs()));
            return result;
        }

    测试结果为:

    -->开始时间为:1571236822985
    执行结果为:0
    -->结束时间为:1571236822992。执行时长为:7
    doDivision
    [1, 2]
    

    一.在配置类中,引入了@EnableAspectJAutoProxy注解,这个注解是做什么的?

    1.先看一下这个注解的源代码:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import(AspectJAutoProxyRegistrar.class)
    public @interface EnableAspectJAutoProxy {

    可以看出该注解为容器导入了AspectJAutoProxyRegistrar组件,

    class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar

    该组件实现了ImportBeanDefinitionRegistrar接口,这个接口可以为容器导入组件(在第一篇文章中已有介绍)

    可以看到为容器导入了org.springframework.aop.config.internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator

    导入的类如下(源码注释):

    Registers an {@link org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
    
    AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    ------->
    	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
    			BeanDefinitionRegistry registry, @Nullable Object source) {
    
    		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    	}

    beanName为:

    public static final String AUTO_PROXY_CREATOR_BEAN_NAME =
    			"org.springframework.aop.config.internalAutoProxyCreator";

    二.注入的AnnotationAwareAspectJAutoProxyCreator组件做了什么?

    AnnotationAwareAspectJAutoProxyCreator类图如下:

    21.该类实现了Aware接口下的BeanFactoryAware接口,说明为AnnotationAwareAspectJAutoProxyCreator类注入了beanFactory。

    具体实现在AbstractAdvisorAutoProxyCreator类中

    @Override
    	public void setBeanFactory(BeanFactory beanFactory) {
    		super.setBeanFactory(beanFactory);
    		if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
    			throw new IllegalArgumentException(
    					"AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory);
    		}
    		initBeanFactory((ConfigurableListableBeanFactory) beanFactory);
    	}
    
    	protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    		this.advisorRetrievalHelper = new BeanFactoryAdvisorRetrievalHelperAdapter(beanFactory);
    	}

    initBeanFactory又被子类AnnotationAwareAspectJAutoProxyCreator重写:

    @Override
    	protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    		super.initBeanFactory(beanFactory);
    		if (this.aspectJAdvisorFactory == null) {
    			this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
    		}
    		this.aspectJAdvisorsBuilder =
    				new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
    	}

    2.2实现了BeanPostProcess接口,说明在初始化Bean阶段会调用后置处理器方法。

     

    三.AnnotationAwareAspectJAutoProxyCreater组件的创建过程。


    在registerBeanPostProcessors(beanFactory)方法中注册bean的后置处理器来拦截bean的创建:

    进入registerBeanPostProcessors方法,

    • 先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor的ID;
    • 注册BeanPostProcessorChecker;
    • 优先注册实现了PriorityOrdered接口的BeanPostProcessor;
    • 再给容器中注册实现了Ordered接口的BeanPostProcessor;
    • 注册没实现优先级接口的BeanPostProcessor;

    在beanFactory.getBean(ppName, BeanPostProcessor.class)创建了BeanPostProcess组件,最后注册组件到容器中:

    //找到所有的BeanPostProcessessors,并注册到容器中
    registerBeanPostProcessors(beanFactory, internalPostProcessors);
    ---》
     private static void registerBeanPostProcessors(
    			ConfigurableListableBeanFactory beanFactory, List<BeanPostProcessor> postProcessors) {
    
    		for (BeanPostProcessor postProcessor : postProcessors) {
    			beanFactory.addBeanPostProcessor(postProcessor);
    		}
    	}

     

    四.AnnotationAwareAspectJAutoProxyCreator(InstantiationAwareBeanPostProcessor) 的作用:

    • 以下为获取业务类Person为例:

    进入refresh()方法中的finishBeanFactoryInitialization(beanFactory),完成BeanFactory初始化工作,创建剩下的单实例bean

    getBean->doGetBean()->getSingleton()-->先从缓存中获取,如果没有获取到则执行:

    return createBean(beanName, mbd, args);进入创建Bean方法。

    1.进入此方法,我们在容器中获取要创建的组件,在组件创建之前,会执行下面的方法:

    给后置处理器一个机会去创建代理实例去替代Bean的实例对象:

    try {
    	// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
    	Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
    	if (bean != null) {
    		return bean;
    		}
    	}

    进入resolveBeforeInstantiation方法,有执行下面两个方法:

    ---->applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);

    ---> applyBeanPostProcessorsAfterInitialization(bean, beanName);

    第一个方法的主要逻辑执行的为下面这个接口中定义的方法:

    public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor

     判断是否为InstantiationAwareBeanPostProcessor,如果是则执行ibp.postProcessBeforeInstantiation(beanClass, beanName);

    此方法为AbstractAutoProxyCreator实现:

    • 判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
    •  判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,或者是否是切面(@Aspect)或是否需要跳过
    @Override
    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
    	// TODO: Consider optimization by caching the list of the aspect names
    	List<Advisor> candidateAdvisors = findCandidateAdvisors();
    	for (Advisor advisor : candidateAdvisors) {
    		if (advisor instanceof AspectJPointcutAdvisor &&
    				((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) {
    		return true;
    		}
    	}
    	return super.shouldSkip(beanClass, beanName);
    }
    • 获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】 每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true
    • 父类super方法永远返回false

    Tips:在此处不会执行以下方法创建代理对象逻辑:

    /*如果我们有自定义的targetsource将在此处创建代理。禁止不必要的目标bean默认实例化:targetsource将以自定义方式处理目标实例。*/
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);	
    if (targetSource != null) {
    			if (StringUtils.hasLength(beanName)) {
    				this.targetSourcedBeans.add(beanName);
    			}
    			Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
    			Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
    			this.proxyTypes.put(cacheKey, proxy.getClass());
    			return proxy;
    		}

    返回为空;第二个applyBeanPostProcessorsAfterInitialization方法在此处不会执行;

    2.接下来会进入创建对象的逻辑:

    doCreateBean-->initializeBean-->

    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

    第二个方法主要逻辑在这-->processor.postProcessAfterInitialization(result, beanName)

    此处在AbstractAutoProxyCreator类中实现:

    	@Override
    	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    		if (bean != null) {
    			Object cacheKey = getCacheKey(bean.getClass(), beanName);
    			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
    				return wrapIfNecessary(bean, beanName, cacheKey);
    			}
    		}
    		return bean;
    	}

    -->return wrapIfNecessary(bean, beanName, cacheKey):

    如果有通知的话则创建代理

    // 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;
    	}

    1、获取当前bean的所有通知方法  Object[]  specificInterceptors
                 1、找到候选的所有的通知方法(找哪些通知方法是需要切入当前bean方法的)
                 2、获取到能在bean使用的通知方法。
                 3、给获取到的通知方法排序
    2、将当前bean保存到advisedBeans中;
    3、创建当前bean的代理对象;

    return proxyFactory.getProxy(getProxyClassLoader());

              1、获取所有通知方法
              2、保存到proxyFactory
              3、创建代理对象:Spring自动决定

    @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);
    		}
    		return new ObjenesisCglibAopProxy(config);
    	}
    	else {
    		return new JdkDynamicAopProxy(config);
    	}
    }

                  Tips:两种代理方式:

                     1.JdkDynamicAopProxy(config);jdk动态代理;
                     2.ObjenesisCglibAopProxy(config);cglib的动态代理;
    4、给容器中返回当前组件使用cglib增强了的代理对象;
    5、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;

     

    五、代理对象方法的执行

    在业务方法打上断点,执行进入:

    容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx);

    1.进入CglibAopProxy类的下面这个方法:拦截目标方法的执行

    CglibAopProxy.intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) 

    2.根据ProxyFactory对象获取将要执行的目标方法拦截器链:

    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

    进入上面的方法,主要逻辑如下--->拦截器链的获取:

    cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);

    用List<Object> interceptorList = new ArrayList<>(advisors.length);来保存所有拦截器

    可以看到其中有两个:

    一个默认的ExposeInvocationInterceptor 和 1个LogWriter的aroundMethod通知方法。

    遍历所有的通知方法,将其转为Interceptor---->registry.getInterceptors(advisor);

    如果是MethodInterceptor,直接加入到集合中,如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor;
    转换完成返回MethodInterceptor数组;

    拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制)

    registry.getInterceptors(advisor);
    -----》
    @Override
    	public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    		List<MethodInterceptor> interceptors = new ArrayList<>(3);
    		Advice advice = advisor.getAdvice();
    		if (advice instanceof MethodInterceptor) {
    			interceptors.add((MethodInterceptor) advice);
    		}
    		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[0]);
    	}

    3.如果没有拦截器链,直接执行目标方法;
    4. 如果有拦截器链,把需要执行的目标对象,目标方法,拦截器链等信息传入创建的一个 CglibMethodInvocation 对象,并调用 proceed()方法

    retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

    5.拦截器链的触发过程:

         如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法;

    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    			return invokeJoinpoint();
    		}

          currentInterceptorIndex 默认为-1,每执行一个通知方法则++currentInterceptorIndex ;

    private int currentInterceptorIndex = -1;
    ++this.currentInterceptorIndex

    链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行;

    当遍历到最后一个拦截器时,执行

    return invokeJoinpoint();跳出递归调用

    前置通知在递归向上层返回时调用了,先执行了前置通知方法:

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

    执行完前置通知后进入目标方法执行然后依次执行各通知,利用catch,finally等控制异常通知和返回通知的顺序。

    拦截器链的机制,保证通知方法与目标方法的执行顺序;

  • 相关阅读:
    linux 解压命令
    在xampp集成环境下使用 thinkphp 连接oracle
    输入框实现新闻列表分页显示(一)
    MyEclipse获取注册码
    Oracle数据库创建表空间
    SQL Server之存储过程
    连接Oracle数据库帮助类
    Oracle数据库的导入和导出
    创建dml触发器
    java连接数据库步骤
  • 原文地址:https://www.cnblogs.com/demo-alen/p/13547230.html
Copyright © 2020-2023  润新知