机器视觉框架源码:VS2019编译、模块拆解与二次开发实战
2026/9/9 9:34:59
先看一个典型问题:假如你有 3 个业务方法(用户注册、订单创建、支付),每个方法都需要加「日志记录」「权限校验」「异常处理」逻辑,传统写法会这样:
java
运行
// 传统写法:通用逻辑和业务逻辑耦合 public void registerUser() { // 1. 日志记录(通用逻辑) log.info("开始执行用户注册"); // 2. 权限校验(通用逻辑) if (!hasPermission()) { throw new NoPermissionException(); } try { // 3. 核心业务逻辑 userService.save(); } catch (Exception e) { // 4. 异常处理(通用逻辑) log.error("注册失败", e); throw e; } } public void createOrder() { // 重复的日志、权限、异常逻辑... orderService.save(); }痛点:通用逻辑(日志、权限)重复写,代码冗余,维护成本高(比如要改日志格式,得改所有方法)。
AOP 的解决思路:把日志、权限这些通用逻辑抽成「切面」,在不修改业务代码的前提下,自动 “织入” 到业务方法的执行环节(比如执行前、执行后、抛出异常时)。
用 “切蛋糕” 类比:业务逻辑是蛋糕的主体,AOP 是 “刀”,按指定规则(切点)切到蛋糕的指定位置(连接点),把切面逻辑(奶油 / 水果)加进去。
| 概念 | 通俗解释 |
|---|---|
| 切面(Aspect) | 抽离出来的通用逻辑模块(比如日志切面、权限切面),包含「切点」+「通知」 |
| 连接点(Join Point) | 程序执行过程中的 “时机点”(比如方法执行前、执行后、抛出异常时) |
| 切点(Pointcut) | 筛选连接点的 “规则”(比如只匹配 service 包下的所有方法) |
| 通知(Advice) | 切面在连接点执行的具体逻辑(比如前置通知:方法执行前打日志) |
| 织入(Weaving) | 把切面逻辑植入到业务代码的过程(AOP 框架自动完成,分编译期、类加载期、运行期) |
AOP 本身是思想,主流实现有两种:
Spring AOP 是最常用的 AOP 框架,下面用「日志切面」演示核心用法:
xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>java
运行
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; // 1. 标记为组件(Spring扫描)+ 标记为切面 @Component @Aspect public class LogAspect { // 2. 定义切点:匹配com.example.service包下所有类的所有方法 @Pointcut("execution(* com.example.service.*.*(..))") public void servicePointcut() {} // 3. 前置通知:方法执行前打日志 @Before("servicePointcut()") public void beforeAdvice(JoinPoint joinPoint) { String methodName = joinPoint.getSignature().getName(); // 获取方法名 Object[] args = joinPoint.getArgs(); // 获取方法参数 System.out.println("前置通知:执行方法[" + methodName + "],参数:" + args); } // 4. 环绕通知:统计方法执行耗时 @Around("servicePointcut()") public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); // 执行原业务方法 Object result = joinPoint.proceed(); long cost = System.currentTimeMillis() - start; System.out.println("环绕通知:方法[" + joinPoint.getSignature().getName() + "]耗时" + cost + "ms"); return result; } // 5. 异常通知:方法抛异常时打日志 @AfterThrowing(pointcut = "servicePointcut()", throwing = "e") public void afterThrowingAdvice(JoinPoint joinPoint, Exception e) { String methodName = joinPoint.getSignature().getName(); System.out.println("异常通知:方法[" + methodName + "]抛出异常:" + e.getMessage()); } }java
运行
import org.springframework.stereotype.Service; @Service public class UserService { public void register(String username) { System.out.println("核心业务:用户[" + username + "]注册成功"); // 模拟异常(可注释测试) // if (username.isEmpty()) { // throw new RuntimeException("用户名不能为空"); // } } }java
运行
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication public class AopDemoApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(AopDemoApplication.class, args); UserService userService = context.getBean(UserService.class); userService.register("张三"); // 调用业务方法 } }plaintext
前置通知:执行方法[register],参数:[张三] 核心业务:用户[张三]注册成功 环绕通知:方法[register]耗时1ms