乐趣区

关于后端:Spring-AOP与AspectJ的对比及应用

1 简介

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

2 Spring AOP vs AspectJ

Spring AOP 是基于 Spring IoC 实现的,它解决大部分常见的需要,但它并不是一个残缺的 AOP 解决方案。对于非 Spring 容器治理的对象,它更没有方法了。而 AspectJ 旨在提供残缺的 AOP 计划,因而也会更简单。

2.1 织入形式

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

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

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

因而须要 AspectJ 编译器(ajc)的反对。

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

2.2 Joinpoints

因为织入形式的区别,两者所反对的 Joinpoint 也是不同的。像 final 的办法和静态方法,无奈通过动静代理来扭转,所以 Spring AOP 无奈反对。但 AspectJ 是间接在运行前织入理论的代码,所以性能会弱小很多。

Joinpoint Spring AOP Supported AspectJ Supported
Method Call No Yes
Method Execution Yes Yes
Constructor Call No Yes
Constructor Execution No Yes
Static initializer execution No Yes
Object initialization No Yes
Field reference No Yes
Field assignment No Yes
Handler execution No Yes
Advice execution No Yes

2.3 性能

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

3 Spring Boot 应用 AspectJ

因为 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 的对象

为了验证 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

配置切面如下:

@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 插件

因为是须要编译时织入代码,所以须要 maven 插件的反对:https://github.com/mojohaus/a…

配置好 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: https://github.com/LarryDpk/p…

退出移动版