In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
Today, I will talk to you about how to analyze the Spring source code to analyze the principle of AOP implementation. Many people may not know much about it. In order to make you understand better, the editor has summarized the following content for you. I hope you can get something according to this article.
Preface
Before I wrote six articles to analyze the Spring Bean loading process in detail, after this part, we will enter a more difficult part, that is, the analysis of the implementation principle of AOP. To explore the principles of AOP implementation, first define several classes, a Dao interface:
Public interface Dao {
Public void select ()
Public void insert ()
}
The implementation class DaoImpl of the Dao interface:
Public class DaoImpl implements Dao {@ Override public void select () {System.out.println ("Enter DaoImpl.select ()");} @ Override public void insert () {System.out.println ("Enter DaoImpl.insert ()");}}
Define a TimeHandler to print time before and after method calls, which plays the role of crosscutting concerns in AOP:
Public class TimeHandler {public void printTime () {System.out.println ("CurrentTime:" + System.currentTimeMillis ());}}
Define a XML file aop.xml:
Write a piece of test code TestAop.java:
Public class TestAop {@ Test public void testAop () {ApplicationContext ac = new ClassPathXmlApplicationContext ("spring/aop.xml"); Dao dao = (Dao) ac.getBean ("daoImpl"); dao.select ();}}
With the above content, we can follow the code based on this to see how Spring implements AOP.
The principle of AOP implementation-- finding the Source of Spring dealing with AOP
One of the big reasons why many friends don't want to see the AOP source code is that they can't find out where the entrance to the AOP source code implementation is. However, we can take a look at the above test code, whether ordinary Bean or AOP, the Bean is finally obtained through the getBean method and the method is called. The objects after getBean have printed the contents of the TimeHandler class printTime () method before and after, and you can imagine that they have been processed by the Spring container.
Such being the case, there are no more than two places to deal with:
There should be special handling when loading Bean definitions
There should have been special treatment in getBean.
Therefore, this paper revolves around [1. There should have been special handling when loading the Bean definition] expand, first find out where the Spring does the special treatment to AOP. The code navigates directly to the parseBeanDefinitions method of DefaultBeanDefinitionDocumentReader:
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); }} 正常来说,遇到、这两个标签的时候,都会执行第9行的代码,因为标签是默认的Namespace。但是在遇到后面的标签的时候就不一样了,并不是默认的Namespace,因此会执行第12行的代码,看一下: public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); 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行从这个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
Next, there is line 8 of the code that parses the content using AopNamespaceHandler's parse method.
Analytical intensifier advisor
AOP Bean definition loading-converts, according to the weaving method, into a RootBeanDefinition named adviceDef
After the above analysis, we have found that Spring is the AOP processed by AopNamespaceHandler, then go to the source code of the parse method of AopNamespaceHandler:
Public BeanDefinition parse (Element element, ParserContext parserContext) {return findParserForElement (element, parserContext) .parse (element, parserContext);}
First, get the specific Parser, because the current node is, there is a column at the end of the previous section, and config is processed through ConfigBeanDefinitionParser, so the findParserForElement (element, parserContext) code gets the ConfigBeanDefinitionParser, and then looks at the parse method of ConfigBeanDefinitionParser:
Public BeanDefinition parse (Element element, ParserContext parserContext) {CompositeComponentDefinition compositeDef = new CompositeComponentDefinition (element.getTagName (), parserContext.extractSource (element)); parserContext.pushContainingComponent (compositeDef); configureAutoProxyCreator (parserContext, element); List 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;}
First of all, let's mention the code in line 6. The specific implementation of this line of code does not follow, but it is very important. I will say a few words about the role of the configureAutoProxyCreator method:
Registers a Bean definition with BeanName for org.springframework.aop.config.internalAutoProxyCreator with the Spring container, which can be customized or used (based on priority) provided by Spring
Spring provides org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator by default, which is the core class of AOP, which will be explained in the next section.
In this method, depending on the configuration of proxy-target-class and expose-proxy, whether to use CGLIB for proxy and whether to expose the final proxy is also set.
The next node is, if you want to see the code parseAspect that must execute line 18, follow it:
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 beanDefinitions = new ArrayList (); List beanReferences = new ArrayList (); List 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); if (isAdviceNode(node, parserContext)) { if (!adviceFoundAlready) { adviceFoundAlready = true; if (!StringUtils.hasText(aspectName)) { parserContext.getReaderContext().error( " 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 = createAspectComponentDefinition( aspectElement, aspectId, beanDefinitions, beanReferences, parserContext); parserContext.pushContainingComponent(aspectComponentDefinition); List 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循环只用来处理标签下的、、、、这五个标签的。 接着,如果是上述五种标签之一,那么进入第33行~第34行的parseAdvice方法: private AbstractBeanDefinition parseAdvice( String aspectName, int order, Element aspectElement, Element adviceElement, ParserContext parserContext, List beanDefinitions, List 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 AbstractBeanDefinition adviceDef = createAdviceDefinition( adviceElement, parserContext, aspectName, order, methodDefinition, aspectFactoryDef, beanDefinitions, beanReferences); // configure the advisor 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 parserContext.getReaderContext().registerWithGeneratedName(advisorDefinition); return advisorDefinition; } finally { this.parseState.pop(); }} 方法主要做了三件事: 根据织入方式(before、after这些)创建RootBeanDefinition,名为adviceDef即advice定义 将上一步创建的RootBeanDefinition写入一个新的RootBeanDefinition,构造一个新的对象,名为advisorDefinition,即advisor定义 将advisorDefinition注册到DefaultListableBeanFactory中 下面来看做的第一件事createAdviceDefinition方法定义: private AbstractBeanDefinition createAdviceDefinition( Element adviceElement, ParserContext parserContext, String aspectName, int order, RootBeanDefinition methodDef, RootBeanDefinition aspectFactoryDef, List beanDefinitions, List beanReferences) { 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 + "]."); }} 既然创建Bean定义,必然该Bean定义中要对应一个具体的Class,不同的切入方式对应不同的Class: before对应AspectJMethodBeforeAdvice After对应AspectJAfterAdvice after-returning对应AspectJAfterReturningAdvice after-throwing对应AspectJAfterThrowingAdvice around对应AspectJAroundAdvice createAdviceDefinition方法剩余逻辑没什么,就是判断一下标签里面的属性并设置一下相应的值而已,至此、两个标签对应的AbstractBeanDefinition就创建出来了。 AOP Bean定义加载--将名为adviceDef的RootBeanDefinition转换成名为advisorDefinition的RootBeanDefinition 下面我们看一下第二步的操作,将名为adviceDef的RootBeanD转换成名为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行的代码是用于判断标签中有没有"order"属性的,有就设置一下,"order"属性是用来控制切入方法优先级的。 AOP Bean定义加载--将BeanDefinition注册到DefaultListableBeanFactory中 最后一步就是将BeanDefinition注册到DefaultListableBeanFactory中了,代码就是前面ConfigBeanDefinitionParser的parseAdvice方法的最后一部分了: // register the final advisorparserContext.getReaderContext().registerWithGeneratedName(advisorDefinition);...跟一下registerWithGeneratedName方法的实现: public String registerWithGeneratedName(BeanDefinition beanDefinition) { String generatedName = generateBeanName(beanDefinition); getRegistry().registerBeanDefinition(generatedName, beanDefinition); return generatedName; } 第2行获取注册的名字BeanName,和的注册差不多,使用的是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已经有了,剩下的就是Bean定义,Bean定义的解析流程之前已经看过了,就不说了。解析切面的过程 AOP Bean定义加载--AopNamespaceHandler处理流程 回到ConfigBeanDefinitionParser的parseAspect方法: private void parseAspect(Element aspectElement, ParserContext parserContext) { ... AspectComponentDefinition aspectComponentDefinition = createAspectComponentDefinition( aspectElement, aspectId, beanDefinitions, beanReferences, parserContext); parserContext.pushContainingComponent(aspectComponentDefinition); List pointcuts = DomUtils.getChildElementsByTagName(aspectElement, POINTCUT); for (Element pointcutElement : pointcuts) { parsePointcut(pointcutElement, parserContext); } parserContext.popAndRegisterContainingComponent(); } finally { this.parseState.pop(); }} 省略号部分表示是解析的是、这种标签,上部分已经说过了,就不说了,下面看一下解析部分的源码。 第5行~第7行的代码构建了一个Aspect标签组件定义,并将Apsect标签组件定义推到ParseContext即解析工具上下文中,这部分代码不是关键。 第9行的代码拿到所有下的pointcut标签,进行遍历,由parsePointcut方法进行处理: 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)); pointcutDefinition = createPointcutDefinition(expression); pointcutDefinition.setSource(parserContext.extractSource(pointcutElement)); String pointcutBeanName = id; if (StringUtils.hasText(pointcutBeanName)) { parserContext.getRegistry().registerBeanDefinition(pointcutBeanName, pointcutDefinition); } else { pointcutBeanName = parserContext.getReaderContext().registerWithGeneratedName(pointcutDefinition); } parserContext.registerComponent( new PointcutComponentDefinition(pointcutBeanName, pointcutDefinition, expression)); } finally { this.parseState.pop(); } return pointcutDefinition;} 第2行~第3行的代码获取标签下的"id"属性与"expression"属性。 第8行的代码推送一个PointcutEntry,表示当前Spring上下文正在解析Pointcut标签。 第9行的代码创建Pointcut的Bean定义,之后再看,先把其他方法都看一下。 第10行的代码不管它,最终从NullSourceExtractor的extractSource方法获取Source,就是个null。 第12行~第18行的代码用于注册获取到的Bean定义,默认pointcutBeanName为标签中定义的id属性: 如果标签中配置了id属性就执行的是第13行~第15行的代码,pointcutBeanName=id 如果标签中没有配置id属性就执行的是第16行~第18行的代码,和Bean不配置id属性一样的规则,pointcutBeanName=org.springframework.aop.aspectj.AspectJExpressionPointcut#序号(从0开始累加) 第20行~第21行的代码向解析工具上下文中注册一个Pointcut组件定义 第23行~第25行的代码,finally块在标签解析完毕后,让之前推送至栈顶的PointcutEntry出栈,表示此次标签解析完毕。 最后回头来一下第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;} 关键就是注意一下两点: 标签对应解析出来的BeanDefinition是RootBeanDefinition,且RootBenaDefinitoin中的Class是org.springframework.aop.aspectj.AspectJExpressionPointcut 标签对应的Bean是prototype即原型的 这样一个流程下来,就解析了标签中的内容并将之转换为RootBeanDefintion存储在Spring容器中。 AOP为Bean生成代理的时机分析 上篇文章说了,org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator这个类是Spring提供给开发者的AOP的核心类,就是AspectJAwareAdvisorAutoProxyCreator完成了【类/接口->Agent], first let's look at the hierarchical structure of AspectJAwareAdvisorAutoProxyCreator:
The most noteworthy point here is the box at the bottom left, which I would like to sum up in a few sentences:
AspectJAwareAdvisorAutoProxyCreator is the implementation class of the BeanPostProcessor interface
The postProcessBeforeInitialization method and the postProcessAfterInitialization method are implemented in the parent class AbstractAutoProxyCreator
The postProcessBeforeInitialization method is an empty implementation
Logic code in the postProcessAfterInitialization method
Based on the above analysis, the timing of generating proxies for Bean is clear: after each Bean is initialized, if necessary, call postProcessBeforeInitialization in AspectJAwareAdvisorAutoProxyCreator to generate proxies for Bean.
Instantiation of proxy object-- to determine whether it is a generation agent or not
The above analysis shows that the timing of Bean generation agent is after each Bean is initialized. After the code is initialized by Bean, the initializeBean method of AbstractAutowireCapableBeanFactory initializes it first:
Protected Object initializeBean (final String beanName, final Object bean, RootBeanDefinition mbd) {if (System.getSecurityManager ()! = null) {AccessController.doPrivileged (new PrivilegedAction () {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 ()) {wrappedBean = applyBeanPostProcessorsAfterInitialization (wrappedBean, beanName);} return wrappedBean;}
The applyBeanPostProcessorsBeforeInitialization method on line 16 before initialization, and the applyBeanPostProcessorsAfterInitialization method on line 29 after initialization:
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;}
The postProcessBeforeInitialization method of each BeanPostProcessor is called here. Based on the previous analysis, take a look at the postProcessAfterInitialization method implementation of AbstractAutoProxyCreator:
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;}
Follow the method wrapIfNecessary in line 5:
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. 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;}
Lines 2-11 are some scenario judgments that do not need to generate agents, which are skipped here. The first question we need to think about is: which target objects need to generate proxies? Because there are a lot of Bean in the configuration file, you certainly can't generate proxies for every Bean, so you need a set of rules to determine whether Bean needs to generate proxies. This set of rules is the code getAdvicesAndAdvisorsForBean in line 14:
Protected List findEligibleAdvisors (Class beanClass, String beanName) {List candidateAdvisors = findCandidateAdvisors (); List eligibleAdvisors = findAdvisorsThatCanApply (candidateAdvisors, beanClass, beanName); extendAdvisors (eligibleAdvisors); if (! eligibleAdvisors.isEmpty ()) {eligibleAdvisors = sortAdvisors (eligibleAdvisors);} return eligibleAdvisors;}
As the name implies, the method means to find the appropriate class for the specified Advisor.
The second line of code, looking for the candidate Advisors, according to the configuration file above, there are two candidate Advisor, respectively, under the node and these two, these two have been transformed into RootBeanDefinition at the time of XML parsing.
Skip line 3 and take a look at the code extendAdvisors method on line 4, and then focus on line 3. The code extendAdvisors method in line 4 adds an org.springframework.aop.support.DefaultPointcutAdvisor to the beginning of the candidate Advisor chain (that is, the location of List.get (0)).
The third line of code, based on the candidate Advisors, looks for an Advisor that can be used, and follows the method implementation:
Public static List findAdvisorsThatCanApply (List candidateAdvisors, Class clazz) {if (candidateAdvisors.isEmpty ()) {return candidateAdvisors;} List eligibleAdvisors = new LinkedList (); 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;}
The main judgment of the whole method revolves around the canApply expansion method:
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; return canApply (pca.getPointcut (), targetClass, hasIntroductions);} else {/ / It doesn't have a pointcut so we assume it applies. Return true;}}
The actual type of the first parameter advisor is AspectJPointcutAdvisor, which is a subclass of PointcutAdvisor, so execute the method on line 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 classes = new HashSet (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;}
This method actually makes a two-tier judgment based on the expression corresponding to the current Advisor:
The target class must meet the matching rules of expression
The methods in the target class must meet the matching rules of expression. Of course, not all methods here need to meet the matching rules of expression. There is only one method that satisfies it.
If both of the above are met, the container will determine that the condition is met and the proxy object needs to be generated by returning an array object in which the corresponding Advisor is stored.
Proxy object instantiation process
Instantiation of proxy object-- carding the context of generating proxy code
After analyzing the conditions for generating proxies, let's take a formal look at how the Spring context generates proxies. Go back to the wrapIfNecessary method of AbstractAutoProxyCreator:
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. 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;}
Line 14 gets the corresponding Advisor array, and line 15 determines that as long as the Advisor array is not empty, a proxy will be created through the code on line 17:
Protected Object createProxy (Class beanClass, String beanName, Object [] specificInterceptors, TargetSource targetSource) {ProxyFactory proxyFactory = new ProxyFactory (); / / Copy our properties (proxyTargetClass etc) inherited from ProxyConfig. ProxyFactory.copyFrom (this); 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);}} 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);} return proxyFactory.getProxy (this.proxyClassLoader);}
Lines 4-6 new gives a ProxyFactory,Proxy, as the name implies, the meaning of the agent factory, which provides an easy way to get and configure the AOP proxy using code.
The code in line 8 makes a judgment that the proxy-target-class= "false" or proxy-target-class is not configured in this node, that is, the CGLIB generation agent is not used. If the condition is met, go ahead and get all the interfaces implemented by the current Bean, that is, these interface Class objects are added to the ProxyFactory.
The code in lines 17-28 is not necessary to see, just add some parameters to the ProxyFactory. Let's focus on line 30, proxyFactory.getProxy (this.proxyClassLoader):
Public Object getProxy (ClassLoader classLoader) {return createAopProxy () .getProxy (classLoader);}
The implementation code is only one line, but it clearly tells us that we have done two things:
Create an AopProxy interface implementation class
Obtain the corresponding proxy through the getProxy method of the implementation class of the AopProxy interface
Starting from these two points, we will analyze it in two parts.
Instantiation of proxy object-- create AopProxy interface implementation class
Take a look at the implementation of the createAopProxy () method, which is in the DefaultAopProxyFactory class:
Protected final synchronized AopProxy createAopProxy () {if (! this.active) {activate ();} return getAopProxyFactory () .createAopProxy (this);}
There is no need to look at the previous part, but go straight to the point, that is, the createAopProxy method:
Public AopProxy createAopProxy (AdvisedSupport config) throws AopConfigException {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);}} usually we can sum up the principle of AOP in three sentences:
Use CGLIB for class generation agents
Use JDK native Proxy for the interface generation agent
You can specify the use of CGLIB generation proxies for interfaces through configuration files
The origin of these three sentences is the createAopProxy method. When you see that the code that defaults to line 19 uses the Proxy generation agent that comes with JDK, there are three exceptions:
The isOptimize method of ProxyConfig is true, which means that Spring is left to optimize rather than user-specified
The isProxyTargetClass method of ProxyConfig is true, which means that proxy-target-class= "true" is configured
ProxyConfig satisfies that the execution result of the hasNoUserSuppliedProxyInterfaces method is true, which means that the object does not implement any interfaces or the implemented interface is SpringProxy interface
After entering the if judgment in line 2, you decide which AopProxy to return based on the type of target. To sum up, it is simply:
Proxy-target-class is not configured or proxy-target-class= "false". Return JdkDynamicAopProxy
Proxy-target-class= "true" or the object does not implement any interface or only implements the SpringProxy interface and returns Cglib2AopProxy
Of course, both JdkDynamicAopProxy and Cglib2AopProxy,AdvisedSupport are passed in as constructor arguments, where the specific Advisor is stored.
Instantiation of proxy object-- obtain the corresponding proxy through the getProxy method
In fact, the code has been analyzed to JdkDynamicAopProxy and Cglib2AopProxy, the rest is nothing to talk about, but to see the familiarity of these two ways to generate agents.
Cglib2AopProxy generation agent code will not look, friends who are not familiar with Cglib can see Cglib and its basic use of an article.
Take a look at the way JdkDynamicAopProxy generates proxies:
Public Object getProxy (ClassLoader classLoader) {if (logger.isDebugEnabled ()) {logger.debug ("Creating JDK dynamic proxy: target source is" + this.advised.getTargetSource ());} Class [] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces (this.advised); findDefinedEqualsAndHashCodeMethods (proxiedInterfaces); return Proxy.newProxyInstance (classLoader, proxiedInterfaces, this);}
Here, let's explain the code on lines 5 and 6. The function of line 5 is to get all the interfaces to be proxied. The function of line 6 is to try to find out whether there are equals methods and hashCode methods in these interface methods. At the same time, if there is a mark, look for the end. The equals method and hashCode method have special handling.
Finally, the proxy object corresponding to the interface / class is obtained through the Proxy.newProxyInstance method on line 7, and Proxy is the way to generate proxies natively supported by JDK.
Principle of proxy method invocation
The principle of generating a proxy for an interface / class has been analyzed in detail, and the method is called after the proxy is generated. Let's take a look at the principle of calling a method using JdkDynamicAopProxy.
Because JdkDynamicAopProxy itself implements the InvocationHandler interface, the logic of the specific agent pre-and post-processing is in the invoke method:
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 {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 ();} 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);} Object retVal; if (this.advised.exposeProxy) {/ / Make invocation available if necessary. OldProxy = AopContext.setCurrentProxy (proxy); setProxyContext = true;} / / 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 chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice (method, targetClass); / / 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. 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);}
The code from line 11 to line 18 indicates that the equals and hashCode methods do not generate proxy content for them even if they meet the expression rules. Instead, the equals and hashCode methods of JdkDynamicAopProxy are called. As for what these two methods do, you can take a look at the source code for yourself.
The code in lines 19-23 indicates that the Class to which the method belongs is an interface and that the Class to which the method belongs is the parent class or parent interface of AdvisedSupport, calling the method directly through reflection.
The code from line 27 to line 30, which is used to determine whether to expose the agent, is configured by the expose-proxy= "true/false" in the tag.
The code in line 41, which gets a list of all interceptors and dynamic interceptors in AdvisedSupport, is used to intercept methods. For our actual code, there are three Object in the list, which are:
Chain.get (0): ExposeInvocationInterceptor, which is a default interceptor, and the corresponding original Advisor is DefaultPointcutAdvisor
Chain.get (1): MethodBeforeAdviceInterceptor, which is used to intercept before the actual method call, and the corresponding original Advisor is AspectJMethodBeforeAdvice
Chain.get (2): AspectJAfterAdvice for processing after the actual method call
In the code from lines 45 to 50, it is normal if the interceptor list is empty, because a method under a class / interface may not meet the matching rules of expression, so it is called directly through reflection at this time.
Line 51 ~ line 56 of the code, if the interceptor list is not empty, according to the meaning of the comment, need a ReflectiveMethodInvocation, and intercept the original method through the proceed method, friends interested in the proceed method can take a look, which uses the idea of recursion to call Object in chain layer by layer.
CGLIB proxy implementation
Let's take a look at the way CGLIB proxies are used. Here you need to learn about CGLIB and how it creates proxies:
Here, the interceptor chain is encapsulated in DynamicAdvisedInterceptor, and Callback,DynamicAdvisedInterceptor is added to implement the MethodInterceptor of CGLIB, so the core logic is in the intercept method:
Here we see the same process of getting the interceptor chain as the JDK dynamic proxy, and CglibMethodInvokcation inherits the ReflectiveMethodInvocation we saw in the JDK dynamic proxy, but does not rewrite its proceed method, but only rewrites the logic that executes the target method, so it is more or less the same as a whole.
After reading the above, do you have any further understanding of how to analyze the Spring source code and the principle of AOP implementation? If you want to know more knowledge or related content, please follow the industry information channel, thank you for your support.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.