在面向切面编程(AOP)中,织入(Weaving)是指将切面(Aspect)的代码插入到目标对象(Target Object)的过程。根据织入发生的时间,可以分为三种主要的织入方式:编译时织入(Compile-time Weaving)、加载时织入(Load-time Weaving)和运行时织入(Runtime Weaving)。
编译时织入
如 AspectJ 的编译器(ajc)来实现编译时织入。
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**
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 功能。这种方式具有较高的灵活性和动态性,能够适应大多数应用场景。