Spring aop 源码解读

55 篇文章 0 订阅

1.今天没事学习一下springaop 工作的源码

在看之前我们一定要知道aop的工作原理及一些概念,可以参考网上查阅,在这里不过多介绍。

首先我们要了解,spring支持2种方式,一种xml配置,一种注解支持,这两种个人觉得都可以,主要看公司有没有规范要求,有些说注解比配置好,个人觉得不是很赞同,不过现在注解方式以一种趋势。言归正传:

首先我们配置aop的相关配置,如下:

<?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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    <bean id="BeanQ" class="com.study.cai.spring.sample.aop.BeanQ" />
    
    <bean id="myBeforeAdvice" class="com.study.cai.spring.sample.aop.MyBeforeAdvice" />
    <bean id="yyArroundAdvice" class="com.study.cai.spring.sample.aop.MyArroundAdvice" />
    
    <aop:config >
        <aop:pointcut id="doMethods" expression="execution(* com.study.cai.spring.sample.aop.*.do*(..))" />
        <aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="doMethods" />
        
        <aop:advisor advice-ref="yyArroundAdvice"
            pointcut="execution(* com.study.cai.spring.sample.aop.*.service*(..))"/>
    </aop:config>
    
    <!-- 配置了包含advice方法的Bean -->
    <bean id="aspectAdviceBean" class="com.study.cai.spring.sample.aop.AspectAdviceBean" />
    
    <aop:config proxy-target-class="true">
        <aop:pointcut id="services" expression="execution(* com.study.cai.spring.sample.aop.*.service*(..))" />
        <aop:aspect id="a1" ref="aspectAdviceBean" order="1">
            <aop:before method="before1" pointcut-ref="doMethods" />
            <aop:before method="before2" pointcut-ref="doMethods"/>
            <aop:before method="before3" pointcut="execution(* com.study.cai.spring.sample.aop.*.do*(..)) and args(tk,..)" arg-names=""/>
            <aop:before method="before4" pointcut="execution(* com.study.cai.spring.sample.aop.*.do*(..)) and args(tk,ti)"/>
            <aop:around method="arround1" pointcut-ref="services"/>
            <aop:around method="arround2" pointcut="execution(* com.study.cai.spring.sample.aop.*.service*(..)) and args(name)"/>
            <aop:after-returning method="afterReturning" pointcut-ref="services" returning="retValue"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="services" throwing="e"/>
            <aop:after method="after" pointcut-ref="services"/>
        </aop:aspect>
    </aop:config>
    
    
    <bean id="aspectAdviceBeanUseAnnotation" class="com.study.cai.spring.sample.aop.AspectAdviceBeanUseAnnotation" />
    
    <aop:aspectj-autoproxy>
        <aop:include name=""/>
    </aop:aspectj-autoproxy>
    
</beans>
    
    
    下面讲述下,xml配置这种aop配置的处理方式,首先我们要知道就是xml配置方式的这种工作原理。首选在我们容器启动时,容器会初始化我们的配置文件,那么xml配置文件头文件就很重要了,根据我们引入头文件uri找到我们的对应的handler,通过handler类处理器进行注册我们的解析类,比如我们的AOP相关解析。首先我们找到aop jar包,在meta-inf

打开,找到我们对应的aop处理类,其他配置也是如此.打开spring.handler

这里有个扩展点,如果我们需要扩展自己的一些类注册,可以像spring一样,编写我们的handler,和schema(配置支撑),然后实现NamespaceHandler接口,实现init方法,比如aop处理类:

此时我们查看xml中配置的aop:config 元素,对应我们config处理类,打开这个解析类,

首先我们要明白,此解析类对应我们xml aop的配置,只不过用一个javabean表达了出来,进入此类看到解析我们元素的方法,parse,点进去我们的autocreater方法,我们可以看到一个aopnamespacesutils类,这个类有2个属性,提一下,proxy-target-class属性对于我们产生代理实现有影响,如果为true,那么在创建代理对象时我们使用cglib方式,expose-proxy这个配置如果为true则会暴露到当前线程我们可以使用,这里不再过多讲解,

点击registerAutoProxyCreatorIfNecessary方法,可以看到如下:

此时我们可以看到,在注册解析我们的配置时其实有3个解析类存储在APC_PRIORITY_LIST集合中,我们进入注册符合并且需要aop增强的类方法中,

在这里我们需要知道,如果我们xml和注解方式都使用了,那么他首选会使用注解方式,这个方法选择优先级方法中我们可以知道,我们知道在次方中会传入这个类,这个很重要,我们看下他的结构

熟悉spring框架我们知道,beanpostprocessor和beanfatoryawar我们都很熟悉,这是在我们容器加载时可以改变我们的bean的一个扩展口。初始化我们bean定义之后我们我们可以看到,针对我们config子元素进行解析

这里可以找到相应的入口,具体怎么解析我们可以自己仔细去看代码,通过上面描述我们知道spring怎么将我们的配置信息转化为bean定义注册到容器中。

 

还记得上面所说的扩展点,我们可以看出实现了顶层beanpostprocesser接口,所以我们可以看到在我们bean初始化获得bean的方法

从这了我们可以得到代理bean,

我们从上面代码中可知,在获取代理类时,首先会从我们缓存中去获取如果缓存中存在我们需要的代理类,那么我们直接返回,如果没有往下走则判断此类是不是一个底层的基础类和该类本身,如果是直接返回u需要代理,如果不是则往下走,getAdvicesAndAdvisorsForBean是进行我们需要代理类的获取,具体获取代码可以跟进去自己看,总体来说就是方法上有切面相关注解的类进行获取或配置切面的类,得到这些需要增强代理的类我们此时创建代理的时机就条件就具备了,进入createProxy创建代理,

上述关键入口已经标明,具体代码逻辑我们可以自己去解读,在这里我只是简单说一下,首先创建一个代理工厂将我们获取到的类信息进行拷贝到我们的ProxyConfig,这个类我们可以获取代理的属性等信息(重要),然后判断我们是否强制对目标类进行代理,然后设置一些属性,在此后选择代理方式做准备,然后创建代理对象

关键来了,

我们创建代理对象到底使用jdk还是cglib呢在这里我们是不是一目了然了,大致逻辑是判断我们获取的目标类是否是类,如果获取类信息是为空则抛出异常,如果为接口则使用jdk否则使用cglib,如果获取到类信息不是类,则直接使用jdk动态代理。这里我们看下JdkDynamicAopProxy

 

它实现了InvocationHandler接口我们是不是很熟悉,在我们自己使用jdk代理时是不是也是实现此接口,在我们调用时执行invoke方法,

关键信息首先使用责任链获取我们增强的执行链,如果为空则直接通过反射进行我们增强,如果有责任链则执行我们方式进行我们目标类的增强。此时我们aop增强源码解读告一段落,有什么欠妥的地方还请广大的大牛指点!!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值