AOP
把这个过程细分为几个步骤
- 先写一个通知类 MyAdvice
- 写一个切入方法 并标记他是切入的通知 用Pointcut注解
Java @Pointcut(“execution(* com.itheima.dao.BookDao.*d*(…))”) private void pt(){}
其中括号里面的是切入点表达式
- 要在所有需要切入方法实际要操作的函数上添加@Around注解 括号里面是切入方法的名字
Java @Around(“pt()”) public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println(“------------------------------”); Long startTime = System.currentTimeMillis(); for (int i = 0 ; i<10000 ; i++) { //调用原始操作 pjp.proceed(); } Long endTime = System.currentTimeMillis(); Long totalTime = endTime-startTime; System.out.println(“执行万次消耗时间:” + totalTime + “ms”); return null; }
- 因为我们要使用Spring的功能 所以要让Spring知道 所以要让这个通知类被Spring扫描到 添加@Component注解
- 但是 Spring此时只会把他当普通的类看待 所以要告诉他这是在做通知操作 @Aspect
Java @Component
Java @Aspect
Java public class MyAdvice {
- 最后要通知Spring 我们要启用AOP的模式去运行 所以要在配置类中 添加@EnableAspectJAutoProxy注解 来告诉Spring我们要使用上面那个@Aspect注解了
@Component @Aspect public class MyAdvice { @Pointcut("execution(* com.itheima.dao.BookDao.*d*(..))") private void pt(){} @Around("pt()") public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("------------------------------"); Long startTime = System.currentTimeMillis(); for (int i = 0 ; i<10000 ; i++) { //调用原始操作 pjp.proceed(); } Long endTime = System.currentTimeMillis(); Long totalTime = endTime-startTime; System.out.println("执行万次消耗时间:" + totalTime + "ms"); return null; } }
AOP工作流程
- Spring容器启动
- 读取所有切面配置中的切入点
- 初始化Bean 判定Bean对应的类中的方法是否匹配到任意切入点
- 匹配失败 创建对象
- 匹配成功 创建原始对象 (目标对象)的代理对象(这里内部使用的是jdk代理模式 )
- 获取Bean执行方法
- 获取Bean调用方法并执行 完成操作
- 获取的Bean是代理对象时 根据代理对象的运行模式运行原始方法与增强的内容 完成操作
AOP工作本质:代理模式
切入点表达式
Execution 执行
Java @Pointcut(“execution(* com.itheima.dao.BookDao.*d*(…))”) private void pt(){}
切入通知之 环绕通知***重要
@Around 表示这是个环绕通知 (在一前一后都有) @Pointcut("execution(int com.itheima.dao.BookDao.select())") private void pt2(){}//代表你的切入点在哪 在这呢↑ @Around("pt2()")//pjp代表原始操作 public Object around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("around before advice ..."); //表示对原始操作的调用 Object ret = pjp.proceed();//这里其实就是调用完原始操作的返回值 System.out.println("around after advice ..."); return ret;//返回值 因为原始操作函数要求要有返回值int }
原始操作函数:
Java public interface BookDao { public void update(); public int select(); }
AfterReturning
//@AfterReturning:返回后通知,在原始方法执行完毕后运行,且原始方法执行过程中未出现异常现象 @AfterReturning("pt2()") public void afterReturning() { System.out.println("afterReturning advice ..."); }
AfterThrowing
//@AfterThrowing:抛出异常后通知,在原始方法执行过程中出现异常后运行 @AfterThrowing("pt2()") public void afterThrowing() { System.out.println("afterThrowing advice ..."); }
|写配置类 写完记得在Spring的配置类中导入|
最后!应用场景
- 日志记录: 通过AOP,在方法执行前后记录方法的输入参数、返回结果以及执行时间,从而实现统一的日志记录功能,避免在每个方法中都添加日志记录的代码。
- 权限控制: 在进入某些方法之前,进行用户权限的验证,确保用户有权执行这些方法。例如,在Web应用中,可以通过AOP拦截请求,并验证用户的登录状态和权限。
- 性能监控: AOP可用于监控程序的性能表现,例如统计某些关键方法的执行时间、调用次数等,帮助发现性能瓶颈。
- 事务管理: 通过AOP实现事务管理,确保在一系列数据库操作中的成功或失败都能得到正确处理,比如回滚所有操作或者提交事务。
- 异常处理: 使用AOP可以集中处理方法中出现的异常,统一记录异常信息或者执行特定的异常处理逻辑。
- 缓存控制: 通过AOP实现缓存的控制,比如在执行方法前检查缓存是否存在有效数据,如果存在则直接返回缓存数据。
- 国际化: AOP可用于统一处理国际化相关的逻辑,例如在方法调用前根据用户的语言偏好设置合适的国际化资源。
- 业务规则的验证: 在方法执行前进行业务规则的验证,确保业务逻辑的正确性,比如数据格式、业务逻辑等
在心理ai小程序这个业务中能用上的基本就是 语言偏好 后台的查询处理
用这个思想 写了一个登录效验器 用于校验那些需要用户登录才能使用的功能 的拦截监测
- 创建登录效验切面类: 创建一个切面类,用于实现登录效验逻辑。
|Java @Aspect @Component public class LoginValidationAspect { @Autowired private StringRedisTemplate redisTemplate; // 使用Spring提供的StringRedisTemplate来操作Redis @Pointcut(“execution(* com.yourpackage.controller.*.*(…)) && @annotation(com.yourpackage.annotation.RequireLogin)”) public void loginRequired() {} @Before(“loginRequired()”) public void validateLogin(JoinPoint joinPoint) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); String token = request.getHeader(“Authorization”); // 假设登录时会返回一个包含用户信息的Token if (token == null || !redisTemplate.hasKey(token)) { throw new UnauthorizedException(“用户未登录,请先登录”); } } }| | :- |
2. 创建自定义注解: 创建一个自定义注解,用于标记需要登录效验的方法。
Java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RequireLogin { }
- 在Controller中使用自定义注解: 在需要进行登录校验的控制器方法上添加自定义注解。
Java @RestController public class UserController { @Autowired private UserService userService; @PostMapping(“/login”) public String login(@RequestBody LoginRequest request) { // 处理登录逻辑 // … return “登录成功”; } @RequireLogin @GetMapping(“/user/info”) public UserInfo getUserInfo() { // 获取用户信息 // … } }
public UserInfo getUserInfo() { // 获取用户信息 // … } }