在进行源码阅读之前建议先看一下这篇文章:Spring AOP 源码分析系列文章导读 by 田小波,写的非常好,推荐阅读。
关于 AOP 中常用的一些术语这里就不解释了,如果不清楚的建议先看一遍上面推荐的文章。
一、AOP 配置入口
在分析源码之前,我们先来看一下 XML 中关于 AOP 的配置:
<bean id="userServiceImpl" class="com.jas.mess.aop.UserServiceImpl"/>
<bean id="ownerAspect" class="com.jas.mess.aop.spring.OwnerAspect"/>
<aop:config>
<aop:aspect ref="ownerAspect">
<aop:pointcut id="ownerPointcut" expression="execution(* com.jas.mess.aop.*.*(..))"/>
<aop:before method="recordLog" pointcut-ref="ownerPointcut"/>
</aop:aspect>
</aop:config>
在以前的文章里有总结过,XML 文件的配置信息会被转化成 BeanDefinition,其中 AOP 相关的配置也不例外,从上面的例子中我们能看出一个明显的区别:普通的 bean 标签与 AOP 配置标签不一样,一个是 <bean /> 一个是 <aop />,除此之外你要想配置 AOP,还需要在 XMl 中引入 xmlns:aop="http://www.springframework.org/schema/aop" 命名空间。
普通的 bean 标签与 AOP 标签处理逻辑通过命名空间区分,下面我们来看源码。
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 命名空间检查,默认的命名空间是 "http://www.springframework.org/schema/beans"
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
// <bean /> 标签处理入口
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
// <aop /> 标签处理入口
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
上面我们说过,AOP 的命名空间是 http://www.springframework.org/schema/aop,因此会执行到 delegate.parseCustomElement(ele)。
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 根据命名空间获取 handler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
上面的流程比较简单,根据命名空间 URI 获取命名空间 handler,这里对应的是 AopNamespaceHandler,通过 handler 去解析配置。handler 内部会根据标签配置信息得到 ConfigBeanDefinitionParser,最终调用 parse 方法。
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
// 注册 AspectJAwareAdvisorAutoProxyCreator 类型的beanDefinition,继承自 BeanPostProcessor
configureAutoProxyCreator(parserContext, element);
// 获取所有子标签
List<Element> childElts = DomUtils.getChildElements(element);
for (Element elt: childElts) {
String localName = parserContext.getDelegate().getLocalName(elt);
// 解析 <aop:poincut />
if (POINTCUT.equals(localName)) {
parsePointcut(elt, parserContext);
}
// 解析 <aop:>advisor />
else if (ADVISOR.equals(localName)) {
parseAdvisor(elt, parserContext);
}
// 解析 <aop:aspect />
else if (ASPECT.equals(localName)) {
parseAspect(elt, parserContext);
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
这里涉及到一个 <aop:advisor /> 标签,大家可能不太清楚它的作用,advisor 与 advice(<aop:before />,<aop:after />…)功能类似,都可以用来增强目标方法,具体用法可以参考这篇文章:Spring Aop(八)——advisor标签 by 234390216
parse 方法会处理 <aop:config /> 标签下的子标签,具体的处理逻辑后面会分析到。在解析标签之前先调用了 configureAutoProxyCreator 方法,这个方法比较重要。
二、注册 AspectJAwareAdvisorAutoProxyCreator beanDefinition
private void configureAutoProxyCreator(ParserContext parserContext, Element element) {
AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);
}
public static void registerAspectJAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
registerAspectJAutoProxyCreatorIfNecessary方法中有三个处理逻辑,下面一一来看下。
@Nullable
public static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
// 注意这里传的是 AspectJAwareAdvisorAutoProxyCreator 类型
return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);
}
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// AUTO_PROXY_CREATOR_BEAN_NAME = "org.springframework.aop.config.internalAutoProxyCreator"
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册一个类型为 AspectJAwareAdvisorAutoProxyCreator 的 beanDefinition
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
注册一个类型为 AspectJAwareAdvisorAutoProxyCreator 的 beanDefinition,注意此时还没有创建 bean,AspectJAwareAdvisorAutoProxyCreator 其实是一个 BeanPostProcessor,后面在对方法增强的时候会用到,具体细节会在后面的文章里总结。
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable 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:config />是否配置了 proxy-target-class 与 expose-proxy,如果有配置,则为 beanDefinition 添加 exposeProxy 与 proxyTargetClass 属性,这两个配置标签后面还会遇到,这里先简单介绍下作用。
proxy-target-class:默认为 false,如果设置为 true,会使用 CGLIB 创建代理对象
expose-proxy:默认为 false,作用:假设 A,B 方法都被增强,A 方法调用 B 方法,A 方法会被切面拦截,A 里的 B 方法是否会被切面拦截增强,默认不会
private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) {
if (beanDefinition != null) {
parserContext.registerComponent(
new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME));
}
}
注册组件。
三、解析 <aop:aspect ref="" id="" />
<aop:aspect /> 标签下可以配置多种类型的子标签,比如 <aop:before />、<aop:after />、<aop:pointcut />、<aop:declare-parents /> 等,不同类型的标签处理逻辑也是不一样的。
private void parseAspect(Element aspectElement, ParserContext parserContext) {
String aspectId = aspectElement.getAttribute(ID);
// 切面 beanName
String aspectName = aspectElement.getAttribute(REF);
try {
// 添加进 LinkedList
this.parseState.push(new AspectEntry(aspectId, aspectName));
List<BeanDefinition> beanDefinitions = new ArrayList<>();
List<BeanReference> beanReferences = new ArrayList<>();
// 获取 <aop:declare-parents /> 子标签
List<Element> declareParents = DomUtils.getChildElementsByTagName(aspectElement, DECLARE_PARENTS);
for (Element declareParentsElement : declareParents) {
beanDefinitions.add(parseDeclareParents(declareParentsElement, parserContext));
}
NodeList nodeList = aspectElement.getChildNodes();
boolean adviceFoundAlready = false;
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
// 处理通知节点 <aop:before,after, around ... />
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));
}
// 处理通知
AbstractBeanDefinition advisorDefinition = parseAdvice(
aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
beanDefinitions.add(advisorDefinition);
}
}
// 创建 AspectComponentDefinition
AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition(
aspectElement, aspectId, beanDefinitions, beanReferences, parserContext);
parserContext.pushContainingComponent(aspectComponentDefinition);
// 获取所有切点 element
List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);
for (Element pointcutElement : pointcuts) {
parsePointcut(pointcutElement, parserContext);
}
parserContext.popAndRegisterContainingComponent();
}
finally {
this.parseState.pop();
}
}
主要流程如下:
- 获取
<aop:aspect >的 id 与 ref 属性,封装成AspectEntry添加到parseState集合中,流程处理完后,出栈 - 获取
<aop:declare-parents />子标签,如果有配置则封装成 beanDefinition - 解析
<aop:aspect >下所有子标签,循环处理通知配置 - 解析切点配置
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)));
RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
// 切面 bean(类)
methodDefinition.getPropertyValues().add("targetBeanName", aspectName);
// 设置切面方法
methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method"));
methodDefinition.setSynthetic(true);
// 创建切面 BeanDefinition
RootBeanDefinition aspectFactoryDef =
new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);
aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);
aspectFactoryDef.setSynthetic(true);
// 创建通知 BeanDefinition,此时已明确通知类型
AbstractBeanDefinition adviceDef = createAdviceDefinition(
adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,
beanDefinitions, beanReferences);
RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);
advisorDefinition.setSource(parserContext.extractSource(adviceElement));
// 将上面创建的 adviceDef 注入到 advisorDefinition 中
advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);
// <aop:aspect ref="" order="" />,控制切面方法优先级
if (aspectElement.hasAttribute(ORDER_PROPERTY)) {
advisorDefinition.getPropertyValues().add(
ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));
}
// 注册通知 BeanDefinition
parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);
return advisorDefinition;
}
finally {
this.parseState.pop();
}
}
上面逻辑主要是创建出 advisorDefinition,并注册到 beanFactory 中,由此可见,我们配置的每一个通知,最终都会被转化成 beanDefinition 并注册到 IoC 容器中。
上面这个方法深追下去知识点还挺多的,比如 createAdviceDefinition 方法,注册 beanDefinition 时 beanName 的生成规则等,有兴趣的自己可以研究下,这里就不介绍了,看不懂的地方可以通过 DEBUG 的方式帮助理解。
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));
// 创建切点 beanDefinition,类型为 ConfigurableBeanFactory.SCOPE_PROTOTYPE
pointcutDefinition = createPointcutDefinition(expression);
pointcutDefinition.setSource(parserContext.extractSource(pointcutElement));
String pointcutBeanName = id;
// 注册切点 BeanDefinition 到 beanFactory 中
if (StringUtils.hasText(pointcutBeanName)) {
parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition);
}
else {
// <aop:pointcut id="" expression=""/> 没有配置 id 先生成 beanName 在注册到 beanFactory 中
pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition);
}
parserContext.registerComponent(
new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression));
}
finally {
this.parseState.pop();
}
return pointcutDefinition;
}
protected AbstractBeanDefinition createPointcutDefinition(String expression) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);
// 切点作用域原型
beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
beanDefinition.setSynthetic(true);
beanDefinition.getPropertyValues().add(EXPRESSION, expression);
return beanDefinition;
}
处理切点的逻辑与通知类似,注意切点 beanDefinition 的作用域并非单例而是原型,初始化 beanDefinition 后注册到 IoC 容器中。
参考
【Spring源码分析】AOP源码解析(上篇) by 五月的仓颉
本文深入分析 Spring AOP 的源码实现,详细解读 AOP 配置的加载过程,包括 XML 配置的解析、AspectJAwareAdvisorAutoProxyCreator 的注册以及切面、通知和切点的定义。
5129

被折叠的 条评论
为什么被折叠?



