AOP与OOP
-
OOP:面向对象编程。关注于数据和行为的封装,通过类和对象来组织代码。用于构建系统的主体结构,定义数据结构和操作这些数据的方法。
-
AOP:面向切面编程。专注于将横切关注点(如日志记录、事务管理、安全性等)与业务逻辑分离,从而提高模块化和代码重用。通常用于处理那些在多个对象或类中重复出现的逻辑,如日志记录、性能统计、事务管理等。
代理模式回顾
编写代码的原则之二:
职责清晰:一个类只负责一件事;
易于测试:一次只测一个功能。
代理(Proxy)通过封装一个已有接口,并向调用方返回相同的接口类型,能让调用方在不改变任何代码的前提下增强某些功能(例如,鉴权、延迟加载、连接池复用等)。AOP本质上只是一种代理模式的实现方式
代理示例代码
public class AProxy implements A {
private A a;
public AProxy(A a) {
this.a = a;
}
public void a() {
if (getCurrentUser().isRoot()) {
this.a.a();
} else {
throw new SecurityException("Forbidden");
}
}
}
通过代理可以获得更清晰,更简洁的代码
-
A接口:只定义接口;
-
ABusiness类:只实现A接口的业务逻辑;
-
APermissionProxy类:只实现A接口的权限检查代理。
Java中的AOP织入方式
-
编译期:在编译时,由编译器把切面调用编译进字节码,这种方式需要定义新的关键字并扩展编译器,AspectJ就扩展了Java编译器,使用关键字aspect来实现织入;
-
类加载器:在目标类被装载到JVM时,通过一个特殊的类加载器,对目标类的字节码重新“增强”;
-
运行期:目标对象和切面都是普通Java类,通过JVM的动态代理功能或者第三方库实现运行期动态织入。(Spring的AOP实现就是基于JVM的动态代理)
AOP常见概念
-
Aspect:切面,即一个横跨多个核心逻辑的功能,或者称之为系统关注点;
-
Joinpoint:连接点,即定义在应用程序流程的何处插入切面的执行;
-
Pointcut:切入点,即一组连接点的集合;
-
Advice:增强,指特定连接点上执行的动作;
-
Introduction:引介,指为一个已有的Java对象动态地增加新的接口;
-
Weaving:织入,指将切面整合到程序的执行流程中;
-
Interceptor:拦截器,是一种实现增强的方式;
-
Target Object:目标对象,即真正执行业务的核心逻辑对象;
-
AOP Proxy:AOP代理,是客户端持有的增强后的对象引用。
使用AspectJ实现AOP
引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.1.9</version>
</dependency>
创建一个类并定义执行方法
@Aspect
@Component
public class LoggingAspect {
@Before("execution(public * com.zyj.service.UserService.*(..))")
public void doAccessCheck() {
System.err.println("[Before] do access check...");
}
// 在执行MailService的每个方法前后执行:
@Around("execution(public * com.zyj.service.MailService.*(..))")
public Object doLogging(ProceedingJoinPoint pjp) throws Throwable {
System.err.println("[Around] start " + pjp.getSignature());
Object retVal = pjp.proceed();
System.err.println("[Around] done " + pjp.getSignature());
return retVal;
}
}
给配置类加上@EnableAspectJAutoProxy注解
通过这个注解,Spring的IoC容器会自动查找带有
@Aspect
的Bean,然后根据每个方法的@Before、@Around等注解把AOP注入到特定的Bean中。
使用注解装配AOP
使用AspectJ实现AOP存在无区别覆盖,容易导致不恰当的覆盖范围,新增的Bean若不清楚当前装配规则容易被强迫装配
通过注解实现AOP,可以清楚的看到Bean中的哪些方法被装配了