aop xml 配置方式解析过程

aop xml 配置方式解析过程

Author QiuRiMangCao 秋日芒草


基础的理解

切面,连接点,增强|通知,切点,引入,advisor 等基础的理解

  1. 切面:一个非业务的功能模块,属于一种增强功能实现,如在调用一个方法的时候增加调用的日志收集,事物管理等 - 有多个advice
    常用切面:日志管理,事务管理,异常管理
  2. 连接点:就是对应被代理的方法(before method after)Joinpoint
  3. 切点:是Joinpoint(连接点)的集合,是很多代理的方法 – pointcut
  4. 增强or通知:before = advice after = advice 都属于增强
    advisor:advice和pointcut的组合或封装,可以认为它是一个只有一个的advice的切面
  5. 引入:是一个类的扩展,可以认为这个被代理类中加入动态的方法,可以给一个类在运行的时候动态加入一个方法

源代码的理解

<aop:config>
    //这是一种切面配置,使用的比较多,txAdvice:事务通知
    <aop:advisor advice-ref="myInterceptor" pointcut="execution(public * com.dongnao.jack.service..*.*(..))" order="1"/>
</aop:config>

//如下是通过aop:advisor这种配置来配置事务管理的方式,比较常用,配置方法如下:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<tx:advice id="userTxAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="save*" propagation="REQUIRED" read-only="false"
            rollback-for="java.lang.RuntimeException" />
        <tx:method name="delete*" propagation="REQUIRED" read-only="false"
            rollback-for="java.lang.RuntimeException" />
        <tx:method name="update*" propagation="REQUIRED" read-only="false"
            rollback-for="java.lang.RuntimeException" />
        <tx:method name="check*" propagation="REQUIRED" read-only="false"
            rollback-for="java.lang.RuntimeException" />
        <tx:method name="print*" propagation="REQUIRED" read-only="false"
            rollback-for="java.lang.RuntimeException" />
        <tx:method name="*" propagation="REQUIRED" read-only="false" isolation="READ_COMMITTED"
            rollback-for="java.lang.RuntimeException" />
    </tx:attributes>
</tx:advice>
<aop:config>
    //pointcut(切点)是Joinpoint(连接点)的集合,expression代表能匹配的所有符合规则的切点
    //但它只是一个通知而不是真正的aop
    <aop:pointcut id="pc"
        expression="execution(public * com.consult.service.impl..*.*(..))" />
    //advisor:advice和pointcut的组合或封装,可以认为它是一个只有一个的advice的切面
    <aop:advisor pointcut-ref="pc" advice-ref="userTxAdvice" />
</aop:config>

//第二中配置aop的方法
<aop:config>
    <aop:aspect id="txAspect" ref="myaspectj" order="2">
        //types-matching:匹配目标对象
        //implement-interface:往目标对象中扩展接口实现类,相当于运行时扩展给MyServiceImpl多一个接口实现的方法
        //myintroduction:为实现扩展接口的实现类引用
        //这个属于“引入”:可以做到目标类的横向扩展
        <aop:declare-parents types-matching="com.dongnao.jack.service.MyServiceImpl"
            implement-interface="com.dongnao.jack.aop.IntroductionIntf" 
            delegate-ref="myintroduction"/>
        <aop:pointcut id="myMethod2"
            expression="execution(public * com.dongnao.jack.service..*.*(..))" />
        <aop:before method="before" pointcut="execution(public * com.dongnao.jack.service..*.*(..))"/>
        <aop:after method="after" pointcut-ref="myMethod2"/>
    </aop:aspect>
</aop:config>

//通过下面的测试代码就可以直接调用IntroductionIntf接口 的 实例化后的方法调用
//当一般都是在扩展一个类来处理,很少时候“aop的引入”来扩展方法
@Test
public void test2() {
    IntroductionIntf service = (IntroductionIntf)context.getBean("myservice");
    service.extend();
}

//那么我们从<aop:config>解析开始入手来学习aop的配置文件spring是如何去解析和使用的

//首先我们先从aop.jar包下META-INF-->spring.handlers入手
//code如下
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

//入口定位到AopNamespaceHandler这个类

//在xml解析的时候,会先去找这个元素对应的url,找实现类,再去调用AopNamespaceHandler的init方法
AopNamespaceHandler.init(){ //这个init方法在哪个时候调用的?
    //基于配置文件的配置
    registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
    //基于自动扫描的配置
    registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
}


//xml 解析成 element 对象后进行xml 中的一个bean配置成一个BeanDefinition对象
new ConfigBeanDefinitionParser().parse(Element element, ParserContext parserContext)

//localName就是</aop:config>的config,然后根据config去找对应的handler
String localName = parserContext.getDelegate().getLocalName(elt);

//注册AspectJAutoProxyCreatorIfNecessary,用的比较多
configureAutoProxyCreator(parserContext, element){
    //aop的入口类
    registerAspectJAutoProxyCreatorIfNecessary {
        registerOrEscalateApcAsRequired --- 》AspectJAwareAdvisorAutoProxyCreator  extends BeanPostProcessor顶层接口,才能作为入口类
    }
}

//在将AspectJAwareAdvisorAutoProxyCreator 注册为RootBeanDefinition的时候,将beanClass存入,这样就可以通过beanclass来new出实例
public RootBeanDefinition(Class<?> beanClass) {
    super();
    setBeanClass(beanClass);
}

//接着就把这个RootBeanDefinitionName 注册到beanDefinitionNames中
this.beanDefinitionMap.put(beanName, beanDefinition);

//sourceElement来判断是用怎么方式来动态代理的
//<aop:config proxy-target-class="true"> 使用cglib的方式来代理的
//<aop:config proxy-target-class="false"> 使用jdk的方式来代理的
useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement)

//分别解析xml中pointcut|aspect|advisor 子标签的解析和处理
List<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);
    }
}

//创建PointcutDefinition的解析类,AspectJExpressionPointcut继承Pointcut
protected AbstractBeanDefinition createPointcutDefinition(String expression) {
    RootBeanDefinition beanDefinition = new RootBeanDefinition(AspectJExpressionPointcut.class);
    beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
    beanDefinition.setSynthetic(true);
    beanDefinition.getPropertyValues().add(EXPRESSION, expression); //将配置文件中的正则存放到beanDefinition中的PropertyValues中
    return beanDefinition;
}

//aop:declare-parents 引入标签的解析,是一个list,所以可以支持多个引入标签
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));
}
//用于创建beanDefinition的
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(DeclareParentsAdvisor.class);

//解析aspectj 注册了DeclareParentsAdvisor


//解析切面中的通知
if (isAdviceNode(node, parserContext)) { 

//解析通知并注册到通知beanDefinitions,一个一个的解析
AbstractBeanDefinition advisorDefinition = parseAdvice(
            aspectName, i, aspectElement, (Element) node, parserContext, beanDefinitions, beanReferences);
    beanDefinitions.add(advisorDefinition);

//解析通知并注册
RootBeanDefinition methodDefinition = new RootBeanDefinition(MethodLocatingFactoryBean.class);
methodDefinition.getPropertyValues().add("targetBeanName", aspectName); //切面的名称
methodDefinition.getPropertyValues().add("methodName", adviceElement.getAttribute("method")); //method="before"
//xml
<aop:before method="before" pointcut="execution(public * com.dongnao.jack.service..*.*(..))"/>
<aop:after method="after" pointcut-ref="myMethod2"/>

//获取与提供的元素对应的通知实现类,并完成该类的注册
RootBeanDefinition adviceDefinition = new RootBeanDefinition(getAdviceClass(adviceElement, parserContext));
//xml
<aop:advisor advice-ref="myInterceptor" pointcut="execution(public * com.dongnao.jack.service..*.*(..))" order="1"/>

//获取与提供的标签元素对应的通知实现类,并完成实例化
getAdviceClass();

//接着又有一大堆标签的解析,注册,封装,然后整个xml标签的解析就完事了

// =======================》xml的解析注册成BeanDefinition end
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值