springAOP源码分析

SpringAop实现为动态代理进行实现的,实现方式有2种,JDK动态代理和CGlib动态代理
先写一个AOP的案列加以说明
配置文件代码为:

    <bean id="userDao" class="com.spring.aop.service.UserDaoImpl"/>

    <bean id="logger" class="com.spring.aop.log.Logger" />

    <!-- 切面:切入点和通知 -->
    <aop:config>
        <aop:aspect id="aspect"  ref="logger">
            <aop:pointcut expression="execution(* com.spring.aop.service..*.*(..))" id="udpateUserMethod" />
            <aop:before method="recordBefore" pointcut-ref="udpateUserMethod" />
            <aop:after method="recordAfter"   pointcut-ref="udpateUserMethod" />
        </aop:aspect>
    </aop:config>

其中增强类Logger的实现为:

package com.spring.aop.log;

public class Logger {

     public void recordBefore(){
            System.out.println("recordBefore");
        }

        public void recordAfter(){
            System.out.println("recordAfter");
        }

} 

被曾强类UserDaoImpl和被曾强类接口的实现为:

package com.spring.aop.service;

public interface UserDao {
    void addUser();
    void deleteUser();
}

package com.spring.aop.service;

public class UserDaoImpl implements UserDao {

    @Override
    public void addUser() {
        System.out.println("add user ");
    }

    @Override
    public void deleteUser() {
         System.out.println("delete user ");
    }

}

测试方法代码:

package com.spring.aop.main;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.aop.service.UserDao;

public class testAop {

    public static void main(String[] args) {
            ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("springAop.xml");//BeanDefination的解析注册,代理对象的生成
            UserDao userDao = (UserDao) applicationContext.getBean("userDao");//可以看到userDao类型是以$Proxy开头的,说明是通过JDK动态代理的方式获取的
            userDao.addUser();//增强行为发生的时刻
    }

}

运行结果:

可以看出对目标方法进行了增强。

下面开始从Spring XML解析进行源码分析

从DefaultBeanDefinitionDocumentReader类的parseBeanDefinitions方法开始进行分析,在parseBeanDefinitions方法分为spring默认标签解析和自定义标签解析,在这里解析标签<aop:config>的时候,使用到了自定义标签解析,代码如下:

    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;
        }        //此时的handler指的是ConfigBeanDefinitionParser对象
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }

下面进入ConfigBeanDefinitionParser对象的parse方法进行分析:

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        CompositeComponentDefinition compositeDef =
                new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
        parserContext.pushContainingComponent(compositeDef);

        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);
            }            // 在这里解析aspect标签
            else if (ASPECT.equals(localName)) {
                parseAspect(elt, parserContext);
            }
        }

        parserContext.popAndRegisterContainingComponent();
        return null;
    }
    private void parseAspect(Element aspectElement, ParserContext parserContext) {
        // 获取aspect标签上面定义的ID
        String aspectId = aspectElement.getAttribute(ID);
        // 获取aspect标签上面引用的增强类 logger
        String aspectName = aspectElement.getAttribute(REF);

        try {
            // 将aspectId和aspectName封装成 AspectEntry对象,并放入栈parseState中
            this.parseState.push(new AspectEntry(aspectId, aspectName));
            //把<aop:before>等通知相关的信息封装到AspectJPointcutAdvisor中,然后放到该集合里
            List<BeanDefinition> beanDefinitions = new ArrayList<BeanDefinition>();
             //把ref相关的信息如aop.xml中的logger,updateUserMethod等封装到RunTimeBeanReference中,然后放到这个集合中
            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);
                if (isAdviceNode(node, parserContext)) {
                    if (!adviceFoundAlready) {
                        // 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));
                    }
                    // 把通知相关信息封装到AspectJPointcutAdvisor这个类中,同时封装ref信息然后放到BeanReferences中
                    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();
        }
    }

最终是将<aop:config>配置的相关信息封装成类,然后放入到containingComponents栈中,方便以后进行操作

原文地址:https://www.cnblogs.com/histlyb/p/9483017.html

时间: 2024-10-29 17:36:07

springAOP源码分析的相关文章

SpringAOP源码分析总结

1.Advisor(增强器):充当Advice和Pointcut的适配器,类似使用Aspect的@Aspect注解的类(前一章节所述).一般有advice和pointcut属性. 祖先接口为org.springframework.aop.Advisor,应用中可直接使用org.springframework.aop.support.DefaultPointcutAdvisor < aop:advisor>大多用于事务管理,定义通知器(通知器跟切面一样,也包括通知和切点) 2.Advice:用于

Spring-AOP源码分析随手记(一)

[email protected](proxyTargetClass = true) 就是弄了个"org.springframework.aop.config.internalAutoProxyCreator"::AnnotationAwareAspectJAutoProxyCreator.class的Bean到容器中 2.分析AnnotationAwareAspectJAutoProxyCreator.class类结构 得出了哪些是重要的几个. 3.查看AnnotationAware

Spring-AOP源码分析随手记(二)

这次来分析下切面的执行过程. 1.怎么看? 怎么开始看源码呢?就直接从被增强的方法调用那里打断点,看看怎么执行的: 然后就来到了这: 2.初步分析 里面有段: if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } 就是上篇文章讲到的注解配置暴露代理对象,放到A

springAOP源码分析之篇四:适配器模式

MethodInterceptor AdvisorAdapter和Advice之间实现了适配器模式首先增加方法的执行时通过拦截器链进行执行的,而配置文件配置的参数解析完以后是一增强对象的形式进行封装的拦截器要想调用增强Advice的增强方法,是无法直接方访问的,因此加一个增强适配类,将增强转换为拦截器MethodInterceptor的结构: public interface MethodInterceptor extends Interceptor { /** * Implement this

Spring源码分析专题——目录

Spring源码分析专题 -- 阅读指引 IOC容器 Spring源码分析专题 -- IOC容器启动过程(上篇) Spring源码分析专题 -- IOC容器启动过程(中篇) Spring源码分析专题 -- IOC容器启动过程(下篇) Spring源码分析专题 -- IOC容器依赖注入 SpringMVC Spring源码分析专题 -- SpringMVC IOC容器依赖注入 Spring源码分析专题 -- SpringMVC原理分析 Spring源码分析专题 -- SpringAOP源码分析 S

Spring IOC 容器源码分析

前言: Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文并不能让你成为 Spring 专家,不过一定有助于大家理解 Spring 的很多概念,帮助大家排查应用中和 Spring 相关的一些问题. 阅读建议:读者至少需要知道怎么配置 Spring,了解 Spring 中的各种概念,少部分内容我还假设读者使用过 SpringMVC.本文要说的 IOC

TeamTalk源码分析之login_server

login_server是TeamTalk的登录服务器,负责分配一个负载较小的MsgServer给客户端使用,按照新版TeamTalk完整部署教程来配置的话,login_server的服务端口就是8080,客户端登录服务器地址配置如下(这里是win版本客户端): 1.login_server启动流程 login_server的启动是从login_server.cpp中的main函数开始的,login_server.cpp所在工程路径为server\src\login_server.下表是logi

Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)

1 背景 还记得前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事件疑惑吗?当时说了,在那一篇咱们只讨论View的触摸事件派发机制,这个疑惑留在了这一篇解释,也就是ViewGroup的事件派发机制. PS:阅读本篇前建议先查看前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>,这一篇承接上一篇. 关于View与ViewGroup的区别在前一篇的A

HashMap与TreeMap源码分析

1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Java这么久,也写过一些小项目,也使用过TreeMap无数次,但到现在才明白它的实现原理).因此本着"不要重复造轮子"的思想,就用这篇博客来记录分析TreeMap源码的过程,也顺便瞅一瞅HashMap. 2. 继承结构 (1) 继承结构 下面是HashMap与TreeMap的继承结构: pu