一、前言
在开发过程中,程序员除了要关心主业务逻辑之外,还需要关注其他统一的日志、异常处理等,这些功能属于系统中不可缺少的功能,如果每一个接口的代码中都需要与这些日志、监控代码耦合在一起,往往会增加系统的维护成本,也会降低工作效率。为了解决这个问题,spring使用了面向切面编程(AOP:Aspect Oriented Programming)的思想,允许我们可以使用配置化的方式将这些通用功能注入到业务代码中。
对于Spring aop的分析将分为三个章节,分别是spring aop标签的解析,代理的生成,以及代理类的执行逻辑等。
Spring aop支持注解和xml的配置方式,xml的配置方式如下图。本文将介绍spring aop xml配置标签的原理。
二、标签解析
在自定义标签解析一文中,介绍了spring解析自定义标签的原理。对于<aop:config>标签来说,其解析类是ConfigBeanDefinitionParser。
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext){
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
//在容器中注册AspectJAwareAdvisorAutoProxyCreator,实现了
//BeanPostProcessor,用于生成类的代理
configureAutoProxyCreator(parserContext, element);
//对aop:config的下级标签进行处理
List<Element> childElts = DomUtils.getChildElements(element);
for (Element elt: childElts) {
String localName = parserContext.getDelegate().getLocalName(elt);
//如果下级标签是aop:pointcut,用于定义全局的切点
if (POINTCUT.equals(localName)) {
parsePointcut(elt, parserContext);
}
//如果下级标签是aop:advisor(用于定义通知,用于与aop:aspect略有差异)
else if (ADVISOR.equals(localName)) {
parseAdvisor(elt, parserContext);
}
//如果标签是aop:aspect,用于定义切面(切点+通知)
else if (ASPECT.equals(localName)) {
parseAspect(elt, parserContext);
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
AspectJAwareAdvisorAutoProxyCreator的类继承关系图如下,实现了BeanPostProcessor,我们可以猜测,代理类的生成逻辑:bean实例化完成后,调用AspectJAwareAdvisorAutoProxyCreator父类实现AbstractAutoProxyCreator实现的postProcessBeforeInstantiation(),根据我们配置的切点策略,判断是否为当前对象生成代理对象。
代码如下,具体逻辑在下文介绍。
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
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;
}
return null;
}
我们以aop:aspect作为例子,讲解spring如何解析aop自定义标签:
private void parseAspect(Element aspectElement, ParserContext parserContext) {
//获取id
String aspectId = aspectElement.getAttribute(ID);
//ref对应增强类的beanName
String aspectName = aspectElement.getAttribute(REF);
try {
this.parseState.push(new AspectEntry(aspectId, aspectName));
List<BeanDefinition> beanDefinitions = new ArrayList<>();
List<BeanReference> beanReferences = new ArrayList<>();
//是否配置了declare-parents,用于被代理类提供其他的接口功能
List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
for (int i = METHOD_INDEX; i < declareParents.size(); i++) {
Element declareParentsElement = declareParents.get(i);
beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
}
// We have to parse "advice" and all the advice kinds in one loop, to get the
// ordering semantics right.
//通过循环处理aop:aspect的下级标签,主要包含定义的切面。
NodeList nodeList = aspectElement.getChildNodes();
boolean adviceFoundAlready = false;
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
//是否是通知,aop:before,aop:after等
if (isAdviceNode(node, parserContext)) {
if (!adviceFoundAlready) {
adviceFoundAlready = true;
if (!StringUtils.hasText(aspectName)) {
parserContext.getReaderContext().error(
"<aspect> tag needs aspect bean reference via 'ref' attribute when declaring advices.",
aspectElement, this.parseState.snapshot());
return;
}
beanReferences.add(new RuntimeBeanReference(aspectName));
}
//解析aop:before这类节点成BeanDefinition,注入到容器中
AbstractBeanDefinition advisorDefinition = parseAdvice(
aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
beanDefinitions.add(advisorDefinition);
}
}
//构建切面的组合类
AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
parserContext.pushContainingComponent(aspectComponentDefinition);
List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
for (Element pointcutElement : pointcuts) {
parsePointcut(pointcutElement, parserContext);
}
parserContext.popAndRegisterContainingComponent();
}
finally {
this.parseState.pop();
}
}
//解析aop:before这类节点成BeanDefinition,注入到容器中
private AbstractBeanDefinition parseAdvice(
String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext,
List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
try {
this.parseState.push(new AdviceEntry(parserContext.getDelegate().getLocalName(adviceElement)));
// 创建MethodLocatingFactoryBean的工厂类beanDefifnition,用于返回对应的增强类方法
RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
methodDefinition.setSynthetic(true);
// create instance factory definition
RootBeanDefinition aspectFactoryDef =
new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
aspectFactoryDef.setSynthetic(true);
// 根据通知类型(before/after等),创建通知。
AbstractBeanDefinition adviceDef = createAdviceDefinition(
adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
beanDefinitions, beanReferences);
// 构建切面,切面=切点+通知
RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
advisorDefinition.setSource(parserContext.extractSource(adviceElement));
advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
//aop:aspect是否配置了order(切面的执行顺序)
if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
advisorDefinition.getPropertyValues().add(
ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
}
//在容器中注册切面(切点+通知)
parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
return advisorDefinition;
}
finally {
this.parseState.pop();
}
}
private AbstractBeanDefinition createAdviceDefinition(
Element adviceElement, ParserContext parserContext, String aspectName, int order,
RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,
List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {
RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
adviceDefinition.setSource(parserContext.extractSource(adviceElement));
adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);
adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);
//处理returning等标签
if (adviceElement.hasAttribute(RETURNING)) {
adviceDefinition.getPropertyValues().add(
RETURNING_PROPERTY, adviceElement.getAttribute(RETURNING));
}
if (adviceElement.hasAttribute(THROWING)) {
adviceDefinition.getPropertyValues().add(
THROWING_PROPERTY, adviceElement.getAttribute(THROWING));
}
if (adviceElement.hasAttribute(ARG_NAMES)) {
adviceDefinition.getPropertyValues().add(
ARG_NAMES_PROPERTY, adviceElement.getAttribute(ARG_NAMES));
}
//返回构造参数
ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();
//构造参数中添加增加类信息、切点、切面工厂类
cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);
Object pointcut = parsePointcutProperty(adviceElement, parserContext);
if (pointcut instanceof BeanDefinition) {
cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcut);
beanDefinitions.add((BeanDefinition) pointcut);
}
else if (pointcut instanceof String) {
RuntimeBeanReference pointcutRef = new RuntimeBeanReference((String) pointcut);
cav.addIndexedArgumentValue(POINTCUT_INDEX, pointcutRef);
beanReferences.add(pointcutRef);
}
cav.addIndexedArgumentValue(ASPECT_INSTANCE_FACTORY_INDEX, aspectFactoryDef);
//返回切面,切面里面定义了使用增强类中的哪个方法增加目标类,切点信息等
return adviceDefinition;
}
//根据通知类型,返回对应的处理类,比如aspect:before的处理类是AspectJMethodBeforeAdvice
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;
}
else {
throw new IllegalArgumentException("Unknown advice kind [" + elementName + "].");
}
}
//before类型通知的处理器,构造方法包含三个参数,分别是切面所在的增强方法、切点、切面的工厂类
public AspectJMethodBeforeAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
在容器中注册的切面的实现类是AspectJPointcutAdvisor:
public class AspectJPointcutAdvisor implements PointcutAdvisor, Ordered {
private final AbstractAspectJAdvice advice;
private final Pointcut pointcut;
@Nullable
private Integer order;
}
可以发现,切面=通知+切点,包含了生成代理的策略。
三、总结
至此,容器中注册了两种bean,第一种是AspectJAwareAdvisorAutoProxyCreator,用于在bean实例化后,生成代理;第二种是切面,切点+通知,用于判断是否生成代理的策略。
Spring解析<aop:config>标签的逻辑:
- 在容器中注册AspectJAwareAdvisorAutoProxyCreator,这是一种BeanPostProcessor,用于生成代理类;
- 遍历<aop:config>的子标签列表,如果是<aop:aspect>标签:解析<aop-aspect>的子标签,如果添加了declare-parents注解,则生成对应的BeanDefinition,根据子标签的类型(before/after/after-returning/after-throwing),选择对应Advice实现类,解析增强类和对应的增强方法、切点,生成AspectJPointcutAdvisor的beanDefinition(主要包含增强方法、切点等),并注册到容器中,这些bean都实现了Advisor接口。这些类将在AspectJAwareAdvisorAutoProxyCreator.postProcessBeforeInstantiation(),生成代理类时作为策略使用。