栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring系列之AOP工作过程详解一

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring系列之AOP工作过程详解一

引言

spring的IOC系列已经正式完结了,这一个星期一直在想着接下来更新什么系列,想了想,面试里面不就是IOC和AOP考的最多吗?那么就索性接下来开始更新AOP系列的文章,就非常的河狸

aop:config

aop部分的解析器由AopNamespaceHandler注册,其init方法:

@Override
public void init() {
    registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
    registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
    registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
}

而init方法的调用则是在Spring解析XMl文件过程中的DefaultNamespaceHandlerResolver#resolve方法中。

其中ConfigBeanDefinitionParser用于解析aop:config,的此标签用以配置pointcut, advisor, aspect,实例:


    
    
    

ConfigBeanDefinitionParser.parse:

@Override
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		CompositeComponentDefinition compositeDef =
				new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
		parserContext.pushContainingComponent(compositeDef);

		configureAutoProxyCreator(parserContext, element);

		List childElts = DomUtils.getChildElements(element);
		for (Element elt: childElts) {
			String localName = parserContext.getDelegate().getLocalName(elt);
			if (POINTCUT.equals(localName)) {
				parsePointcut(elt, parserContext);
			}
			else if (ADVISOR.equals(localName)) {
				parseAdvisor(elt, parserContext);
			}
			else if (ASPECT.equals(localName)) {
				parseAspect(elt, parserContext);
			}
		}

		parserContext.popAndRegisterContainingComponent();
		return null;
	}

还是分部分讲解:

    解析代理子类的生成

这篇文章只讲解第一部分“解析过程”

解析

解析的过程主要分为以下几个部分。

proxy-target-class & expose-proxy

对应着aop:config的两个属性,前者代表是否为被代理这生成CGLIB子类,默认false,只为接口生成代理子类(话说如果不生成子类那么怎么拦截?)。后者代表是否将代理bean暴露给用户,如果暴露,可以通过Spring AopContext类获得,默认不暴露。

解析的过程无非就是属性的读取,不再详细说明。

对应代码:

AopNamespaceUtils:

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
		if (sourceElement != null) {
			boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
			if (proxyTargetClass) {
				AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
			}
			boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
			if (exposeProxy) {
				AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
			}
		}
	}

aop:pointcut

pointcut的解析是生成一个BeanDefinition并将其id, expression等属性保存在BeanDefinition中。注意以下几点:

BeanDefinition的ID来自于id属性,如果没有,那么自动生成。BeanDefinition的class是AspectJexpressionPointcutBeanDefinition的scope为prototype。

AspectJexpressionPointcut类图:

对应代码:
ConfigBeanDefinitionParser:

private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) {
		String id = pointcutElement.getAttribute(ID);
		String expression = pointcutElement.getAttribute(expression);

		AbstractBeanDefinition pointcutDefinition = null;

		try {
			this.parseState.push(new PointcutEntry(id));
			pointcutDefinition = createPointcutDefinition(expression);
			pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));

			String pointcutBeanName = id;
			if (StringUtils.hasText(pointcutBeanName)) {
				parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
			}
			else {
				pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
			}

			parserContext.registerComponent(
					new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
		}
		finally {
			this.parseState.pop();
		}

		return pointcutDefinition;
	}

aop:advisor

首先是其所有属性的示例:


advisor概念是Spring独有的,来自于上古时代,应该是较早时候的aop概念的实现: AOP Alliance (Java/J2EE AOP standards)。Spring官方的说法: aop-schema-advisors。

advice-ref是必须的属性,并且这里的advice必须实现org.aopalliance.aop.Advice的子接口。这些子接口指的什么呢?比如org.aopalliance.intercept.MethodInterceptor

最常见的用途就是结合事务使用:


    
        
        
        
    



    
    

解析的套路和上面类似,只不过此处的beanClass是DefaultBeanFactoryPointcutAdvisor,其类图:

另外注意对于pointcut和pointcut-ref两者处理的区别,对于pointcut属性,Spring会同样创建一个AspectJexpressionPointcut类型的BeanDefinition,对于pointcut-ref会生成一个RuntimeBeanReference对象指向原pointcut的引用。此类的类图:

可以看出,这种aop的实现需要实现各种接口,所以不应该再使用此种方式进行aop,除了Spring内部的实现。

对应代码:

	private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
		AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
		String id = advisorElement.getAttribute(ID);

		try {
			this.parseState.push(new AdvisorEntry(id));
			String advisorBeanName = id;
			if (StringUtils.hasText(advisorBeanName)) {
				parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
			}
			else {
				advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
			}

			Object pointcut = parsePointcutProperty(advisorElement, parserContext);
			if (pointcut instanceof BeanDefinition) {
				advisorDef.getPropertyValues().add(POINTCUT, pointcut);
				parserContext.registerComponent(
						new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
			}
			else if (pointcut instanceof String) {
				advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
				parserContext.registerComponent(
						new AdvisorComponentDefinition(advisorBeanName, advisorDef));
			}
		}
		finally {
			this.parseState.pop();
		}
	}

aop:aspect

配置举例:





    
    
        
        
    

解析形成的BeanDefinition结构如下:

AspectComponentDefinition
    beanRefArray
        RuntimeBeanReference(aop:aspect的ref属性)
    beanDefArray
        // 被注册
        RootBeanDefinition(aop:declare-parents)
            beanClass: DeclareParentsAdvisor
            ConstructorArg
                implement-interface
                types-matching
                default-impl
                delegate-ref
        // 被注册
        RootBeanDefinition(aop:before,aop:after...)
            beanClass: AspectJPointcutAdvisor
            ConstructorArg
                RootBeanDefinition
                    beanClass: 由子标签决定
                    ConstructorArg
                        RootBeanDefinition
                            beanClass: MethodLocatingFactoryBean
                            properties
                                targetBeanName: aspectName
                                methodName: method属性
                        RootBeanDefinition
                            beanClass: SimpleBeanFactoryAwareAspectInstanceFactory
                            properties
                                aspectBeanName: aspectName
                        //还有pointcut定义和引用...

结构图里面的aspectName来自于aop:aspect的ref属性,此属性是必须配置的,因为Spring要知道aop:before等标签指定的方法是哪个bean/类/对象的方法。

aop:declare-parents
对于aop:declare-parents子标签,其决定的是代理子类应该实现哪些接口:


此标签最终被解析成为beanClass为DeclareParentsAdvisor的BeanDefinition,并注册到容器中。其类图:

其它
此处的其它指的是aop:before, aop:after等最核心的标签。其最终被解析为beanClass为AspectJPointcutAdvisor的BeanDefinition,类图:

正如上面结构图里所描述的,其构造参数为一个BeanDefintion,此对象的beanClass是不确定的,由aop:before/after中的before和after决定,代码:

private Class getAdviceClass(Element adviceElement, ParserContext parserContext) {
    String elementName = parserContext.getDelegate().getLocalName(adviceElement);
    if (BEFORE.equals(elementName)) {
        return AspectJMethodBeforeAdvice.class;
    } else if (AFTER.equals(elementName)) {
        return AspectJAfterAdvice.class;
    } else if (AFTER_RETURNING_ELEMENT.equals(elementName)) {
        return AspectJAfterReturningAdvice.class;
    } else if (AFTER_THROWING_ELEMENT.equals(elementName)) {
        return AspectJAfterThrowingAdvice.class;
    } else if (AROUND.equals(elementName)) {
        return AspectJAroundAdvice.class;
    }
}

而此BeanDefintion的构造参数又由以下几个部分组成:

    MethodLocatingFactoryBeanSimpleBeanFactoryAwareAspectInstanceFactory

MethodLocatingFactoryBean
第一个便是beanClass为此类型的BeanDefinition。其内部有一个methodName属性,存储的便是标签的method属性的值。其类图:

这个东西是干什么用的呢?其实是用于在指定的advice(aop:aspect的ref属性)中得到Method对象。入口在setBeanFactory方法:

@Override
public void setBeanFactory(BeanFactory beanFactory) {
    Class beanClass = beanFactory.getType(this.targetBeanName);
    this.method = BeanUtils.resolveSignature(this.methodName, beanClass);
}

SimpleBeanFactoryAwareAspectInstanceFactory
其类图:

此类用于在BeanFactory中定位aspect bean,这个bean指的是谁?


就是它!查找很简单:

@Override
public Object getAspectInstance() {
    return this.beanFactory.getBean(this.aspectBeanName);
}

从整个aop:aspect标签最终被解析为一个AspectJPointcutAdvisor来看,Spring在实现上仍将其作为Advisor的概念。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/781471.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号