@boothsun
2023-01-29T19:18:21.000000Z
字数 3350
阅读 1055
Spring
内容总结自以下优秀博文:
1. 关于 Spring AOP (AspectJ) 你该知晓的一切
2. AspectJ 编译时织入(Compile Time Weaving, CTW)
AOP(Aspect-Oriented Programming),即面向切面编程,它利用一种称为“横切”的技术,将影响了多个类的公共行为封装到一个可重用的模块,并将其命名为“Aspect”(即切面)。所谓“切面”,简单来说就是那些与业务无关,却为多个业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之前的耦合度。
常见的应用场景就是 日志打印收集,权限校验,事务管理,性能监测等公共业务逻辑。这些业务并不是主流程的逻辑,但是确实是不可或缺的逻辑。通常,对于这些代码我们并不希望他们侵入到核心代码逻辑中,更希望他们可以在核心代码逻辑外围,实现自动的执行。
AspectJ是以一种编译时静态织入的方式来实现AOP功能的,所谓编译时静态织入就是指编译期间字节码文件改写,根据切点规则将通知写到代码的指定位置。AspectJ会要求使用自己的特定编译器编译,然后在编译期间,AspectJ会根据定义的切点规则,将通知代码加到业务代码的前后,所以这种形式在源代码上看上去是隔离开的,但是到了字节码文件时又是堆砌在一起。
// 1. AuthCheck
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthCheck {
}
// 2. SampleService
package org.opoo.samples.aspectj;
public interface SampleService {
int add(int x, int y);
String getPassword(String username);
}
// 3. SampleServiceImpl
package org.opoo.samples.aspectj;
public class SampleServiceImpl implements SampleService {
public int add(int x, int y) {
return x + y;
}
/**
* 带自定义Annotation的方法。
*/
@AuthCheck
public String getPassword(String username) {
return "password";
}
}
// 4. SampleAspect
package org.opoo.samples.aspectj;
import org.aspectj.lang.reflect.MethodSignature;
import java.lang.reflect.Method;
public aspect SampleAspect{
/**
* 切点:SampleService继承树中所有 public 且以 add 开头的方法。SampleServiceImpl#add(int,int)方法满足这个条件。
*/
pointcut serviceAddMethods(): execution(public * org.opoo.samples.aspectj.SampleService+.add*(..));
/**
* 切点:SampleService继承树中所有标注了AuthCheck的方法。
*/
public pointcut serviceAuthCheckAnnotatedMethods(): execution(* org.opoo.samples.aspectj.SampleService+.*(..)) && @annotation(AuthCheck);
/**
* 通知
*/
Object around(): serviceAddMethods(){
Object oldValue = proceed();
System.out.println("原值是:" + oldValue);
return Integer.MIN_VALUE;
}
before(): serviceAuthCheckAnnotatedMethods(){
if(1==1){//权限检查代码
throw new IllegalStateException("权限不足");
}
}
/**
* 切入点:SampleService继承树中所有 public 的方法。
*/
public pointcut serviceAllPublicMethods(): execution(public * org.opoo.samples.aspectj.SampleService+.*(..));
after(): serviceAllPublicMethods(){
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
Method method = methodSignature.getMethod();
System.out.println("[LOG] 方法被调用了: " + method);
}
}
// 运行结果:
原值是:30
[LOG] 方法被调用了: public int org.opoo.samples.aspectj.SampleServiceImpl.add(int,int)
现值是:-2147483648
[LOG] 方法被调用了: public java.lang.String org.opoo.samples.aspectj.SampleServiceImpl.getPassword(java.lang.String)
AspectJ是一个Java实现的AOP框架,它能够对Java代码进行AOP编译(一般在编译期进行),让Java代码具有AspectJ的AOP功能(当然需要特殊的编译器),可以这样说AspectJ是目前实现AOP框架中最成熟的,功能最丰富的语言,更幸运的是,AspectJ与Java程序完全兼容,几乎是无缝关联,因此对于有Java编程基础的工程师,上手和使用都非常容易。在案例中,我们使用aspect关键字定义了一个类,这个类就是一个切面,它可以是单独的日志切面(功能),也可以是权限切面或者其他,在切面内部使用了pointcut定义了两个切点,一个用于权限验证,一个用于日志记录,而所谓的切点就是那些需要应用切面的方法,如需要在sayHello方法执行前后进行权限验证和日志记录,那么就需要捕捉该方法,而pointcut就是定义这些需要捕捉的方法(常常是不止一个方法的),这些方法也称为目标方法,最后还定义了两个通知,通知就是那些需要在目标方法前后执行的函数,如before()即前置通知在目标方法之前执行,即在sayHello()方法执行前进行权限验证,另一个是after()即后置通知,在sayHello()之后执行,如进行日志记录。到这里也就可以确定,切面就是切点和通知的组合体,组成一个单独的结构供后续使用。