一、AOP代码准备
aop.xml文件准备,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd
">
<!-- <bean class="com.mashibing.MyBeanFactoryPostProcessorBySelf" ></bean>--><bean id="logUtil" class="com.mashibing.aop.xml.util.LogUtil" ></bean><bean id="myCalculator" class="com.mashibing.aop.xml.service.MyCalculator" ></bean><aop:config><aop:aspect ref="logUtil"><aop:pointcut id="myPoint" expression="execution( Integer com.mashibing.aop.xml.service.MyCalculator.* (..))"/><aop:around method="around" pointcut-ref="myPoint"></aop:around><aop:before method="start" pointcut-ref="myPoint"></aop:before><aop:after method="logFinally" pointcut-ref="myPoint"></aop:after><aop:after-returning method="stop" pointcut-ref="myPoint" returning="result"></aop:after-returning><aop:after-throwing method="logException" pointcut-ref="myPoint" throwing="e"></aop:after-throwing></aop:aspect></aop:config><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
以上是常规xml配置aop文件准备,下面是具体的切面java类准备。
package com.mashibing.aop.xml.util;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;import java.util.Arrays;//@Aspect
//@Component
//@Order(200)
public class LogUtil {// @Pointcut("execution(public Integer com.mashibing.service.MyCalculator.*(Integer,Integer))")public void myPointCut(){}// @Pointcut("execution(* *(..))")public void myPointCut1(){}// @Before(value = "myPointCut()")private int start(JoinPoint joinPoint){//获取方法签名Signature signature = joinPoint.getSignature();//获取参数信息Object[] args = joinPoint.getArgs();System.out.println("log---"+signature.getName()+"方法开始执行:参数是"+Arrays.asList(args));return 100;}// @AfterReturning(value = "myPointCut()",returning = "result")public static void stop(JoinPoint joinPoint,Object result){Signature signature = joinPoint.getSignature();System.out.println("log---"+signature.getName()+"方法执行结束,结果是:"+result);}// @AfterThrowing(value = "myPointCut()",throwing = "e")public static void logException(JoinPoint joinPoint,Exception e){Signature signature = joinPoint.getSignature();System.out.println("log---"+signature.getName()+"方法抛出异常:"+e.getMessage());}// @After("myPointCut()")public static void logFinally(JoinPoint joinPoint){Signature signature = joinPoint.getSignature();System.out.println("log---"+signature.getName()+"方法执行结束。。。。。over");}// @Around("myPointCut()")public Object around(ProceedingJoinPoint pjp) throws Throwable {Signature signature = pjp.getSignature();Object[] args = pjp.getArgs();Object result = null;try {System.out.println("log---环绕通知start:"+signature.getName()+"方法开始执行,参数为:"+Arrays.asList(args));//通过反射的方式调用目标的方法,相当于执行method.invoke(),可以自己修改结果值result = pjp.proceed(args);
// result=100;System.out.println("log---环绕通知stop"+signature.getName()+"方法执行结束");} catch (Throwable throwable) {System.out.println("log---环绕异常通知:"+signature.getName()+"出现异常");throw throwable;}finally {System.out.println("log---环绕返回通知:"+signature.getName()+"方法返回结果是:"+result);}return result;}
}
以上的例子就比较简单,就是在xml里面配置对应的aop切点、切面,接下来我们看下在spring AOP源码中是如何运行的。
二、Spring AOP 配置文件解析
在之前的spring 标签解析源码中我们有介绍到spring 配置文件解析是在refresh方法中,具体可见:Spring源码解析(15)之refresh源码分析(三)配置文件解析_refresh本地配置文件-CSDN博客文章浏览阅读476次。一、前言在前面的博客中我们有介绍到refresh方法中的otainFreshBeanFaction方法,这个方法的作用就是获取bean工厂,在这期间还会完成配置文件的加载,生成BeanDefinition,需要注意的是这里生成的BeanFactory默认的是DefaultListableBeanFactory,需要注意的是,我们自定的一些xsd约束文件也是在这里完成解析,通过实现BeanDefitionParser接口,并且实现parse方法,自定义的标签也通过实现NamespaceHandlerSup_refresh本地配置文件https://blog.csdn.net/jokeMqc/article/details/123029526 启动启动类代码:
public static void main(String[] args) throws NoSuchMethodException {AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();ac.register(SpringConfiguration.class);ac.refresh();MyCalculator bean = ac.getBean(MyCalculator.class);System.out.println(bean.add(1, 1));}
在AbstractApplicationContext.refresh中的obtainFreshBeanFactory解析对应xml文件文件。
@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing./*** 前戏,做容器刷新前的准备工作* 1、设置容器的启动时间* 2、设置活跃状态为true* 3、设置关闭状态为false* 4、获取Environment对象,并加载当前系统的属性值到Environment对象中* 5、准备监听器和事件的集合对象,默认为空的集合*/prepareRefresh();// Tell the subclass to refresh the internal bean factory.// 创建容器对象:DefaultListableBeanFactory// 加载xml配置文件的属性值到当前工厂中,最重要的就是BeanDefinitionConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.// beanFactory的准备工作,对各种属性进行填充prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.// 子类覆盖方法做额外的处理,此处我们自己一般不做任何扩展工作,但是可以查看web中的代码,是有具体实现的postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.// 调用各种beanFactory处理器invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.// 注册bean处理器,这里只是注册功能,真正调用的是getBean方法registerBeanPostProcessors(beanFactory);// Initialize message source for this context.// 为上下文初始化message源,即不同语言的消息体,国际化处理,在springmvc的时候通过国际化的代码重点讲initMessageSource();// Initialize event multicaster for this context.// 初始化事件监听多路广播器initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.// 留给子类来初始化其他的beanonRefresh();// Check for listener beans and register them.// 在所有注册的bean中查找listener bean,注册到消息广播器中registerListeners();// Instantiate all remaining (non-lazy-init) singletons.// 初始化剩下的单实例(非懒加载的)finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.// 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人finishRefresh();}catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.// 为防止bean资源占用,在异常处理中,销毁已经在前面过程中生成的单件beandestroyBeans();// Reset 'active' flag.// 重置active标志cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}}
进入到obtainFreshBeanFactory中的refreshBeanFactory()中的loadDefinitions中解析对应的配置文件。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {// 初始化BeanFactory,并进行XML文件读取,并将得到的BeanFactory记录在当前实体的属性中refreshBeanFactory();// 返回当前实体的beanFactory属性return getBeanFactory();}@Overrideprotected final void refreshBeanFactory() throws BeansException {// 如果存在beanFactory,则销毁beanFactoryif (hasBeanFactory()) {destroyBeans();closeBeanFactory();}try {// 创建DefaultListableBeanFactory对象DefaultListableBeanFactory beanFactory = createBeanFactory();// 为了序列化指定id,可以从id反序列化到beanFactory对象beanFactory.setSerializationId(getId());// 定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖customizeBeanFactory(beanFactory);// 初始化documentReader,并进行XML文件读取及解析,默认命名空间的解析,自定义标签的解析loadBeanDefinitions(beanFactory);this.beanFactory = beanFactory;}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}}
最后我们知道是在parseBeanDefinitions解析对应的标签,而parseDefaultElement解析的是bean,import、alias等默认标签,而我们的aop标签是在parseCustomerElement中解析,继续端点跟进去看下具体的解析过程。
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {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;if (delegate.isDefaultNamespace(ele)) {parseDefaultElement(ele, delegate);}else {delegate.parseCustomElement(ele);}}}}else {delegate.parseCustomElement(root);}}
@Nullablepublic BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {// 获取对应的命名空间String namespaceUri = getNamespaceURI(ele);if (namespaceUri == null) {return null;}// 根据命名空间找到对应的NamespaceHandlerspringNamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);if (handler == null) {error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);return null;}// 调用自定义的NamespaceHandler进行解析return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));}
获取对应命名空间然后获取对应handler去解析,就会进入到ConfigBeanDefinitionParser对象中。
这里就能够看得到对应的aop标签的解析,包括:pointcut、advisor、aspect标签的解析。
public BeanDefinition parse(Element element, ParserContext parserContext) {CompositeComponentDefinition compositeDef =new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));parserContext.pushContainingComponent(compositeDef);// 注册自动代理模式创建器,AspectjAwareAdvisorAutoProxyCreatorconfigureAutoProxyCreator(parserContext, element);// 解析aop:config子节点下的aop:pointcut/aop:advice/aop:aspectList<Element> 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;}
在解析对象这些标签之前首先先看下对应的configureAutoProxyCreator,主要是往IOC容器中注入切面主动代理类:AbstractAutoPorxyCreator对象,自动代码构造器对象。
private void configureAutoProxyCreator(ParserContext parserContext, Element element) {AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext, element);}public static void registerAspectJAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {// 注册名为org.springframework.aop.config.internalAutoProxyCreator的beanDefinition,其中的class类为`AspectJAwareAdvisorAutoProxyCreator`,其也会被注册到bean工厂中BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));// 如果指定proxy-target-class=true,则使用CGLIB代理,否则使用JDK代理// 其实其为AspectJAwareAdvisorAutoProxyCreator类的proxyTargetClass属性useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);// 注册到spring的bean工厂中,再次校验是否已注册registerComponentIfNecessary(beanDefinition, parserContext);}@Nullablepublic static BeanDefinition registerAspectJAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {return registerOrEscalateApcAsRequired(AspectJAwareAdvisorAutoProxyCreator.class, registry, source);}@Nullableprivate static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");// 如果已经存在了自动代理创建器且存在的自动代理创建器与现在不一致,那么需要根据优先级来判断到底需要使用哪个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) {// 改变bean所对应的className的属性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);registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);return beanDefinition;}
我们来看下他实际注入的这个AspectJAwareAdvisorAutoProxyCreator的继承关系,可以看得到实际上他是BPP,我们知道BPP他是针对bean的一个后置处理器,也就是在这个后置处理器中生成对应的代理对象,在后续会看得到。
我们继续往下看他配置文件解析,目前是解析AspectJ标签,所以会进入到parseAspect方法中。
/*** 解析切面* @param aspectElement* @param parserContext*/private void parseAspect(Element aspectElement, ParserContext parserContext) {// <aop:aspect> id属性String aspectId = aspectElement.getAttribute(ID);// aop ref属性,必须配置。代表切面String aspectName = aspectElement.getAttribute(REF);try {this.parseState.push(new AspectEntry(aspectId, aspectName));List<BeanDefinition> beanDefinitions = new ArrayList<>();List<BeanReference> beanReferences = new ArrayList<>();// 解析<aop:aspect>下的declare-parents节点// 采用的是DeclareParentsAdvisor作为beanClass加载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.// 解析其下的advice节点NodeList nodeList = aspectElement.getChildNodes();boolean adviceFoundAlready = false;for (int i = 0; i < nodeList.getLength(); i++) {Node node = nodeList.item(i);// 是否为advice:before/advice:after/advice:after-returning/advice:after-throwing/advice:around节点if (isAdviceNode(node, parserContext)) {// 校验aop:aspect必须有ref属性,否则无法对切入点进行观察操作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));}// 解析advice节点并注册到bean工厂中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);// 解析aop:point-cut节点并注册到bean工厂List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT);for (Element pointcutElement : pointcuts) {parsePointcut(pointcutElement, parserContext);}parserContext.popAndRegisterContainingComponent();}finally {this.parseState.pop();}}
这里会后去AspectJ下面的子标签然后去解析对应的Advice对象。
然后进入parseAdvice解析具体的advice对应,我们来看下advice方法的解析逻辑。
/*** 解析通知类并注册到bean工厂** Parses one of '{@code before}', '{@code after}', '{@code after-returning}',* '{@code after-throwing}' or '{@code around}' and registers the resulting* BeanDefinition with the supplied BeanDefinitionRegistry.* @return the generated advice RootBeanDefinition*/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)));// create the method factory bean// 解析advice节点中的"method"属性,并包装为MethodLocatingFactoryBean对象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// 关联aspectName,包装为SimpleBeanFactoryAwareAspectInstanceFactory对象RootBeanDefinition aspectFactoryDef =new RootBeanDefinition(SimpleBeanFactoryAwareAspectInstanceFactory.class);aspectFactoryDef.getPropertyValues().add("aspectBeanName", aspectName);aspectFactoryDef.setSynthetic(true);// register the pointcut// 涉及point-cut属性的解析,并结合上述的两个bean最终包装为AbstractAspectJAdvice通知对象AbstractBeanDefinition adviceDef = createAdviceDefinition(adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef,beanDefinitions, beanReferences);// configure the advisor// 最终包装为AspectJPointcutAdvisor对象RootBeanDefinition advisorDefinition = new RootBeanDefinition(AspectJPointcutAdvisor.class);advisorDefinition.setSource(parserContext.extractSource(adviceElement));advisorDefinition.getConstructorArgumentValues().addGenericArgumentValue(adviceDef);if (aspectElement.hasAttribute(ORDER_PROPERTY)) {advisorDefinition.getPropertyValues().add(ORDER_PROPERTY, aspectElement.getAttribute(ORDER_PROPERTY));}// register the final advisorparserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);return advisorDefinition;}finally {this.parseState.pop();}}
然后在createAdviceDefinition创建对应需要的advice对象,我们跟进去看具体的逻辑。
private AbstractBeanDefinition createAdviceDefinition(Element adviceElement, ParserContext parserContext, String aspectName, int order,RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef,List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) {// 首先根据adviceElement节点分析出是什么类型的AdviceRootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));adviceDefinition.setSource(parserContext.extractSource(adviceElement));// 设置aspectName属性和declarationOrder属性adviceDefinition.getPropertyValues().add(ASPECT_NAME_PROPERTY, aspectName);adviceDefinition.getPropertyValues().add(DECLARATION_ORDER_PROPERTY, order);// 解析节点是否含有`returning`/`throwing`/`arg-names`,有则设置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));}// 设置构造函数的入参变量// Method/AspectJExpressionPointcut/AspectInstanceFactory三个入参ConstructorArgumentValues cav = adviceDefinition.getConstructorArgumentValues();cav.addIndexedArgumentValue(METHOD_INDEX, methodDef);// 解析point-cut属性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;}
然后我们可以看得到对应的getAdviceClass,这里就是获取对应的代理切点的class,我们继续跟进去看下具体的获取逻辑。
/*** Gets the advice implementation class corresponding to the supplied {@link Element}.*/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 + "].");}}
其实我们可以看得到对应的advice对象的创建他需要三个参数,第一个是对应的method方法,也就是MethodLocatingFactoryBean,第二就是对应的Pointcut对象也就是对应的AspectJExpressionPointcut对象,第三个参数就是对应的BeanFactory对象,也就是SimpleBeanFactoryAwareAspectInstanceFactory对象。
此时Advice对象的Definition对象已经创建好了,然后最终包装为AspectJPointcutAdvisor对象,所以解析AOP的xml文件提前生成的BeanDefinition对象如下图:
以上是AOP需要提前准备的BeanDefinition,有了这些BeanDefinition之后,那么他是在哪里开始创建这些Bean对象,后续我们会继续分析,这里只分析AOP的xml配置文件解析。