springAOP源码解析(一)

首先讲一下aop的概念,aop面向切面编程.我的理解是这是面向对象思想的一种进阶,把特定的逻辑与主逻辑分开处理,再通过一定的方式组合起来形成完整的处理过程.
下面说一下AOP的几个重要概念(都是我自己的理解,概念上肯定有偏差,但便于我记忆):
通知(advice):即要在主逻辑处运行的逻辑.我更喜欢另一个名字增强(《深入理解spring》中看到的).advice是个空接口,常用的实现由beforceAdvice、afterAdvice、ThrowsAdvice等
切点(pointcut):即要在哪里附加特定的逻辑,一般是特定的类或特定的方法上
连接点(joinpoint):即要在切点的哪个位置附加特定的逻辑,如方法前、方法后、抛出异常后等等
暂时想到这么多,以后想到再补充


OK,下面正式开始,首先先放一个aop的小demo

public class Car {
    private Engine engine;
    public void introduce(){
        System.out.println("engine brand:" + engine.getBrandName());
    }
    public Engine getEngine() {
        return engine;
    }
    public void setEngine(Engine engine) {
        this.engine = engine;
    }

}
//@Aspect
public class Myadvice {
    //@Before("execution(* com.kevindai.aop.*.*(..))")
    public void printSth(){
        System.out.println("------------------bibibi-----------------");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:annotation-config/>
    <aop:aspectj-autoproxy/>
    <bean name="car" class="com.kevindai.ioc.Car">
        <property name="engine" ref="engine"/>
    </bean>
    <bean name="engine" class="com.kevindai.ioc.Engine">
        <property name="brandName" value="bmw"/>
    </bean>
    <bean name="myadvice" class="com.kevindai.aop.Myadvice"></bean>
    <!--需要用注解的同学请把下面一段注释,然后把Myadvice中的注解注释取消-->
    <aop:config>
        <aop:aspect ref="myadvice">
            <aop:pointcut id="myAspect" expression="execution(* com.kevindai.aop.*.*(..))" />  
            <aop:before method="printSth"  pointcut-ref="myAspect" />
            <aop:before method="printSth"  pointcut="execution(* com.kevindai.aop.*.*(..))" />
        </aop:aspect>

    </aop:config>
</beans>
public class AOPTest {
    @SuppressWarnings("resource")
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("springioc.xml");

        Car advice = (Car) ctx.getBean("car");
        advice.introduce();

    }
}

首先咱们是用applicationContext来初始化spring容器的,因此,当容器启动时咱们定义的bean就应该被初始化了,所以咱们先看看初始化时怎么对<aop:config></aop:config>中的内容进行处理;追踪的方法这里不再赘述,我贴出我认为比较关键的代码

解析<aop:config></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;
        }
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }

这里就是获取节点名,并进行解析,下面咱们看看解析的过程,追寻过程比较复杂,我不一一贴出,最终会到下面这段代码中

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);
            }
            else if (ASPECT.equals(localName)) {
                parseAspect(elt, parserContext);
            }
        }

        parserContext.popAndRegisterContainingComponent();
        return null;
    }

这里其实主要是会对aop的个节点进行解析,如pointcut、advisor、aspect,因最外层节点是aspect,因此咱们进入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);
                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));
                    }
                    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();
        }
    }

想了一下,这里代码太复杂,等以后抽出整块的时间再仔细研究吧

//TODO

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOPSpring框架中的一个重要模块,它提供了一种面向切面编程的方式,可以让开发者将一些通用的、横切的关注点(如事务、安全、缓存等)从业务逻辑中剥离出来,使得业务逻辑更加清晰简洁,代码复用更加方便。 Spring AOP的实现原理主要基于Java动态代理和CGLIB动态代理两种方式,其中Java动态代理主要用于接口代理,而CGLIB动态代理则主要用于类代理。Spring AOP中的核心概念是切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)和织入(Weaving)。 在Spring AOP中,切面是一个横向的关注点,它跨越多个对象和方法,通常包含一些通用的功能,如日志记录、安全控制等。连接点则是程序中可以被切面拦截的特定点,如方法调用、异常抛出等。通知是切面在连接点执行前后所执行的动作,包括前置通知(Before)、后置通知(After)、异常通知(AfterThrowing)、返回通知(AfterReturning)和环绕通知(Around)。切点则是用来匹配连接点的规则,它可以指定哪些连接点会被切面拦截。织入则是将切面应用到目标对象中的过程,它可以在编译时、类加载时、运行时等不同的阶段进行。 Spring AOP源码解析涉及到很多细节,包括代理的生成、通知的执行、切点的匹配等,需要深入了解Spring框架的内部实现和Java的反射机制。对于初学者而言,可以先从Spring AOP的基本概念和用法入手,了解其实现原理的同时,也可以通过调试和查看源码来加深理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值