1、⭐⭐⭐简单介绍🌙🌙🌙
如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP,如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换.本文中由于使用的是没接口的,所以会采用cglib实现动态代理。
2、⭐⭐⭐注解说明🌙🌙🌙
@Aspect表示该类是一个用于设置AOP切面相关的切面类
@Pointcut 来声明切入点表达式
@Order 注解指定切面的优先级, 值越小优先级越高
@Before在方法开始之前执行一段代码
@After在方法执行之后执行的代码. 无论该方法是否出现异常
@AfterReturning在方法法正常结束受执行的代码,返回通知是可以访问到方法的返回值的!
@AfterThrowing在目标方法出现异常时会执行的代码.可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
@Around环绕通知需要携带 ProceedingJoinPoint 类型的参数.环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.且环绕通知必须有返回值, 返回值即为目标方法的返回值
@Configuration//用于定义配置类,可替换xml配置文件
@EnableAspectJAutoProxy(proxyTargetClass=true) //开启AspectJ 自动代理模式,如果不填proxyTargetClass=true,默认为false(代表即便你配置了@EnableAspectJAutoProxy,也不会开启代理模式,调试的时候我就被卡在这里蛮久...)
@ComponentScan(basePackages = "com.atguigu.spring5.configuration2")//扫描注入类
3、⭐⭐⭐示例代码🌙🌙🌙:
<1>简单的bean对象 User.java
package com.atguigu.spring5.configuration2;
import org.springframework.stereotype.Component;
@Component("user")
public class User {
public void add(){
System.out.println("add ...");
}
}
<2>用于替换以前bean的xml配置文件的配置类 SpringConfiguration.java
package com.atguigu.spring5.configuration2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
//即使用jdk默认代理模式,AspectJ代理模式是CGLIB代理模式
//如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
//如果目标对象实现了接口,可以强制使用CGLIB实现AOP (此例子我们就是强制使用cglib实现aop)
//如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
@Configuration//用于定义配置类,可替换xml配置文件
@EnableAspectJAutoProxy(proxyTargetClass=true) //开启AspectJ 自动代理模式,如果不填proxyTargetClass=true,默认为false
@ComponentScan(basePackages = "com.atguigu.spring5.configuration2")//扫描注入类
public class SpringConfiguration {
@Bean
public User user() {
return new User();
}
}
<3>用于替换bean的xml配置文件里AOP相关的配置类LoggingAspect .java
package com.atguigu.spring5.configuration2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import java.util.Arrays;
/**
* 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
*/
//@Order(2)
@Aspect
@Component
public class LoggingAspect {
/**
* 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码.
* 使用 @Pointcut 来声明切入点表达式.
* 后面的其他通知直接使用方法名来引用当前的切入点表达式.
*/
@Pointcut("execution(public void com.atguigu.spring5.configuration2.User.*(..))")
public void declareJointPointExpression(){
}
/**
* 在 com.atguigu.spring5.aop.User 接口的每一个实现类的每一个方法开始之前执行一段代码
*/
@Before("declareJointPointExpression()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
Object [] args = joinPoint.getArgs();
System.out.println("The beforeMethod " + methodName + " begins with " + Arrays.asList(args));
}
/**
* 在方法执行之后执行的代码. 无论该方法是否出现异常
*/
@After("declareJointPointExpression()")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The afterMethod " + methodName + " ends");
}
/**
* 在方法法正常结束受执行的代码
* 返回通知是可以访问到方法的返回值的!
*/
@AfterReturning(value="declareJointPointExpression()",returning="result")
public void afterReturning(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("The afterReturning " + methodName + " ends with " + result);
}
/**
* 在目标方法出现异常时会执行的代码.
* 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
*/
@AfterThrowing(value="declareJointPointExpression()",throwing="e")
public void afterThrowing(JoinPoint joinPoint, Exception e){
String methodName = joinPoint.getSignature().getName();
System.out.println("The afterThrowing " + methodName + " occurs excetion:" + e);
}
/**
* 环绕通知需要携带 ProceedingJoinPoint 类型的参数.
* 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
* 且环绕通知必须有返回值, 返回值即为目标方法的返回值
*/
/*
@Around("execution(public int com.atguigu.springspring5.aop.User.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjd){
Object result = null;
String methodName = pjd.getSignature().getName();
try {
//前置通知
System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
//执行目标方法
result = pjd.proceed();
//返回通知
System.out.println("The method " + methodName + " ends with " + result);
} catch (Throwable e) {
//异常通知
System.out.println("The method " + methodName + " occurs exception:" + e);
throw new RuntimeException(e);
}
//后置通知
System.out.println("The method " + methodName + " ends");
return result;
}
*/
}
<4>测试类TestAnnotationConfig.java
package com.atguigu.spring5.configuration2;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
public class TestAnnotationConfig {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfiguration.class);
User user = ctx.getBean(User.class);
user.add();
}
}
<5>运行测试类TestAnnotationConfig.java结果如下:
4、核心代码逻辑及思维导图
以下是文章中提到的使用完全注解方式进行AOP功能实现的核心逻辑,转换成横向的思维导图:
graph LR
A["AOP注解实现"] -->|定义切面类| B["@Aspect"]
A -->|声明切入点| C["@Pointcut"]
A -->|指定切面优先级| D["@Order"]
A -->|方法执行前| E["@Before"]
A -->|方法执行后| F["@After"]
A -->|方法正常返回后| G["@AfterReturning"]
A -->|方法抛出异常后| H["@AfterThrowing"]
A -->|环绕方法执行| I["@Around"]
A -->|定义配置类| J["@Configuration"]
A -->|开启AspectJ自动代理| K["@EnableAspectJAutoProxy"]
A -->|扫描组件包路径| L["@ComponentScan"]
A -->|具体实现类| M["示例代码"]
M -->|业务逻辑类| M1["User 类"]
M -->|配置Spring环境| M2["SpringConfiguration 类"]
M -->|定义切面逻辑| M3["LoggingAspect 类"]
M -->|运行AOP测试| M4["TestAnnotationConfig 类"]
在这个思维导图中,每个注解和配置类都有了更详细的说明,描述了它们在AOP实现中的作用:
- @Aspect: 定义一个类作为切面类。
- @Pointcut: 声明一个切入点,指定哪些方法可以被拦截。
- @Order: 指定切面的执行顺序,值越小优先级越高。
- @Before: 在目标方法执行之前执行的逻辑。
- @After: 在目标方法执行之后执行的逻辑,无论是否抛出异常。
- @AfterReturning: 当目标方法正常执行完成后执行的逻辑。
- @AfterThrowing: 当目标方法抛出异常时执行的逻辑。
- @Around: 环绕通知,可以在方法执行前后添加逻辑。
- @Configuration: 定义配置类,用于替代XML配置。
- @EnableAspectJAutoProxy: 开启AspectJ的自动代理功能。
- @ComponentScan: 指定Spring扫描组件的包路径。
- 示例代码: 包含具体的实现类。
每个示例代码的子节点也都有相应的说明:
- User 类: 业务逻辑类,被AOP代理的目标对象。
- SpringConfiguration 类: 配置Spring环境,包括Bean的定义和AOP的配置。
- LoggingAspect 类: 定义切面逻辑,包括各种通知类型的实现。
- TestAnnotationConfig 类: 运行AOP测试的入口类。