AOP主流是运行织入还是编译织入,有什么区别

在面向切面编程(AOP)中,织入(Weaving)是指将切面(Aspect)的代码插入到目标对象(Target Object)的过程。根据织入发生的时间,可以分为三种主要的织入方式:编译时织入(Compile-time Weaving)、加载时织入(Load-time Weaving)和运行时织入(Runtime Weaving)。

编译时织入

如 AspectJ 的编译器(ajc)来实现编译时织入。

  • 优点

    性能优化:由于织入发生在编译阶段,生成的字节码已经包含了切面逻辑,运行时不需要额外的代理机制,因此性能较高。
    透明性:对最终用户透明,生成的字节码与普通 Java 类无异。

  • 缺点
    侵入性:需要使用特殊的编译器或构建工具,对开发流程有一定侵入性。
    灵活性:一旦编译完成,切面逻辑就固定在字节码中,难以动态修改。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package com.example.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class LoggingAspect {
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}

    @Before("serviceMethods()")
    public void logBefore() {
        System.out.println("Before method execution");
    }
}
1
2
使用 AspectJ 的编译器 ajc 来编译代码:
ajc -inpath src -d bin

加载时织入

如 Aspect 和 Spring AOP 的@Aspect 和**@EnableAspectJAutoProxy**

  • 优点

    灵活性:可以在运行时动态决定是否织入切面逻辑,具有较高的灵活性。
    透明性:对最终用户透明,生成的字节码与普通 Java 类无异。

  • 缺点

    性能开销:由于织入发生在运行时,可能会引入一定的性能开销。
    复杂性:需要配置特殊的类加载器或代理机制,增加了系统的复杂性。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.example.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class ProfilingAspect {
    @Around("methodsToBeProfiled()")
    public Object profile(ProceedingJoinPoint pjp) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = pjp.proceed();
        long timeTaken = System.currentTimeMillis() - startTime;
        System.out.println("Time taken: " + timeTaken + " ms");
        return result;
    }

    @Pointcut("execution(public * com.example.service.*.*(..))")
    public void methodsToBeProfiled() {}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
配置 META-INF/aop.xml

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
    <weaver>
        <include within="com.example..*"/>
    </weaver>
    <aspects>
        <aspect name="com.example.aspect.ProfilingAspect"/>
    </aspects>
</aspectj>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
在 Spring 配置文件中启用加载时织入:

<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"
       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">
    <context:load-time-weaver/>
</beans>

运行时织入

Spring 的 AOP 框架默认使用运行时织入,通过动态代理机制来实现。

AspectJ也支持运行时织入,通过动态代理机制来实现。

  • 优点

    灵活性:可以在运行时动态决定是否织入切面逻辑,具有最高的灵活性。
    动态性:可以在运行时动态修改切面逻辑,适应动态变化的需求。

  • 缺点

    性能开销:由于织入发生在运行时,可能会引入较大的性能开销。
    复杂性:需要使用动态代理机制,增加了系统的复杂性。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package com.example.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class LoggingAspect {
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}

    @Before("serviceMethods()")
    public void logBefore() {
        System.out.println("Before method execution");
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
在 Spring 配置文件中启用 AOP:

<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">
    <aop:aspectj-autoproxy/>
    <bean id="loggingAspect" class="com.example.aspect.LoggingAspect"/>
</beans>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
使用 @EnableAspectJAutoProxy 注解

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
    @Bean
    public LoggingAspect loggingAspect() {
        return new LoggingAspect();
    }
}

在实际开发中,运行时织入 是最主流的选择,尤其是在使用 Spring 框架时。Spring AOP 默认使用运行时织入,通过动态代理机制来实现 AOP 功能。这种方式具有较高的灵活性和动态性,能够适应大多数应用场景。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计