什么是Spring AOP?
是一种基于动态代理技术的面向切面的编程思想。
动态代理是怎样实现的?
利用反射或者字节码操作,在运行的时候生成代理类和对象,实现在不修改原始类的情况下增强功能。必要的参数有 需要代理的类或接口、执行的目标方法、执行方法所需要的入参。
第一步:根据传入的对象反射出一个代理对象
第二步:根据需要执行的目标方法入参定位到需要增强的方法
第三步:把方法入参交给代理对象需要执行的目标方法,执行增强方法,返回出参
JDK动态代理和CJLB动态代理的区别是什么?
JDK动态代理是基于接口的代理,而CGLIB动态代理是基于继承的代理。通常情况下,如果你的目标类实现了接口,你可以使用JDK动态代理;如果目标类没有实现接口,你可以使用CGLIB动态代理。
AOP和OOP有什么区别呢?
AOP是面向切面编程,关注横切关注点,如日志记录、事务管理等。OOP是面向对象编程,更多的是关注对象及其之间的交互,如封装继承多态等。
AOP有哪些常用术语?
切面:业务增强逻辑和原有逻辑交界的地方
织入:把增强逻辑插入到原有逻辑前的动作
连接点:需要增强的原有逻辑方法
切入点:切面具体织入的位置
通知:增强的逻辑部分
目标对象:需要增强的对象
代理对象:帮助原有对象完成增强逻辑的对象
Spring中AOP有哪几种实现方式?
1.通过创建代理对象实现AOP,根据接口的JDK动态代理或者基于继承的CJLB动态代理,如根据接口的示例如下:
public interface Calculator {
int add(int a, int b);
}
public class CalculatorImpl implements Calculator {
@Override
public int add(int a, int b) {
System.out.println("Adding " + a + " and " + b);
return a + b;
}
}
public class LoggingAspect {
public void beforeAdd(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
System.out.println("Before adding: " + args[0] + " and " + args[1]);
}
public void afterAdd(JoinPoint joinPoint, int result) {
System.out.println("After adding: result is " + result);
}
}
<bean id="calculator" class="com.example.CalculatorImpl"/>
<bean id="loggingAspect" class="com.example.LoggingAspect"/>
<aop:config>
<aop:aspect ref="loggingAspect">
<aop:pointcut id="addMethod" expression="execution(* com.example.Calculator.add(..))"/>
<aop:before method="beforeAdd" pointcut-ref="addMethod"/>
<aop:after-returning method="afterAdd" returning="result" pointcut-ref="addMethod"/>
</aop:aspect>
</aop:config>
2.通过AspectJ实现AOP,AspectJ是一个独立的AOP框架,能够支持注解或者配置文件xml的方式来增强Spring的代理能力,有丰富的切入点表达式等。使用示例如下:
@Aspect
public class LoggingAspect {
@Before("execution(* com.example.Calculator.add(..))")
public void beforeAdd(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
System.out.println("Before adding: " + args[0] + " and " + args[1]);
}
@AfterReturning(pointcut = "execution(* com.example.Calculator.add(..))", returning = "result")
public void afterAdd(JoinPoint joinPoint, int result) {
System.out.println("After adding: result is " + result);
}
}
Spring AOP有哪些常用注解?
@Aspect:用于定义切面(Aspect)。
@Before:在目标方法执行之前执行通知(Advice)。
@After:在目标方法执行之后(无论是否发生异常)执行通知。
@AfterReturning:在目标方法正常返回后执行通知,可以访问方法的返回值。
@AfterThrowing:在目标方法抛出异常后执行通知,可以访问到抛出的异常。
@Around:可以在方法调用之前和之后执行通知,可以控制方法的执行流程,包括是否执行目标方法。
@Pointcut:定义切点(Pointcut),用于指定切入点表达式。
AspectJ的切入点表达式是什么规则?
Execution(执行表达式)
Execution 是最常用的连接点。它匹配方法的执行,语法形式为 execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)。
其中 modifiers-pattern 匹配修饰符,ret-type-pattern 匹配返回类型,declaring-type-pattern 匹配声明类型,name-pattern 匹配方法名,param-pattern 匹配参数,throws-pattern 匹配异常。
// 匹配返回类型为String的UserService类中的所有方法
execution(String com.example.service.UserService.*(..))
// 匹配任意参数且返回类型为void的任意方法
execution(void *(..))
// 匹配在com.example.service包及其子包下的所有方法
execution(* com.example.service..*(..))
Within(类型表达式)
Within 匹配某个类型内的所有方法执行。语法形式为 within(package-name-pattern?) 或者 withincode(method-pattern)。
// 匹配com.example.service包及其子包下的所有方法
within(com.example.service.*)
// 匹配UserService类内的所有方法
within(com.example.service.UserService)
This 和 Target(目标对象表达式)
This 匹配当前执行对象的类型。
Target 匹配当前执行对象的目标对象类型。
// 匹配当前执行对象为UserService类型的方法
this(com.example.service.UserService)
// 匹配目标对象类型为UserService的方法
target(com.example.service.UserService)
Args(参数表达式)
Args 匹配传入方法的参数类型。语法形式为 args(type-pattern)。
// 匹配参数类型为String的方法
args(String)
// 匹配参数类型为int和String的方法
args(int, String)
@annotation(注解表达式)
@annotation 匹配带有特定注解的方法。语法形式为 @annotation(annotation-type-pattern)。
// 匹配使用@Transactional注解的方法
@annotation(org.springframework.transaction.annotation.Transactional)
@within 和 @target(注解目标表达式)
@within 匹配包含特定注解的类型。
// 匹配类上标注有@Service注解的方法
@within(org.springframework.stereotype.Service)
// 匹配目标对象类型上标注有@Service注解的方法
@target(org.springframework.stereotype.Service)
if(条件表达式)
if 条件表达式可以添加额外的条件来限制匹配。
// 匹配返回类型为String且方法名以"get"开头的方法,且参数类型不为null
execution(String get*(..)) && args(!null)
Pointcut 组合
切入点表达式可以通过 &&(逻辑与)、||(逻辑或)和 !(逻辑非)等逻辑操作符进行组合。
// 匹配声明类型为UserService的所有方法,并且方法名以"get"开头或者参数类型为String
within(com.example.service.UserService) && (execution(* get*(..)) || args(String))
评论区