SpringAOP源码分析总结
2019-02-08 20:09 xiaok1024 阅读(353) 评论(0) 编辑 收藏 举报
祖先接口为org.springframework.aop.Advisor,应用中可直接使用org.springframework.aop.support.DefaultPointcutAdvisor
< aop:advisor>大多用于事务管理,定义通知器(通知器跟切面一样,也包括通知和切点)
<!-- 会重复读,不会脏读事务 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" timeout="120" propagation="REQUIRED" rollback-for="Exception" />
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
<aop:pointcut id="txPointCut" expression="..."/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
</aop:config>
!
< aop:aspect>大多用于日志,缓存**,自定义切面(切面包括通知和切点)
//aop配置
<bean id="sleepHelperAspect" class="com.ghs.aop.SleepHelperAspect"></bean>
<aop:config>
<aop:pointcut expression="execution(* *.sleep(..))" id="sleepPointcut"/>
<aop:aspect ref="sleepHelperAspect">
<!--前置通知-->
<aop:before method="beforeSleep" pointcut-ref="sleepPointcut"/>
<!--后置通知-->
<aop:after method="afterSleep" pointcut-ref="sleepPointcut"/>
</aop:aspect>
</aop:config>
<bean id="human" class="com.ghs.aop.Human"/>
小结:
不管是< aop:advisor>还是< aop:aspect>最终的实现逻辑是一样的。aop:advisor和< aop:aspect>其实都是将通知和切面进行了封装,原理基本上是一样的,只是使用的方式不同而已
1.通知(Advice) 就是你想要的功能,也就是上面说的 安全,事物,日志等。你给先定义好把,然后在想用的地方用一下。 2.连接点(JoinPoint) 这个更好解释了,就是spring允许你使用通知的地方,那可真就多了,基本每个方法的前,后(两者都有也行),或抛出异常时都可以是连接点,spring只支持方法连接点.其他如aspectJ还可以让你在构造器或属性注入时都行,不过那不是咱关注的,只要记住,和方法有关的前前后后(抛出异常),都是连接点。 3.切入点(Pointcut) 上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有几十个连接点了对把,但是你并不想在所有方法附近都使用通知(使用叫织入,以后再说),你只想让其中的几个,在调用这几个方法之前,之后或者抛出异常时干点什么,那么就用切点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。 4.切面(Aspect) 切面是通知和切入点的结合。现在发现了吧,没连接点什么事情,连接点就是为了让你好理解切点,搞出来的,明白这个概念就行了。通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义。 5.引入(introduction) 允许我们向现有的类添加新方法属性。这不就是把切面(也就是新方法属性:通知定义的)用到目标类中吗 6.目标(target) 引入中所提到的目标类,也就是要被通知的对象,也就是真正的业务逻辑,他可以在毫不知情的情况下,被咱们织入切面。而自己专注于业务本身的逻辑。 7.代理(proxy) 怎么实现整套aop机制的,都是通过代理,这个一会给细说。 8.织入(weaving) 把切面应用到目标对象来创建新的代理对象的过程。有3种方式,spring采用的是运行时,为什么是运行时,后面解释。 关键就是:切点定义了哪些连接点会得到通知
9.顾问(advisor)切面的另一种方式,一般用于自定义复杂的增强方法
4.我所理解的aop原理
spring用代理类包裹切面,把他们织入到Spring管理的bean中。也就是说代理类伪装成目标类,它会截取对目标类中方法的调用,让调用者对目标类的调用都先变成调用伪装类,伪装类中就先执行了切面,再把调用转发给真正的目标bean。 现在可以自己想一想,怎么搞出来这个伪装类,才不会被调用者发现(过JVM的检查,JAVA是强类型检查,哪里都要检查类型)。 1.实现和目标类相同的接口,我也实现和你一样的接口,反正上层都是接口级别的调用,这样我就伪装成了和目标类一样的类(实现了同一接口,咱是兄弟了),也就逃过了类型检查,到java运行期的时候,利用多态的后期绑定(所以spring采用运行时),伪装类(代理类)就变成了接口的真正实现,而他里面包裹了真实的那个目标类,最后实现具体功能的还是目标类,只不过伪装类在之前干了点事情(写日志,安全检查,事物等)。 这就好比,一个人让你办件事,每次这个时候,你弟弟就会先出来,当然他分不出来了,以为是你,你这个弟弟虽然办不了这事,但是他知道你能办,所以就答应下来了,并且收了点礼物(写日志),收完礼物了,给把事给人家办了啊,所以你弟弟又找你这个哥哥来了,最后把这是办了的还是你自己。但是你自己并不知道你弟弟已经收礼物了,你只是专心把这件事情做好。
顺着这个思路想,要是本身这个类就没实现一个接口呢,你怎么伪装我,我就压根没有机会让你搞出这个双胞胎的弟弟,那么就用第2种代理方式,创建一个目标类的子类,生个儿子,让儿子伪装我
2.生成子类调用,这次用子类来做为伪装类,当然这样也能逃过JVM的强类型检查,我继承的吗,当然查不出来了,子类重写了目标类的所有方法,当然在这些重写的方法中,不仅实现了目标类的功能,还在这些功能之前,实现了一些其他的(写日志,安全检查,事物等)。 这
AOP的实现原理分析。为了探究AOP实现原理,首先定义几个类,一个Dao接口:
public interface Dao {
public void select();
public void insert();
}
Dao接口的实现类DaoImpl:
public class DaoImpl implements Dao {
定义一个TimeHandler,用于方法调用前后打印时间,在AOP中,这扮演的是横切关注点的角色:
public class TimeHandler {
public void printTime() {
System.out.println("CurrentTime:" + System.currentTimeMillis());
}
}
定义一个XML文件aop.xml:
写一段测试代码TestAop.java:
public class TestAop {
代码运行结果就不看了,有了以上的内容,我们就可以根据这些跟一下代码,看看Spring到底是如何实现AOP的。
AOP实现原理——找到Spring处理AOP的源头
有很多朋友不愿意去看AOP源码的一个很大原因是因为找不到AOP源码实现的入口在哪里,这个确实是。不过我们可以看一下上面的测试代码,就普通Bean也好、AOP也好,最终都是通过getBean方法获取到Bean并调用方法的**,getBean之后的对象已经前后都打印了TimeHandler类printTime()方法里面的内容,可以想见它们已经是被Spring容器处理过了。
既然如此,那无非就两个地方处理:
-
加载Bean定义(BeanDefinition)的时候应该有过特殊的处理
-
getBean的时候应该有过特殊的处理
因此,本文围绕【1.加载Bean定义的时候应该有过特殊的处理】展开,先找一下到底是哪里Spring对AOP做了特殊的处理。代码直接定位到DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法:
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)) {
//进行解析标准的NameSpace
9 parseDefaultElement(ele, delegate);
}
else {
//进行解析AOP标签的方法(不仅仅是解析Aop)
12 delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
正常来说,遇到<bean id="daoImpl”…>、<bean id="timeHandler”…>这两个标签的时候,都会执行第9行的代码,因为<bean>标签是默认的Namespace。但是在遇到后面的aop:config标签的时候就不一样了,aop:config并不是默认的Namespace,因此会执行第12行的代码,看一下:
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { //获取nameSpaceURI,Namespace=”http://www.springframework.org/schema/aop String namespaceUri = getNamespaceURI(ele); //根据URI获取相对应的处理器 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)); }
因为之前把整个XML解析成了org.w3c.dom.Document,org.w3c.dom.Document以树的形式表示整个XML,具体到每一个节点就是一个Node。
首先第2行从aop:config这个Node(参数Element是Node接口的子接口)中拿到Namespace=”http://www.springframework.org/schema/aop“,第3行的代码根据这个Namespace获取对应的NamespaceHandler即Namespace处理器,具体到aop这个Namespace的NamespaceHandler是org.springframework.aop.config.AopNamespaceHandler类,也就是第3行代码获取到的结果。具体到AopNamespaceHandler里面,有几个Parser,是用于具体标签转换的,分别为:
-
config–>ConfigBeanDefinitionParser
-
aspectj-autoproxy–>AspectJAutoProxyBeanDefinitionParser
-
scoped-proxy–>ScopedProxyBeanDefinitionDecorator
-
spring-configured–>SpringConfiguredBeanDefinitionParser
接着,就是第8行的代码,利用AopNamespaceHandler的parse方法,解析aop:config下的内容了。
AOP Bean定义加载——根据织入方式将aop:before、aop:after转换成名为adviceDef的RootBeanDefinition
上面经过分析,已经找到了Spring是通过AopNamespaceHandler处理的AOP,那么接着进入AopNamespaceHandler的parse方法源代码:
public BeanDefinition parse(Element element, ParserContext parserContext) { //1.findParserForElement(element, parserContext)获取到的是ConfigBeanDefinitionParser //2.调用ConfigBeanDefinitionParser.parse(element, parserContext) return findParserForElement(element, parserContext).parse(element, parserContext); }
首先获取具体的Parser,因为当前节点是aop:config,上一部分最后有列,config是通过ConfigBeanDefinitionParser来处理的,因此findParserForElement(element, parserContext)这一部分代码获取到的是ConfigBeanDefinitionParser,接着看ConfigBeanDefinitionParser的parse方法:
public BeanDefinition parse(Element element, ParserContext parserContext) { CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element)); parserContext.pushContainingComponent(compositeDef); 6 configureAutoProxyCreator(parserContext, element); 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)) { 18 parseAspect(elt, parserContext); } } parserContext.popAndRegisterContainingComponent(); return null; }
重点先提一下第6行的代码,该行代码的具体实现不跟了但它非常重要,configureAutoProxyCreator方法的作用用几句话说一下:
-
向Spring容器注册了一个BeanName为org.springframework.aop.config.internalAutoProxyCreator的BeanDefinition,可以自定义也可以使用Spring提供的(根据优先级来)
-
Spring默认提供的是org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator,这个类是AOP的核心类,留在下篇讲解
-
在这个方法里面也会根据配置proxy-target-class(true->cglib)和expose-proxy,设置是否使用CGLIB进行代理以及是否暴露最终的代理。
aop:config下的节点为aop:aspect,想见必然是执行第18行的代码parseAspect,跟进去:
private void parseAspect(Element aspectElement, ParserContext parserContext) { String aspectId = aspectElement.getAttribute(ID); String aspectName = aspectElement.getAttribute(REF); try { this.parseState.push(new AspectEntry(aspectId, aspectName)); List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>(); List<BeanReference> beanReferences = new ArrayList<BeanReference>(); 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. NodeList nodeList = aspectElement.getChildNodes(); boolean adviceFoundAlready = false; for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); //用来判断是否为<aop:aspect>下的五个通知 如<aop:before>... 22 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)); } //解析标签 33 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(); } }
从第20行~第37行的循环开始关注这个方法。这个for循环有一个关键的判断就是第22行的ifAdviceNode判断,看下ifAdviceNode方法做了什么:
private boolean isAdviceNode(Node aNode, ParserContext parserContext) { if (!(aNode instanceof Element)) { return false; } else { String name = parserContext.getDelegate().getLocalName(aNode); return (BEFORE.equals(name) || AFTER.equals(name) || AFTER_RETURNING_ELEMENT.equals(name) || AFTER_THROWING_ELEMENT.equals(name) || AROUND.equals(name)); } }
即这个for循环只用来处理aop:aspect标签下的aop:before、aop:after、aop:after-returning、<aop:after-throwing method=”">、<aop:around method=”">这五个标签的。
接着,如果是上述五种标签之一,那么进入第33行~第34行的parseAdvice方法:
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 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); // register the pointcut //创建RootBeanDefinition,名为adviceDef即advice定义 AbstractBeanDefinition adviceDef = createAdviceDefinition( adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef, beanDefinitions, beanReferences); // configure the advisor //将创建好的RootBeanDefinition写入一个新的RootBeanDefinition,构造一个新的对象,名为 advisorDefinition 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 advisor //将advisorDefinition注册到DefaultListableBeanFactory中 parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition); return advisorDefinition; } finally { this.parseState.pop(); } }
方法主要做了三件事:
-
根据织入方式(before、after这些)创建RootBeanDefinition,名为adviceDef即advice定义
-
将上一步创建的RootBeanDefinition写入一个新的RootBeanDefinition,构造一个新的对象,名为advisorDefinition,即advisor定义(配置advisor)
-
将advisorDefinition注册到DefaultListableBeanFactory中
下面来看做的第一件事createAdviceDefinition方法定义:
private AbstractBeanDefinition createAdviceDefinition( Element adviceElement, ParserContext parserContext, String aspectName, int order, RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef, List<BeanDefinition> beanDefinitions, List<BeanReference> beanReferences) { //getAdviceClass()根据不同的名字获取不同的 adviceDefinition->AspectJMethodBeforeAdvice.class 6 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); 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; }
首先可以看到,创建的AbstractBeanDefinition实例是RootBeanDefinition,这和普通Bean创建的实例为GenericBeanDefinition不同。然后进入第6行的getAdviceClass方法看一下:
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 + "]."); } }
既然创建BeanDefinition,必然该BeanDefinition中要对应一个具体的Class,不同的切入方式对应不同的Class:
-
before对应AspectJMethodBeforeAdvice
-
After对应AspectJAfterAdvice
-
after-returning对应AspectJAfterReturningAdvice
-
after-throwing对应AspectJAfterThrowingAdvice
-
around对应AspectJAroundAdvice
createAdviceDefinition方法剩余逻辑没什么,就是判断一下标签里面的属性并设置一下相应的值而已,至此aop:before、aop:after两个标签对应的AbstractBeanDefinition就创建出来了。
AOP Bean定义加载——将名为adviceDef的RootBeanDefinition转换成名为advisorDefinition的RootBeanDefinition
下面我们看一下第二步的操作,将名为adviceDef的RootBeanDefinition转换成名为advisorDefinition的RootBeanDefinition,跟一下上面一部分ConfigBeanDefinitionParser类parseAdvice方法的第26行~32行的代码:
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)); }
这里相当于将上一步生成的RootBeanDefinition包装了一下,new一个新的RootBeanDefinition出来,Class类型是org.springframework.aop.aspectj.AspectJPointcutAdvisor。
第4行~第7行的代码是用于判断aop:aspect标签中有没有”order”属性的,有就设置一下,”order”属性是用来控制切入方法优先级的。
AOP Bean定义加载——将BeanDefinition注册到DefaultListableBeanFactory中
最后一步就是将BeanDefinition注册到DefaultListableBeanFactory中了,代码就是前面ConfigBeanDefinitionParser的parseAdvice方法的最后一部分了:
... // register the final advisor parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition); ...
跟一下registerWithGeneratedName方法的实现:
public String registerWithGeneratedName(BeanDefinition beanDefinition) { String generatedName = generateBeanName(beanDefinition); getRegistry().registerBeanDefinition(generatedName, beanDefinition); return generatedName; }
第2行获取注册的名字BeanName,和<bean>的注册差不多,使用的是Class全路径+”#”+全局计数器的方式,其中的Class全路径为org.springframework.aop.aspectj.AspectJPointcutAdvisor,依次类推,每一个BeanName应当为org.springframework.aop.aspectj.AspectJPointcutAdvisor#0、org.springframework.aop.aspectj.AspectJPointcutAdvisor#1、org.springframework.aop.aspectj.AspectJPointcutAdvisor#2这样下去。
第3行向DefaultListableBeanFactory中注册,BeanName已经有了,剩下的就是BeanDefinition,BeanDefinition的解析流程之前已经看过了,就不说了。
AOP Bean定义加载——AopNamespaceHandler处理aop:pointcut流程
回到ConfigBeanDefinitionParser的parseAspect方法:
private void parseAspect(Element aspectElement, ParserContext parserContext) { //解析<aop:before>等五个标签的属性 ... -------------------------- AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition( aspectElement, aspectId, beanDefinitions, beanReferences, parserContext); parserContext.pushContainingComponent(aspectComponentDefinition); List<Element> pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT); for (Element pointcutElement : pointcuts) { //遍历解析pointcut parsePointcut(pointcutElement, parserContext); } parserContext.popAndRegisterContainingComponent(); } finally { this.parseState.pop(); } }
省略号部分表示是解析的是aop:before、aop:after这种标签,上部分已经说过了,就不说了,下面看一下解析aop:pointcut部分的源码。
第5行~第7行的代码构建了一个Aspect标签组件定义,并将Apsect标签组件定义推到ParseContext即解析工具上下文中,这部分代码不是关键。
第9行的代码拿到所有aop:aspect下的pointcut标签,进行遍历,由parsePointcut方法进行处理:
private AbstractBeanDefinition parsePointcut(Element pointcutElement, ParserContext parserContext) { //1.获取<aop:pointcut>标签下的”id”属性与”expression”属性。 String id = pointcutElement.getAttribute(ID); String expression = pointcutElement.getAttribute(EXPRESSION); AbstractBeanDefinition pointcutDefinition = null; try { //发布解析状态,表示当前Spring上下文正在解析Pointcut标签 this.parseState.push(new PointcutEntry(id)); //创建Pointcut的BeanDefinition pointcutDefinition = createPointcutDefinition(expression); pointcutDefinition.setSource(parserContext.extractSource(pointcutElement)); String pointcutBeanName = id; //注册获取到的BeanDefinition //判断是否配置了id if (StringUtils.hasText(pointcutBeanName)) { //**pointcutBeanName=id** parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition); } else { //**pointcutBeanName=org.springframework.aop.aspectj.AspectJExpressionPointcut#序号(从0开始累加)** pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition); } parserContext.registerComponent( new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression)); } finally { this.parseState.pop(); } return pointcutDefinition; }
步骤:
第2行~第3行的代码1.获取aop:pointcut标签下的”id”属性与”expression”属性。
第8行的代码推送一个PointcutEntry,表示当前Spring上下文正在解析Pointcut标签。
第9行的代码创建Pointcut的BeanDefinition,之后再看,先把其他方法都看一下。
第10行的代码不管它,最终从NullSourceExtractor的extractSource方法获取Source,就是个null。
第12行~第18行的代码用于注册获取到的BeanDefinition,默认pointcutBeanName为aop:pointcut标签中定义的id属性:
-
如果aop:pointcut标签中配置了id属性就执行的是第13行~第15行的代码,pointcutBeanName=id
-
如果aop:pointcut标签中没有配置id属性就执行的是第16行~第18行的代码,和Bean不配置id属性一样的规则,pointcutBeanName=org.springframework.aop.aspectj.AspectJExpressionPointcut#序号(从0开始累加)
第20行~第21行的代码向解析工具上下文中注册一个Pointcut组件定义
第23行~第25行的代码,finally块在aop:pointcut标签解析完毕后,让之前推送至栈顶的PointcutEntry出栈,表示此次aop:pointcut标签解析完毕。
最后回头来一下第9行代码createPointcutDefinition的实现,比较简单:
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; }
关键就是注意一下两点:
-
aop:pointcut标签对应解析出来的BeanDefinition是RootBeanDefinition,且RootBenaDefinitoin中的Class是org.springframework.aop.aspectj.AspectJExpressionPointcut
-
aop:pointcut标签对应的Bean是prototype即原型的
这样一个流程下来,就解析了aop:pointcut标签中的内容并将之转换为RootBeanDefintion存储在Spring容器中。
AspectJAwareAdvisorAutoProxyCreator及为Bean生成代理时机分析
上篇文章说了,org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator这个类是Spring提供给开发者的AOP的核心类,就是AspectJAwareAdvisorAutoProxyCreator完成了【类/接口–>代理】的转换过程,首先我们看一下AspectJAwareAdvisorAutoProxyCreator的层次结构:
这里最值得注意的一点是最左下角的那个方框,我用几句话总结一下:
-
AspectJAwareAdvisorAutoProxyCreator是BeanPostProcessor接口的实现类
-
postProcessBeforeInitialization方法与postProcessAfterInitialization方法实现在父类AbstractAutoProxyCreator中
-
postProcessBeforeInitialization方法是一个空实现
-
逻辑代码在postProcessAfterInitialization方法中
基于以上的分析,将Bean生成代理的时机已经一目了然了:在每个Bean初始化之后,如果需要,调用AspectJAwareAdvisorAutoProxyCreator中的postProcessBeforeInitialization为Bean生成代理。
代理对象实例化—-判断是否为<bean>生成代理
上文分析了Bean生成代理的时机是在每个Bean初始化之后,下面把代码定位到Bean初始化之后,先是AbstractAutowireCapableBeanFactory的initializeBean方法进行初始化:
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { invokeAwareMethods(beanName, bean); return null; } }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { //初始化之前 wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { //初始化之后 = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
初始化之前是第16行的applyBeanPostProcessorsBeforeInitialization方法(空方法),初始化之后即29行的applyBeanPostProcessorsAfterInitialization方法:
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) { result = beanProcessor.postProcessAfterInitialization(result, beanName); if (result == null) { return result; } } return result; }
这里调用每个BeanPostProcessor的postProcessAfterInitialization方法。按照之前的分析,看一下AbstractAutoProxyCreator的postProcessAfterInitialization方法实现:
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
跟一下第5行的方法wrapIfNecessary:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (this.targetSourcedBeans.contains(beanName)) { return bean; } if (this.nonAdvisedBeans.contains(cacheKey)) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.nonAdvisedBeans.add(cacheKey); return bean; } // Create proxy if we have advice. //用于判断Bean是不是需要生成代理,返回的是advisor[] 14 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.add(cacheKey); Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.nonAdvisedBeans.add(cacheKey); return bean; }
第2行~第11行是一些不需要生成代理的场景判断,这里略过。首先我们要思考的第一个问题是:哪些目标对象需要生成代理?因为配置文件里面有很多Bean,肯定不能对每个Bean都生成代理,因此需要一套规则判断Bean是不是需要生成代理,这套规则就是第14行的代码getAdvicesAndAdvisorsForBean:
protected Object[] getAdvicesAndAdvisorsForBean( Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); } ---------------------------------------------------------------------- protected List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) { //寻找合适(所有)的Advisor 2 List<Advisor> candidateAdvisors = findCandidateAdvisors(); //根据候选Advisors,寻找可以使用的Advisor 3 List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); //向候选Advisor链的开头,添加一个DefaultPointcutAdvisor 4 extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }
顾名思义,方法的意思是为指定class寻找合适的Advisor。
第2行代码,寻找候选Advisors,根据上文的配置文件,有两个候选Advisor,分别是aop:aspect节点下的aop:before和aop:after这两个,这两个在XML解析的时候已经被转换生成了RootBeanDefinition。
跳过第3行的代码,先看下第4行的代码extendAdvisors方法,之后再重点看一下第3行的代码。第4行的代码extendAdvisors方法作用是向候选Advisor链的开头(也就是List.get(0)的位置),添加一个org.springframework.aop.support.DefaultPointcutAdvisor。
第3行代码,根据候选Advisors,寻找可以使用的Advisor,跟一下方法实现:
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) { if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } List<Advisor> eligibleAdvisors = new LinkedList<Advisor>(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } } boolean hasIntroductions = !eligibleAdvisors.isEmpty(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor) { // already processed continue; } if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors; }
整个方法的主要判断都围绕canApply展开方法:
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) { if (advisor instanceof IntroductionAdvisor) { return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); } else if (advisor instanceof PointcutAdvisor) { PointcutAdvisor pca = (PointcutAdvisor) advisor; 7 return canApply(pca.getPointcut(), targetClass, hasIntroductions); } else { // It doesn't have a pointcut so we assume it applies. return true; } }
第一个参数advisor的实际类型是AspectJPointcutAdvisor,它是PointcutAdvisor的子类,因此执行第7行的方法:
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) { if (!pc.getClassFilter().matches(targetClass)) { return false; } MethodMatcher methodMatcher = pc.getMethodMatcher(); IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null; if (methodMatcher instanceof IntroductionAwareMethodMatcher) { introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; } Set<Class> classes = new HashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); classes.add(targetClass); for (Class<?> clazz : classes) { Method[] methods = clazz.getMethods(); for (Method method : methods) { if ((introductionAwareMethodMatcher != null && introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) || methodMatcher.matches(method, targetClass)) { return true; } } } return false; }
这个方法其实就是拿当前Advisor对应的expression做了两层判断:
-
目标类必须满足expression的匹配规则
-
目标类中的方法必须满足expression的匹配规则,当然这里方法不是全部需要满足expression的匹配规则,有一个方法满足即可
如果以上两条都满足,那么容器则会判断该<bean>满足条件,需要被生成代理对象,具体方式为返回一个数组对象,该数组对象中存储的是<bean>对应的Advisor。
代理对象实例化—-为<bean>生成代理代码上下文梳理
上文分析了为<bean>生成代理的条件,现在就正式看一下Spring上下文是如何为<bean>生成代理的。回到AbstractAutoProxyCreator的wrapIfNecessary方法:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (this.targetSourcedBeans.contains(beanName)) { return bean; } if (this.nonAdvisedBeans.contains(cacheKey)) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.nonAdvisedBeans.add(cacheKey); return bean; } // Create proxy if we have advice. Advisor数组 14 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.add(cacheKey); //创建代理对象 17 Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.nonAdvisedBeans.add(cacheKey); return bean; }
第14行拿到<bean>对应的Advisor数组,第15行判断只要Advisor数组不为空,那么就会通过第17行的代码为<bean>创建代理:
protected Object createProxy( Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) { //创建ProxyFactory并进行设置 4 ProxyFactory proxyFactory = new ProxyFactory(); // Copy our properties (proxyTargetClass etc) inherited from ProxyConfig. proxyFactory.copyFrom(this); //进行判断是否为CGLIB代理,false->进入 8 if (!shouldProxyTargetClass(beanClass, beanName)) { // Must allow for introductions; can't just set interfaces to // the target's interfaces only. //获取所有的实现的接口 Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader); for (Class<?> targetInterface : targetInterfaces) { //代理工厂添加这些接口 proxyFactory.addInterface(targetInterface); } } //为proxyFactory添加一些参数 Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); for (Advisor advisor : advisors) { proxyFactory.addAdvisor(advisor); } proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); } 30 return proxyFactory.getProxy(this.proxyClassLoader); }
第4行~第6行new出了一个ProxyFactory,Proxy,顾名思义,代理工厂的意思,提供了简单的方式使用代码获取和配置AOP代理。
第8行的代码做了一个判断,判断的内容是aop:config这个节点中proxy-target-class=”false”或者proxy-target-class不配置,即不使用CGLIB生成代理。如果满足条件,进判断,获取当前Bean实现的所有接口,将这些接口Class对象都添加到ProxyFactory中。
第17行~第28行的代码没什么看的必要,向ProxyFactory中添加一些参数而已。重点看第30行proxyFactory.getProxy(this.proxyClassLoader)这句:
public Object getProxy(ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); }
实现代码就一行,但是却明确告诉我们做了两件事情:
-
创建AopProxy接口实现类(如:JdkDynamicAopProxy)
-
通过AopProxy接口的实现类的getProxy方法获取<bean>对应的代理(JdkDynamicAopProxy.newProxyInstance())
就从这两个点出发,分两部分分析一下。
代理对象实例化—-创建AopProxy接口实现类
看一下createAopProxy()方法的实现,它位于DefaultAopProxyFactory类中:
protected final synchronized AopProxy createAopProxy() { if (!this.active) { activate(); } return getAopProxyFactory().createAopProxy(this); }
前面的部分没什么必要看,直接进入重点即createAopProxy方法:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { //true让Spring自己去优化而不是用户指定||false->jdk ||判断bean实现了接口? if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } if (targetClass.isInterface()) { return new JdkDynamicAopProxy(config); } if (!cglibAvailable) { throw new AopConfigException( "Cannot proxy target class because CGLIB2 is not available. " + "Add CGLIB to the class path or specify proxy interfaces."); } return CglibProxyFactory.createCglibProxy(config); } else { return new JdkDynamicAopProxy(config); } }
平时我们说AOP原理三句话就能概括:
-
对类生成代理使用CGLIB
-
对接口生成代理使用JDK原生的Proxy
-
可以通过配置文件指定对接口使用CGLIB生成代理
这三句话的出处就是createAopProxy方法。看到默认是第19行的代码使用JDK自带的Proxy生成代理,碰到以下三种情况例外:
-
ProxyConfig的isOptimize方法为true,这表示让Spring自己去优化而不是用户指定
-
ProxyConfig的isProxyTargetClass方法为true,这表示配置了proxy-target-class=”true”
-
ProxyConfig满足hasNoUserSuppliedProxyInterfaces方法执行结果为true,这表示<bean>对象没有实现任何接口或者实现的接口是SpringProxy接口
在进入第2行的if判断之后再根据目标<bean>的类型决定返回哪种AopProxy。简单总结起来就是:
-
proxy-target-class没有配置或者proxy-target-class=”false”,返回JdkDynamicAopProxy
-
proxy-target-class=”true”或者<bean>对象没有实现任何接口或者只实现了SpringProxy接口,返回Cglib2AopProxy
当然,不管是JdkDynamicAopProxy还是Cglib2AopProxy,AdvisedSupport都是作为构造函数参数传入的,里面存储了具体的Advisor。
代理对象实例化—-通过getProxy方法获取<bean>对应的代理
其实代码已经分析到了JdkDynamicAopProxy和Cglib2AopProxy,剩下的就没什么好讲的了,无非就是看对这两种方式生成代理的熟悉程度而已。
Cglib2AopProxy生成代理的代码就不看了,对Cglib不熟悉的朋友可以看Cglib及其基本使用一文。
JdkDynamicAopProxy生成代理的方式稍微看一下:
public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); } //获取所有要代理的接口 5 Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised); //寻找这些接口方法里面有没有重写equals方法和hashCode方法,同时都有的话打个标记,寻找结束(这些方法是不 进行代理增强的) 6 findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); //直接调用原生的方法生成代理对象 return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }
这边解释一下第5行和第6行的代码,第5行代码的作用是拿到所有要代理的接口,第6行代码的作用是尝试寻找这些接口方法里面有没有equals方法和hashCode方法,同时都有的话打个标记,寻找结束,equals方法和hashCode方法有特殊处理。
最终通过第7行的Proxy.newProxyInstance方法获取接口/类对应的代理对象,Proxy是JDK原生支持的生成代理的方式。
代理方法调用原理
前面已经详细分析了为接口/类生成代理的原理,生成代理之后就要调用方法了,这里看一下使用JdkDynamicAopProxy调用方法的原理。
由于JdkDynamicAopProxy本身实现了InvocationHandler接口,因此具体代理前后处理的逻辑在invoke方法中:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodInvocation invocation; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Class targetClass = null; Object target = null; try { //equals方法与hashCode方法即使满足expression规则,也不会为之产生代理内容,调用的是JdkDynamicAopProxy的equals方法与hashCode方法 11 if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); 18 } //方法所属的Class是一个接口并且方法所属的Class是AdvisedSupport的父类或者父接口,直接通过反射调用该方法 19 if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); 23 } Object retVal; //是用于判断是否将代理暴露出去的,由<aop:config>标签中的expose-proxy=”true/false”配置。 27 if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; 30 } // May be null. Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. target = targetSource.getTarget(); if (target != null) { targetClass = target.getClass(); } //获取这个方法的拦截链 // Get the interception chain for this method. List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); //AdvisedSupport中的所有拦截器和动态拦截器列表 41 // Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. //为空,直接通过反射调用该方法,而不进行任何AOP增强 if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args); } else { //将拦截链进行唤醒,调用对原方法进行拦截, // We need to create a method invocation... invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); } // Massage return value if necessary. if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // Special case: it returned "this" and the return type of the method // is type-compatible. Note that we can't help if the target sets // a reference to itself in another returned object. retVal = proxy; } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }
第11行~第18行的代码,表示equals方法与hashCode方法即使满足expression规则,也不会为之产生代理内容,调用的是JdkDynamicAopProxy的equals方法与hashCode方法。至于这两个方法是什么作用,可以自己查看一下源代码。
第19行~第23行的代码,表示方法所属的Class是一个接口并且方法所属的Class是AdvisedSupport的父类或者父接口,直接通过反射调用该方法。
第27行~第30行的代码,是用于判断是否将代理暴露出去的,由aop:config标签中的expose-proxy=”true/false”配置。
第41行的代码,获取AdvisedSupport中的所有拦截器和动态拦截器列表,用于拦截方法,具体到我们的实际代码,列表中有三个Object,分别是:
-
chain.get(0):ExposeInvocationInterceptor,这是一个默认的拦截器,对应的原Advisor为DefaultPointcutAdvisor
-
chain.get(1):MethodBeforeAdviceInterceptor,用于在实际方法调用之前的拦截,对应的原Advisor为AspectJMethodBeforeAdvice
-
chain.get(2):AspectJAfterAdvice,用于在实际方法调用之后的处理
第45行~第50行的代码,如果拦截器列表为空,很正常,因为某个类/接口下的某个方法可能不满足expression的匹配规则,因此此时通过反射直接调用该方法。
第51行~第56行的代码,如果拦截器列表不为空,按照注释的意思,需要一个ReflectiveMethodInvocation,并通过proceed方法对原方法进行拦截,proceed方法感兴趣的朋友可以去看一下,里面使用到了递归的思想对chain中的Object进行了层层的调用。
下面我们来看一下CGLIB代理的方式,这里需要读者去了解一下CGLIB以及其创建代理的方式:
CglibAopProxy
public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource()); } try { Class<?> rootClass = this.advised.getTargetClass(); Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy"); Class<?> proxySuperClass = rootClass; if (ClassUtils.isCglibProxyClass(rootClass)) { proxySuperClass = rootClass.getSuperclass(); Class<?>[] additionalInterfaces = rootClass.getInterfaces(); for (Class<?> additionalInterface : additionalInterfaces) { this.advised.addInterface(additionalInterface); } } // Validate the class, writing log messages as necessary. validateClassIfNecessary(proxySuperClass, classLoader); // Configure CGLIB Enhancer... Enhancer enhancer = createEnhancer(); if (classLoader != null) { enhancer.setClassLoader(classLoader); if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { enhancer.setUseCache(false); } } enhancer.setSuperclass(proxySuperClass); enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader)); //获取拦截器链 Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types); //创建代理 return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException | IllegalArgumentException ex) { throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed throw new AopConfigException("Unexpected AOP exception", ex); } }
getCallbacks()
private Callback[] getCallbacks(Class<?> rootClass) throws Exception { // Parameters used for optimization choices... boolean exposeProxy = this.advised.isExposeProxy(); boolean isFrozen = this.advised.isFrozen(); boolean isStatic = this.advised.getTargetSource().isStatic(); //将AdviceSupport(里面包含连接器链)封装到DynamicAdvisedInterceptor,,并加入了Callback, DynamicAdvisedInterceptor实现了CGLIB的MethodInterceptor Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised); // Choose a "straight to target" interceptor. (used for calls that are // unadvised but can return this). May be required to expose the proxy. Callback targetInterceptor; if (exposeProxy) { targetInterceptor = isStatic ? new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) : new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()); } else { targetInterceptor = isStatic ? new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) : new DynamicUnadvisedInterceptor(this.advised.getTargetSource()); } // Choose a "direct to target" dispatcher (used for // unadvised calls to static targets that cannot return this). Callback targetDispatcher = isStatic ? new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp(); Callback[] mainCallbacks = new Callback[] { aopInterceptor, // for normal advice targetInterceptor, // invoke target without considering advice, if optimized new SerializableNoOp(), // no override for methods mapped to this targetDispatcher, this.advisedDispatcher, new EqualsInterceptor(this.advised), new HashCodeInterceptor(this.advised) }; Callback[] callbacks; // If the target is a static one and the advice chain is frozen, // then we can make some optimizations by sending the AOP calls // direct to the target using the fixed chain for that method. if (isStatic && isFrozen) { Method[] methods = rootClass.getMethods(); Callback[] fixedCallbacks = new Callback[methods.length]; this.fixedInterceptorMap = new HashMap<>(methods.length); // TODO: small memory optimization here (can skip creation for methods with no advice) for (int x = 0; x < methods.length; x++) { List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass); fixedCallbacks[x] = new FixedChainStaticTargetInterceptor( chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass()); this.fixedInterceptorMap.put(methods[x].toString(), x); } // Now copy both the callbacks from mainCallbacks // and fixedCallbacks into the callbacks array. callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length]; System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length); System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length); this.fixedInterceptorOffset = mainCallbacks.length; } else { callbacks = mainCallbacks; } return callbacks; }
这里将拦截器链封装到了DynamicAdvisedInterceptor中,并加入了Callback,DynamicAdvisedInterceptor实现了CGLIB的MethodInterceptor,所以其核心逻辑在intercept方法中:
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; Object target = null; TargetSource targetSource = this.advised.getTargetSource(); try { if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool... target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); //获取这个方法的拦截器链 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; // Check whether we only have one InvokerInterceptor: that is, // no real advice, but just reflective invocation of the target. if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { //直接调用目标的方法 Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = methodProxy.invoke(target, argsToUse); } else { //创建一个Invcation()对象并对其进行调用 // We need to create a method invocation... retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); } retVal = processReturnType(proxy, target, method, retVal); return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }
这里我们看到了与JDK动态代理同样的获取拦截器链的过程,并且CglibMethodInvokcation继承了我们在JDK动态代理看到的ReflectiveMethodInvocation,但是并没有重写其proceed方法,只是重写了执行目标方法的逻辑,所以整体上是大同小异的。
createProxyClassAndInstance()
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) { enhancer.setInterceptDuringConstruction(false); enhancer.setCallbacks(callbacks); return (this.constructorArgs != null && this.constructorArgTypes != null ? enhancer.create(this.constructorArgTypes, this.constructorArgs) : enhancer.create()); }
到这里,整个Spring 动态AOP的源码就分析完了,Spring还支持静态AOP,这里就不过多赘述了,有兴趣的读者可以查阅相关资料来学习。
分析:
/** * AOP:【动态代理】 * 指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式; * * 1、导入aop模块;Spring AOP:(spring-aspects) * 2、定义一个业务逻辑类(MathCalculator);在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常,xxx) * 3、定义一个日志切面类(LogAspects):切面类里面的方法需要动态感知MathCalculator.div运行到哪里然后执行; * 通知方法: * 前置通知(@Before):logStart:在目标方法(div)运行之前运行 * 后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束) * 返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行 * 异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行 * 环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced()) * 4、给切面类的目标方法标注何时何地运行(通知注解); * 5、将切面类和业务逻辑类(目标方法所在类)都加入到容器中; * 6、必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect) * [7]、给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】 * 在Spring中很多的 @EnableXXX; * * 三步: * 1)、将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect) * 2)、在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式) * 3)、开启基于注解的aop模式;@EnableAspectJAutoProxy * * AOP原理:【看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么?】 * @EnableAspectJAutoProxy; * 1、@EnableAspectJAutoProxy是什么? * @Import(AspectJAutoProxyRegistrar.class):给容器中导入AspectJAutoProxyRegistrar * 利用AspectJAutoProxyRegistrar自定义给容器中注册bean;BeanDefinetion * internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator * * 给容器中注册一个AnnotationAwareAspectJAutoProxyCreator; * * 2、 AnnotationAwareAspectJAutoProxyCreator: * AnnotationAwareAspectJAutoProxyCreator * ->AspectJAwareAdvisorAutoProxyCreator * ->AbstractAdvisorAutoProxyCreator * ->AbstractAutoProxyCreator * implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware * 关注后置处理器(在bean初始化完成前后做事情)、自动装配BeanFactory * * AbstractAutoProxyCreator.setBeanFactory() * AbstractAutoProxyCreator.有后置处理器的逻辑; * * AbstractAdvisorAutoProxyCreator.setBeanFactory()-》initBeanFactory() * * AnnotationAwareAspectJAutoProxyCreator.initBeanFactory() * * * 流程: * 1)、传入配置类,创建ioc容器 * 2)、注册配置类,调用refresh()刷新容器; * 3)、registerBeanPostProcessors(beanFactory);注册bean的后置处理器来方便拦截bean的创建; * 1)、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor * 2)、给容器中加别的BeanPostProcessor * 3)、优先注册实现了PriorityOrdered接口的BeanPostProcessor; * 4)、再给容器中注册实现了Ordered接口的BeanPostProcessor; * 5)、注册没实现优先级接口的BeanPostProcessor; * 6)、注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中; * 创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】 * 1)、创建Bean的实例 * 2)、populateBean;给bean的各种属性赋值 * 3)、initializeBean:初始化bean; * 1)、invokeAwareMethods():处理Aware接口的方法回调 * 2)、applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization() * 3)、invokeInitMethods();执行自定义的初始化方法 * 4)、applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization(); * 4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;--》aspectJAdvisorsBuilder * 7)、把BeanPostProcessor注册到BeanFactory中; * beanFactory.addBeanPostProcessor(postProcessor); * =======以上是创建和注册AnnotationAwareAspectJAutoProxyCreator的过程======== * * AnnotationAwareAspectJAutoProxyCreator => InstantiationAwareBeanPostProcessor * 4)、finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作;创建剩下的单实例bean * 1)、遍历获取容器中所有的Bean,依次创建对象getBean(beanName); * getBean->doGetBean()->getSingleton()-> * 2)、创建bean * 【AnnotationAwareAspectJAutoProxyCreator在所有bean创建之前会有一个拦截,InstantiationAwareBeanPostProcessor,会调用postProcessBeforeInstantiation()】 * 1)、先从缓存中获取当前bean,如果能获取到,说明bean是之前被创建过的,直接使用,否则再创建; * 只要创建好的Bean都会被缓存起来 * 2)、createBean();创建bean; * AnnotationAwareAspectJAutoProxyCreator 会在任何bean创建之前先尝试返回bean的实例 * 【BeanPostProcessor是在Bean对象创建完成初始化前后调用的】 * 【InstantiationAwareBeanPostProcessor是在创建Bean实例之前先尝试用后置处理器返回对象的】 * 1)、resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation * 希望后置处理器在此能返回一个代理对象;如果能返回代理对象就使用,如果不能就继续 * 1)、后置处理器先尝试返回对象; * bean = applyBeanPostProcessorsBeforeInstantiation(): * 拿到所有后置处理器,如果是InstantiationAwareBeanPostProcessor; * 就执行postProcessBeforeInstantiation * if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } * * 2)、doCreateBean(beanName, mbdToUse, args);真正的去创建一个bean实例;和3.6流程一样; * 3)、 * * * AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】 的作用: * 1)、每一个bean创建之前,调用postProcessBeforeInstantiation(); * 关心MathCalculator和LogAspect的创建 * 1)、判断当前bean是否在advisedBeans中(保存了所有需要增强bean) * 2)、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean, * 或者是否是切面(@Aspect) * 3)、是否需要跳过 * 1)、获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】 * 每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor; * 判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true * 2)、永远返回false * * 2)、创建对象 * postProcessAfterInitialization; * return wrapIfNecessary(bean, beanName, cacheKey);//包装如果需要的情况下 * 1)、获取当前bean的所有增强器(通知方法) Object[] specificInterceptors * 1、找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的) * 2、获取到能在bean使用的增强器。 * 3、给增强器排序 * 2)、保存当前bean在advisedBeans中; * 3)、如果当前bean需要增强,创建当前bean的代理对象; * 1)、获取所有增强器(通知方法) * 2)、保存到proxyFactory * 3)、创建代理对象:Spring自动决定 * JdkDynamicAopProxy(config);jdk动态代理; * ObjenesisCglibAopProxy(config);cglib的动态代理; * 4)、给容器中返回当前组件使用cglib增强了的代理对象; * 5)、以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程; * * * 3)、目标方法执行 ; * 容器中保存了组件的代理对象(cglib增强后的对象),这个对象里面保存了详细信息(比如增强器,目标对象,xxx); * 1)、CglibAopProxy.intercept();拦截目标方法的执行 * 2)、根据ProxyFactory对象获取将要执行的目标方法拦截器链; * List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); * 1)、List<Object> interceptorList保存所有拦截器 5 * 一个默认的ExposeInvocationInterceptor 和 4个增强器; * 2)、遍历所有的增强器,将其转为Interceptor; * registry.getInterceptors(advisor); * 3)、将增强器转为List<MethodInterceptor>; * 如果是MethodInterceptor,直接加入到集合中 * 如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor; * 转换完成返回MethodInterceptor数组; * * 3)、如果没有拦截器链,直接执行目标方法; * 拦截器链(每一个通知方法又被包装为方法拦截器,利用MethodInterceptor机制) * 4)、如果有拦截器链,把需要执行的目标对象,目标方法, * 拦截器链等信息传入创建一个 CglibMethodInvocation 对象, * 并调用 Object retVal = mi.proceed(); * 5)、拦截器链的触发过程; * 1)、如果没有拦截器执行执行目标方法,或者拦截器的索引和拦截器数组-1大小一样(指定到了最后一个拦截器)执行目标方法; * 2)、链式获取每一个拦截器,拦截器执行invoke方法,每一个拦截器等待下一个拦截器执行完成返回以后再来执行; * 拦截器链的机制,保证通知方法与目标方法的执行顺序; * * 总结: * 1)、 @EnableAspectJAutoProxy 开启AOP功能 * 2)、 @EnableAspectJAutoProxy 会给容器中注册一个组件 AnnotationAwareAspectJAutoProxyCreator * 3)、AnnotationAwareAspectJAutoProxyCreator是一个后置处理器; * 4)、容器的创建流程: * 1)、registerBeanPostProcessors()注册后置处理器;创建AnnotationAwareAspectJAutoProxyCreator对象 * 2)、finishBeanFactoryInitialization()初始化剩下的单实例bean * 1)、创建业务逻辑组件和切面组件 * 2)、AnnotationAwareAspectJAutoProxyCreator拦截组件的创建过程 * 3)、组件创建完之后,判断组件是否需要增强 * 是:切面的通知方法,包装成增强器(Advisor);给业务逻辑组件创建一个代理对象(cglib); * 5)、执行目标方法: * 1)、代理对象执行目标方法 * 2)、CglibAopProxy.intercept(); * 1)、得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor) * 2)、利用拦截器的链式机制,依次进入每一个拦截器进行执行; * 3)、效果: * 正常执行:前置通知-》目标方法-》后置通知-》返回通知 * 出现异常:前置通知-》目标方法-》后置通知-》异常通知 * * * */