SpringAOP与AspectJ的对比及应用详解

2023-02-09 12:12:27
目录
1 简介2 Spring AOP vs AspectJ2.1 织入方式2.2 Joinpoints2.3 性能3 Spring Boot使用AspectJ3.1 引入依赖3.2 被AOP的对象3.3 配置Aspect3.4 maven插件3.5 执行及测试3.6 一些遇到的错误4 总结

1>

AOP,即面向切面编程是很常用的技术,特别是在Java Web开发中。而最流行的AOP框架分别是Spring AOP和AspectJ。

2>

Spring AOP是基于Spring IoC实现的,它解决大部分常见的需求,但它并不是一个完整的AOP解决方案。对于非Spring容器管理的对象,它更没有办法了。而AspectJ旨在提供完整的AOP方案,因此也会更复杂。

2.1>

两者织入方式有极大的不同,这也是它们的本质区别,它们实现代理的方式不同。

AspectJ是在运行前织入的,分为三类:

    编译时织入编译后织入加载时织入

    因此需要AspectJ编译器(ajc)的支持。

    而Spring AOP是运行时织入的,主要使用了两种技术:JDK动态代理和CGLIB代理。对于接口使用JDK Proxy,而继承的使用CGLIB。

    2.2>

    因为织入方式的区别,两者所支持的Joinpoint也是不同的。像final的方法和静态方法,无法通过动态代理来改变,所以Spring AOP无法支持。但AspectJ是直接在运行前织入实际的代码,所以功能会强大很多。

    JoinpointSpring AOP SupportedAspectJ Supported
    Method CallNoYes
    Method ExecutionYesYes
    Constructor CallNoYes
    Constructor ExecutionNoYes
    Static initializer executionNoYes
    Object initializationNoYes
    Field referenceNoYes
    Field assignmentNoYes
    Handler executionNoYes
    Advice executionNoYes

    2.3>

    编译织入会比较运行时织入快很多,Spring AOP是使用代理模式在运行时才创建对应的代理类,效率没有AspectJ高。

    3>

    因为AspectJ比较强大,在项目中应用会更多,所以这里只介绍它与Spring Boot的集成。

    3.1>

    引入以下依赖,在Spring Boot基础上加了Lombok和aspectj:

    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>${aspectj.version}</version>
      </dependency>
      <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>${aspectj.version}</version>
      </dependency>
      <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
        <scope>provided</scope>
      </dependency>
    </dependencies>
    

    3.2>

    为了验证AOP的功能,我们添加一个TestController,它有一个处理Get请求的方法,同时会调用private的成员方法和静态方法:

    @RestController
    @RequestMapping("/test")
    @Slf4j
    public class TestController {
        @GetMapping("/hello")
        public String hello() {
            log.info("------hello() start---");
            test();
            staticTest();
            log.info("------hello() end---");
            return "Hello, pkslow.";
        }
        private void test() {
            log.info("------test() start---");
            log.info("test");
            log.info("------test() end---");
        }
        private static void staticTest() {
            log.info("------staticTest() start---");
            log.info("staticTest");
            log.info("------staticTest() end---");
        }
    }
    

    3.3>

    配置切面如下:

    @Aspect
    @Component
    @Slf4j
    //@EnableAspectJAutoProxy
    public class ControllerAspect {
        @Pointcut("execution(* com.pkslow.springboot.controller..*.*(..))")
        private void testControllerPointcut() {
        }
        @Before("testControllerPointcut()")
        public void doBefore(JoinPoint joinPoint){
            log.info("------pkslow aop doBefore start------");
            String method = joinPoint.getSignature().getName();
            String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();
            log.info("Method: {}.{}" ,declaringTypeName, method);
            log.info("------pkslow aop doBefore end------");
        }
        @Around("testControllerPointcut()")
        public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
            log.info("------pkslow aop doAround start------");
            long start = System.nanoTime();
            Object obj = joinPoint.proceed();
            long end = System.nanoTime();
            log.info("Execution Time: " + (end - start) + " ns");
            log.info("------pkslow aop doAround end------");
            return obj;
        }
    }
    

    @Pointcut定义哪些类和方法会被捕抓来代理,这里配置的是controller下的所有方法。

    @Before@Around则定义了一些处理逻辑。@Before是打印了方法名,而@Around是做了一个计时。

    注意:是不需要配置@EnableAspectJAutoProxy的。

    3.4>

    因为是需要编译时织入代码,所以需要maven插件的支持:github.com/mojohaus/as…

    配置好pom.xml文件即可。

    然后执行命令打包:

    mvn clean package

    这时会显示一些织入信息,大致如下:

    [INFO] Join point 'method-execution(java.lang.String com.pkslow.springboot.controller.TestController.hello())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:14) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
    [INFO] Join point 'method-execution(java.lang.String com.pkslow.springboot.controller.TestController.hello())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:14) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
    [INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.test())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:22) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
    [INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.test())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:22) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
    [INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.staticTest())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:28) advised by around advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
    [INFO] Join point 'method-execution(void com.pkslow.springboot.controller.TestController.staticTest())' in Type 'com.pkslow.springboot.controller.TestController' (TestController.java:28) advised by before advice from 'com.pkslow.springboot.aop.ControllerAspect' (ControllerAspect.class(from ControllerAspect.java))
    

    看到以上信息,说明成功织入了代码,具体可以查看生成的class文件。

    可以看到有许多代码都不是我们写的,而是织入生成。

    3.5>

    编译成功后,我们就执行代码。如果是通过IDEA来执行,则在运行前不需要再build了,因为已经通过maven build过了包。通过IDEA自带的编译器build,可能无法织入。或者选择ajc作为编译器。

    具体请参考:IDEA启动Springboot但AOP失效

    访问如下:

    GET http://localhost:8080/test/hello
    

    则日志如下,成功实现AOP功能:

    3.6>

    遇到错误:

    ajc Syntax error, annotations are only available if source level is 1.5 or greater
    

    需要配置插件:

    <complianceLevel>${java.version}</complianceLevel>
    <source>${java.version}</source>
    <target>${java.version}</target>
    

    可能还会遇到无法识别Lombok的错误,配置如下则解决该问题:

    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>aspectj-maven-plugin</artifactId>
      <version>1.14.0</version>
      <configuration>
        <complianceLevel>${java.version}</complianceLevel>
        <source>${java.version}</source>
        <target>${java.version}</target>
        <proc>none</proc>
        <showWeaveInfo>true</showWeaveInfo>
        <forceAjcCompile>true</forceAjcCompile>
        <sources/>
        <weaveDirectories>
          <weaveDirectory>${project.build.directory}/classes</weaveDirectory>
        </weaveDirectories>
      </configuration>
      <executions>
        <execution>
          <goals>
            <goal>compile</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
    

    4>

    AOP场景应用特别多,还是需要掌握的。

    代码请看GitHub: github.com/LarryDpk/pk…

    以上就是Spring AOP与AspectJ的对比及应用详解的详细内容,更多关于Spring AOP对比AspectJ应用的资料请关注易采站长站其它相关文章!