精华内容
下载资源
问答
  • aspectj原理 对于Java项目,有时我将AspectJ用于各种任务,但是对于Android项目,我最初决定不使用它。 有多种原因: 与通用Java项目相比,Android项目的结构及其构建过程已经更加复杂和缓慢。 对于Android开发...
    aspectj原理

    aspectj原理

    对于Java项目,有时我将AspectJ用于各种任务,但是对于Android项目,我最初决定不使用它。

    有多种原因:

    • 与通用Java项目相比,Android项目的结构及其构建过程已经更加复杂和缓慢。
    • 对于Android开发, AspectJ仅支持编译时编织,这使它的用处不大。
    • Android Studio IDE不支持AspectJ。

    但是,最终我在测试方面发现了某些方面的用途。 我决定尝试使用AspectJ的另一个原因是Gradle提供了Android AspectJ插件。 它们使我将AspectJ集成到Android构建过程中变得更加容易,我使用了这一点

    使用方面,我可以在特定测试需要时将测试代码编织到应用程序代码中。 然后,当不再需要时,我将无需进行方面编织就进行常规构建,并且测试代码将完全隔离在方面文件中。我发现这可以很好地替代测试期间有时发生的情况:

    • 为特定测试添加测试代码,然后必须将其删除或在以后将其注释掉
    • 添加临时日志记录以查找错误,分析一些代码等

    一些例子有什么好处

    没有AspectJ:

    在测试Crashlytics时Firebase文档建议添加带有使应用程序崩溃的点击处理程序的临时按钮。

    使用AspectJ:

    相反,我可以将引发崩溃的代码编织到我喜欢的任何现有应用程序事件中,例如按钮单击,菜单单击等。

    没有AspectJ:

    编写一些临时测试代码以引发异常,以查看应用程序是否正确处理了它。

    使用AspectJ:

    有一个方面会从我选择的任何方法中引发异常。 我也可以使用切入点建议多种方法,以便可以从多个位置抛出测试异常。

    (例如,检查Google Play服务是否可用。)

    没有AspectJ:

    编写一些测试代码以模拟您要测试的外部服务的响应。 例如,当检查Google Play服务是否可用时,您可能需要测试您的应用是否正确处理了不成功的结果代码,例如SERVICE_MISSING,SERVICE_VERSION_UPDATE_REQUIRED等。

    使用AspectJ:

    编写一个方面,以建议您在哪里调用外部服务,并让其返回要测试的结果。 由您决定是否要使用proceed(..)建议中实际调用外部服务,还是让建议返回所需的结果。

    AspectJ的最常见用途之一是方法跟踪,这对于分析应用程序或尝试跟踪难以发现的错误很有用。

    没有AspectJ:

    将临时跟踪日志记录添加到您可能需要概要分析或导致错误的应用程序代码中。

    使用AspectJ:

    通过调整跟踪方面的切入点,启用对方法调用的跟踪并记录我想要的任何或所有方法调用。 例如,编写切入点来建议类中的所有方法既快速又容易。

    周围有许多AspectJ跟踪方面的示例,或者您可以使用android工具通过检测生成跟踪日志

    AspectJ示例

    让我们从一个常见的场景开始,一个服务类带有一个进行网络调用的方法(大概是要获取一些数据并对其应用业务逻辑)。 此服务方法将由应用程序中的活动或片段调用。请注意,此示例假定您已经熟悉如何使用AspectJ本机语法进行编程。

     public class BusinessService { 
      public Result serviceNetworkCall() {
    
        return ... // return some data from a network call
    
      }
     }
    

    我想测试活动/片段是否正确处理了调用service方法时出现错误的情况。 这是一个方面,可以用来模拟方法调用上引发的异常。

     aspect TestExceptionInjector {
    
      pointcut injectTestException(): target(BusinessService) && execution( public Result *(..)); 
      Result around(): injectTestException() {
    
        // can allow the adviced method to run first before the exception is thrown by calling <em>proceed()</em> first, in case we want some side effects to occur
    
        // simulate an exception being thrown from the method(s) adviced
    
        throw new RuntimeException( "TEST" );
    
      }
     }
    

    在这方面,切入点将建议服务方法,并引发异常而不是其正常执行。 然后,我们可以运行该应用程序,以检查调用service方法的Activity / Fragment是否正确处理了此错误。

    我还可以在服务类中使用React性库(例如RxJava)的情况下执行此操作。

     public class BusinessServiceRx { 
      public Observable<Result> serviceNetworkCallRx() {
    
        return ... // return some data from a network call
    
      }
    
      public Observable<Result> anotherServiceNetworkCallRx() {
    
        return ... // return some data from a network call
    
      }
     }
     privileged aspect TestExceptionInjectorRx {
    
      pointcut injectTestExceptionRx(): target(BusinessServiceRx) && execution( public Observable<Result> *(..)); 
      Observable<Result> around(): injectTestExceptionRx() {
    
        // simulate an exception being thrown downstream from the method(s) adviced
    
        return Observable.error( new RuntimeException( "TEST" ));
    
      }
     }
    

    此示例还显示,方面中的切入点可以建议多种服务方法。

    确定何时构建方面(或不构建)

    接下来,我们需要将示例中的方面编织到应用程序代码中,然后找到某种方法来确定何时使用要测试的方面进行构建以及何时仅进行常规构建。 在本文的结尾部分,我将展示用作此方法之一的工作流程。

    仅用于测试?

    正如我已经提到的,当使用方面时,测试/记录代码驻留在与应用程序代码隔离的方面文件中,并且永远不要进入发行版。 但是,当我要再次运行测试时,它始终可用,并且无需担心测试完成后会忘记删除临时测试代码。

    仅将AOP(面向方面​​的编程)用于测试是我个人的偏爱。 当然,您可以在开发过程中轻松使用AspectJ,并将方面包含在应用程序代码中。

    翻译自: https://www.javacodegeeks.com/2020/05/is-aspectj-still-useful-for-android-part-1.html

    aspectj原理

    展开全文
  • Spring AOP原理以及原生AspectJ原理探究

    千次阅读 2018-12-13 15:50:14
    前两天看了一些关于spring AOP以及AspectJ的文章,但是总是感觉非常的乱,有的说spring aop跟aspectj相互独立,有的说spring aop依赖于aspectj,有的甚至直接把两者混为一谈。甚至很多实用Spring Aop的朋友并不知道...

    前言

    前两天看了一些关于spring AOP以及AspectJ的文章,但是总是感觉非常的乱,有的说spring aop跟aspectj相互独立,有的说spring aop依赖于aspectj,有的甚至直接把两者混为一谈。甚至很多实用Spring Aop的朋友并不知道是依赖于AspectJ来开启AspectJ模式,进而简化配置的。

    为什么用AspectJ

    为什么用AspectJ,我的理解是两个字”方便“。我们知道面向切面编程(Aspect Oriented Programming)有诸多好处,但是在使用AspectJ之前我们一般是怎么编写切面的呢?我想一般来说应该是三种吧:静态代理,jdk动态代理,cglib动态代理。但是我们知道,静态代理的重用性太差,一个代理不能同时代理多种类;动态代理可以做到代理的重用,但是即使这样,他们调用起来还是比较麻烦,除了写切面代码以外,我们还需要将代理类耦合进被代理类的调用阶段,在创建被代理类的时候都要先创建代理类,再用代理类去创建被代理类,这就稍微有点麻烦了。比如我们想在现有的某个项目里统一新加入一些切面,这时候就需要创建切面并且侵入原有代码,在创建对象的时候添加代理,还是挺麻烦的。
    说到底,这种麻烦出现的本质原因是,代理模式并没有做到切面与业务代码的解耦。虽然将切面的逻辑独立进了代理类,但是决定是否使用切面的权利仍然在业务代码中。这才导致了上面这种麻烦。
    而Spring AOP里面的代理实现方式就是spring用代理类包裹切面,把他们织入到Spring管理的bean中。也就是说代理类伪装成目标类,它会截取对目标类中方法的调用,让调用者对目标类的调用都先变成调用伪装类,伪装类中就先执行了切面,再把调用转发给真正的目标bean。
    Spring的这个处理确实让动态代理简洁了很多,但是,AspectJ框架对于代理的处理更加简洁,我们无需关注切点和切面(即被代理对象),只需关注我们需要织入逻辑就ok。这也是为什么Spring引用他的原因。

    实现原理

    关于静态代理和动态代理的实现方式及原理,不了解的或者有兴趣的可以点我
    而AspectJ的实现原理其实比较简单,首先是使用切面语法,这套东西做到了将决定是否使用切面的权利还给了切面。在写切面的时候就可以决定哪些类的哪些方法会被代理,从而从逻辑上不需要侵入业务代码。其次,最重要的就是他的织入工具aspectjweaver,他的作用就是在编译期(或者类加载期)将切面代码通过某种形式插入到业务代码中,也就是我们生成的.class文件其实就是相当于我们动态代理中的代理对象了

    AspectJ相关jar包

    AspectJ其实是eclipse基金会的一个项目,官网就在eclipse官网里。官网里提供了一个aspectJ.jar的下载链接,但其实这个链接只是一个安装包,把安装包里的东西解压后就是一个文档+脚本+jar包的程序包,其中比较重要的是如下部分:

    myths@pc:~/aspectj1.8$ tree bin/ lib/
    bin/
    ├── aj
    ├── aj5
    ├── ajbrowser
    ├── ajc
    └── ajdoc
    lib/
    ├── aspectjrt.jar
    ├── aspectjtools.jar
    ├── aspectjweaver.jar
    └── org.aspectj.matcher.jar
    

    当然,这些jar包并不总是需要从官网下载,很多情况下在maven等中心库中直接找会更方便。
    这当中重点的文件是四个jar包中的前三个,bin文件夹中的脚本其实都是调用这些jar包的命令。

    • aspectjrt.jar包主要是提供运行时的一些注解,静态方法等等东西,通常我们要使用aspectJ的时候都要使用这个包。
    • aspectjtools.jar包主要是提供赫赫有名的ajc编译器,可以在编译期将将java文件或者class文件或者aspect文件定义的切面织入到业务代码中。通常这个东西会被封装进各种IDE插件或者自动化插件中。
    • aspectjweaverjar包主要是提供了一个java agent用于在类加载期间织入切面(Load time weaving)。并且提供了对切面语法的相关处理等基础方法,供ajc使用或者供第三方开发使用。这个包一般我们不需要显式引用,除非需要使用LTW。

    上面的说明其实也就指出了aspectJ的几种标准的使用方法(参考文档):

    • 编译时织入,利用ajc编译器替代javac编译器,直接将源文件(java或者aspect文件)编译成class文件并将切面织入进代码。
    • 编译后织入,利用ajc编译器向javac编译期编译后的class文件或jar文件织入切面代码。
    • 加载时织入,不使用ajc编译器,利用aspectjweaver.jar工具,使用java agent代理在类加载期将切面织入进代码。

    示例

    首先,引入AspectJ依赖:
    在这里插入图片描述
    创建App.java文件:

    package com.mythsman.test;
    
    public class App {
    
        public void say() {
            System.out.println("App say");
        }
    
        public static void main(String[] args) {
            App app = new App();
            app.say();
        }
    }
    

    建切面类AnnoAspect.java:

    package com.mythsman.test;
    
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    
    @Aspect
    public class AnnoAspect {
    
        @Pointcut("execution(* com.mythsman.test.App.say(..))")
        public void jointPoint() {
        }
    
        @Before("jointPoint()")
        public void before() {
            System.out.println("AnnoAspect before say");
        }
    
    
        @After("jointPoint()")
        public void after() {
            System.out.println("AnnoAspect after say");
        }
    
    }
    

    我们编译之后,可以看见App.class如下:

    package com.mythsman.test;
    
    import java.io.PrintStream;
    
    public class App
    {
      public void say()
      {
        try
        {
          AnnoAspect.aspectOf().before();System.out.println("App say");
        }
        catch (Throwable localThrowable)
        {
          AnnoAspect.aspectOf().after();throw localThrowable;
        }
        AnnoAspect.aspectOf().after();
      }
      
      public static void main(String[] args)
      {
        App app = new App();
        app.say();
      }
    }
    

    可以看见,代码被织入到了App.class中。正如我们上面所说,aspectJ的实现和Spring AOP是不一样的,他是直接将代码织入到编译后的class文件中。这里,也引申出一个新的问题,那Spring AOP引用aspectJ这个框架,是不是表示Spring现在也是通过织入的方式实现AOP的呢?其实spring aop还是通过动态代理来实现aop的,虽然spring aop采用了aspectj语法来定义切面,但是在实现切面逻辑的时候还是采用CGLIB来进行动态代理的方法。

    展开全文
  • 去官网下载aspectj的jar包,关注下自己使用的jdk的版本https://www.eclipse.org/aspectj/downloads.php下载成功以后,执行命令java -jar aspectj-1.8.14.jar会弹出安装界面安装成功以后,还需要添加环境变量,以mac...
    • 去官网下载aspectj的jar包,关注下自己使用的jdk的版本
    https://www.eclipse.org/aspectj/downloads.php
    • 下载成功以后,执行命令
    java -jar aspectj-1.8.14.jar
    • 会弹出安装界面
    e400ece97146a92226e05e620198234d.png
    • 安装成功以后,还需要添加环境变量,以mac为例
    vim ./.bash_profileexport PATH=${JAVA_HOME}/bin:$PATH:/Users/local/aspectj1.8/binexport CLASSPATH=${CLASSPATH}:/Users/local/aspectj1.8/lib/aspectjrt.jarsource ./.bash_profile
    • 配置完成后,输入命令ajc,确定下是否安装成功,成功的话会提示相应的版本
    153812eb093be9db8377d70f762faf4b.png
    • 首先编写一个简单的 Java 类,这个 Java 类用于模拟一个业务组件
    public class Hello {    public void sayHello() {        System.out.println("Hello, AspectJ!");    }    public static void main(String[] args) {        Hello hello = new Hello();        hello.sayHello();    }}
    • 假设现在客户需要在执行 sayHello() 方法之前启动事务,当该方法执行结束时关闭事务,在传统编程模式下,我们必须手动修改 sayHello() 方法
    • 如果改为使用 AspectJ,则可以无须修改上面的 sayHello() 方法,使用AspectJ来创建一个特殊的类
    public aspect TxAspect {    void around():call(void Hello.sayHello()){        System.out.println("开始事务...");        proceed();        System.out.println("事务结束...");    }}
    • TxAspect 不是一个 Java 类, aspect 也不是 Java 支持的关键字,它只是 AspectJ 才能识别的关键字,上面的代码中 proceed() 代表回调原来的 sayHello() 方法
    • 接下来执行如下命令进行编译:
    ajc -d . Hello.java TxAspect.aj
    • 然后会输出结果
    开始事务...Hello AspectJ!事务结束...
    • 从上面运行结果来看,我们完全可以不对 Hello.java 类进行任何修改,同时又可以满足客户的需求,上面程序只是在控制台打印”开始事务 …”、”结束事务 …”来模拟了事务操作,实际上我们可用实际的事务操作代码来代替这两行简单的语句,这就可以满足客户需求了
    • 通过使用 AspectJ 提供的 AOP 支持,我们可以为 sayHello() 方法不断增加新功能。
    • 为什么在对 Hello 类没有任何修改的前提下,而 Hello 类能不断地、动态增加新功能,这看上去并不符合 Java 基本语法规则啊。实际上我们可以使用 Java 的反编译工具来反编译前面程序生成的 Hello.class 文件,发现 Hello.class 文件的代码如下:
    import org.aspectj.runtime.internal.AroundClosure;public class Hello {    public Hello() {    }    public void sayHello() {        System.out.println("Hello, AspectJ!");    }    public static void main(String[] args) {        Hello hello = new Hello();        sayHello_aroundBody1$advice(hello, TxAspect.aspectOf(), (AroundClosure)null);    }}
    • 该 Hello.class 里新增了很多内容,这表明 AspectJ 在编译时”自动”编译得到了一个新类,这个新类增强了原有的 Hello.java 类的功能,因此 AspectJ 通常被称为编译时增强的 AOP 框架。

    都看到这了,你是最棒的

    c9ba6e2e9b13a6c0f1ce8036b2a79efb.gif
    展开全文
  • 是非常复杂的,其实现过程主要包含xml标签的解析,切面表达式的解析,判断bean是否需要应用切面逻辑,以及使用Jdk代理或者是Cglib代理生成代理类,本文主要讲解Xml标签的解析的实现原理。关于Spring Aop的实现,由于...

    64b5a14a662f65f1687423d222ee1225.png

    作者:爱宝贝丶

    来源:https://dwz.cn/HIiwPh8S

    对于Spring Aop的实现,是非常复杂的,其实现过程主要包含xml标签的解析,切面表达式的解析,判断bean是否需要应用切面逻辑,以及使用Jdk代理或者是Cglib代理生成代理类,本文主要讲解Xml标签的解析的实现原理。

           关于Spring Aop的实现,由于其是使用自定义标签进行驱动的,因而读者朋友如果对Spring如何实现自定义标签比较熟悉,那么可以继续往下阅读。

    Aop使用示例

    首先我们声明了一个切面类如下:

    @Aspect
    public class DogAspect {
    @Around("execution(public void com.business.Dog.*(..))")
    public Object aspect(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("before run.");
    Object result = joinPoint.proceed();
    System.out.println("after run.");
    return result;
    }
    }

           该切面类主要用于环绕com.business.Dog类中的public类型的,并且返回值是void的所有方法,下面我们就在com.business包中声明一个Dog类如下:

    public class Dog {
    public void run() {
    System.out.println("Tidy is running.");
    }
    }

           这里切面类和目标类都已经声明完成,但如果不将其加入Spring容器中,其是不会工作的,加入容器的方式非常简单,下面就是一种方式:

    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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <bean id="dog" class="com.business.Dog"/>

    <bean id="aspect" class="com.business.DogAspect"/>

    <aop:aspectj-autoproxy/>
    beans>

           这里需要说明的是,将DogAspect声明为一个bean并不能使其工作,因为其也仅仅只是一个bean而已,要使其工作还需要使用上面的标签实现切面的自动装配。下面使我们运行整个程序的驱动类:

    public class DogApp {
    public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    Dog dog = context.getBean(Dog.class);
    dog.run();
    }
    }

           执行结果如下:

    before run.
    Tidy is running.
    after run.

           可以看到,我们在驱动类中获取的是Dog的实例,并且运行其run()方法,但是最终的运行结果中也运行了切面类中的环绕逻辑。

    实现原理

           根据前面对Spring自定义标签使用的讲解,我们知道这里就是一个自定义标签,并且该标签会在相应jar包的META-INF目录下有一个spring.handlers文件,该文件中声明了解析该标签的类。通过查看该类我们得到如下配置:

    http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

           这里我们打开AopNamespaceHandler,其实现如下:

    public class AopNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
    registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
    registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
    registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
    registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
    }
    }

           可以看到,我们需要解析的标签解析器在这个类中进行了注册,即AspectJAutoProxyBeanDefinitionParser,打开这个类其主要实现如下:

    class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {

    // 解析标签的时候将会执行的方法
    @Override
    @Nullable
    public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 注册一个类型为AnnotationAwareAspectJAutoProxyCreator的bean到Spring容器中
    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    // 通过读取配置文件对扩展相关属性
    extendBeanDefinition(element, parserContext);
    return null;
    }

    private void extendBeanDefinition(Element element, ParserContext parserContext) {
    // 获取前面注册的AnnotationAwareAspectJAutoProxyCreator对应的BeanDefinition
    BeanDefinition beanDef =
    parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
    // 解析当前标签的子标签
    if (element.hasChildNodes()) {
    addIncludePatterns(element, parserContext, beanDef);
    }
    }

    // 解析子标签中的name属性,其可以有多个,这个name属性最终会被添加到
    // AnnotationAwareAspectJAutoProxyCreator的includePatterns属性中,
    // Spring在判断一个类是否需要进行代理的时候会判断当前bean的名称是否与includePatterns中的
    // 正则表达式相匹配,如果不匹配,则不进行代理
    private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) {
    ManagedList includePatterns = new ManagedList<>();
    NodeList childNodes = element.getChildNodes();for (int i = 0; i < childNodes.getLength(); i++) {
    Node node = childNodes.item(i);if (node instanceof Element) {
    Element includeElement = (Element) node;// 解析子标签中的name属性
    TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
    valueHolder.setSource(parserContext.extractSource(includeElement));
    includePatterns.add(valueHolder);
    }
    }// 将解析到的name属性设置到AnnotationAwareAspectJAutoProxyCreator// 的includePatterns属性中if (!includePatterns.isEmpty()) {
    includePatterns.setSource(parserContext.extractSource(element));
    beanDef.getPropertyValues().add("includePatterns", includePatterns);
    }
    }
    }

           这里我们继续看AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);方法,该方法的实现如下:

    public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
    ParserContext parserContext, Element sourceElement)
    {
    // 注册AnnotationAwareAspectJAutoProxyCreator的BeanDefinition
    BeanDefinition beanDefinition =
    AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
    parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    // 解析标签中的proxy-target-class和expose-proxy属性值,
    // proxy-target-class主要控制是使用Jdk代理还是Cglib代理实现,expose-proxy用于控制
    // 是否将生成的代理类的实例防御AopContext中,并且暴露给相关子类使用
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    // 将注册的BeanDefinition封装到BeanComponentDefinition中
    registerComponentIfNecessary(beanDefinition, parserContext);
    }

    private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry,
    @Nullable Element sourceElement)
    {
    if (sourceElement != null) {
    // 解析标签中的proxy-target-class属性值
    boolean proxyTargetClass =
    Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
    if (proxyTargetClass) {
    // 将解析得到的proxy-target-class属性值设置到上面生成的
    // AnnotationAwareAspectJAutoProxyCreator的BeanDefinition的proxyTargetClass
    // 属性中
    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
    }
    // 解析标签中的expose-proxy属性值
    boolean exposeProxy =
    Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
    if (exposeProxy) {
    // 将解析得到的expose-proxy属性值设置到
    // AnnotationAwareAspectJAutoProxyCreator的exposeProxy属性中
    AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
    }
    }
    }

    private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition,
    ParserContext parserContext)
    {
    // 如果生成的AnnotationAwareAspectJAutoProxyCreator的BeanDefinition成功,则将其封装到
    // BeanComponentDefinition中,并且将其添加到ParserContext中
    if (beanDefinition != null) {
    BeanComponentDefinition componentDefinition =
    new BeanComponentDefinition(beanDefinition,
    AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
    parserContext.registerComponent(componentDefinition);
    }
    }

           这里可以看到AnnotationAwareAspectJAutoProxyCreatorBeanDefinition在第一步进行了注册,然后读取标签中的proxy-target-class和expose-proxy属性,并且将属性值设置到生成的BeanDefinition中。最后将生成的BeanDefinition注册到ParserContext中。这里我们继续看AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement))方法,其实现如下:

    @Nullable
    public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
    @Nullable Object source)
    {
    // 注册AnnotationAwareAspectJAutoProxyCreator类型的BeanDefinition
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class,
    registry, source);
    }

    @Nullable
    private static BeanDefinition registerOrEscalateApcAsRequired(Class> cls,
    BeanDefinitionRegistry registry, @Nullable Object source)
    {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

    // 如果已经注册过AnnotationAwareAspectJAutoProxyCreator的Definition,如果其
    // 和当前将要注册的BeanDefinition是同一个类型,则不再注册,如果不同,则判断其优先级比
    // 当前将要注册的BeanDefinition要高,则将其类名设置为当前要注册的BeanDefinition的名称
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
    BeanDefinition apcDefinition =
    registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
    if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
    int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
    int requiredPriority = findPriorityForClass(cls);
    if (currentPriority < requiredPriority) {
    apcDefinition.setBeanClassName(cls.getName());
    }
    }
    return null;
    }

    // 如果不存在已经注册的Aop的bean,则生成一个,并且设置其执行优先级为最高优先级,并且标识
    // 该bean为Spring的系统Bean,设置完之后则对该bean进行注册
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
    }

           可以看到,在真正生成AnnotationAwareAspectJAutoProxyCreatorBeanDefinition的时候,首先会判断是否已经生成过该bean,这里不会将已经生成的bean进行覆盖;如果没有生成该bean,则创建一个并进行注册。这里需要说明的是,Spring注册该bean的时候使用的order是Ordered.HIGHEST_PRECEDENCE,这么设置的原因在于Spring使用该bean进行切面逻辑的织入,因而这个bean必须在所有用户自定义的bean实例化之前进行实例化,而用户自定义的bean的实例化优先级是比较低的,这样才能实现织入代理逻辑的功能。

    小结

           本文首先使用一个简单的示例展示了Spring Aop的使用方式,然后对标签中的解析过程进行了讲解。可以看到,该标签的解析过程最终是生成了一个AnnotationAwareAspectJAutoProxyCreatorBeanDefinition


    觉得有收获,诚邀关注、点赞、转发

    813ddf1072ce535737b85c45a920dca9.png

    展开全文
  • AspectJ原理与使用

    千次阅读 2017-01-19 08:19:17
    http://yangjunfeng.iteye.com/blog/398028
  • 本文主要介绍Spring Secutity的实现原理,并基于Spring Secutity设计基于RBAC的权限系统。一、技术选型为何把Spring Secutity作为权限系统的技术选型,主要考虑了以下几个方面:数据鉴权的能力:Spring Secutity支持...
  • 本篇文章是网易云信研发工程师对Spring事务实现原理及实现的研究和总结,分享给大家,希望和大家共同探讨。事务是一个由有限操作集合组成的逻辑单元。事务操作包含两个目的,数据一致以及操作隔离。数据一致是指事务...
  • AspectJ 使用及原理

    千次阅读 2019-08-01 15:08:57
    AspectJ 使用及原理一.简介二.原理三.使用(一)基本概念1.连接点(JoinPoint)2.切点(PointCut)3.插入逻辑(Advice)4.切面(Aspect)(二)类型匹配表达式1.注解2.修饰符3.返回值4.类5.方法名6.方法参数7.组合使用(三)切入点...
  • 今天试着用一下Spring + AspectJ的AOP,遇到了点问题,没有拦截到Bean的方法,比如controller里面的方法。网上搜索了大堆东西,都没有什么用了,而且看Spring官网的Reference也没有看出个什么问题。这种情况下只好去...
  • AspectJ框架实现原理

    万次阅读 2014-07-14 15:18:03
    is AspectJAspectJ是一个代码生成工具(Code Generator)。 AspectJ语法就是用来定义代码生成规则的语法。您如果使用过Java Compiler Compiler (JavaCC),您会发现,两者的代码生成规
  • Spring AOP & AspectJ原理探析

    千次阅读 2017-12-09 00:54:36
    鉴于CSDN对**版权保护的不作为**以及落后的运营手段,本博客将于近期关闭,并清空全部文章。 原有文章将会经过再次的校对、整理,转移至本人在**简书**的[博客空间](https://www.jianshu.com/u/3ec23ef9a408)... ...
  • AOP之AspectJ 技术原理详解及实战总结

    万次阅读 2018-03-21 18:14:05
    二Android中使用AspectJ 1 Gradle 配置示 2 基本概念 21 切面Aspect 22 连接点JoinPoint 23 切点PointCut 24 通知Advise 3 执原 31 BeforeAfterAfterThrowing插入示意图...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 650
精华内容 260
关键字:

aspectj原理