Spring AOP

目录

简介

AOP 组成

Spring AOP 实现

 动态代理

Spring 拦截器实现

统一的异常处理

统一的格式返回


简介

AOP 是一种思想 , Spring AOP 框架 是具体实现 .

AOP (Aspect Oriented Programming) : 面向切面编程 .它是对某一类事情的集中处理 (针对某一方面的问题主动进行处理 ) .

举个栗子 : 

当用户点到博客进行发布文章时 ,  后端就要判断是否已经登录 , 当去删除一个文章时 , 也需要去判断当前是否是登录状态 . 每次涉及到权限问题时都需要判断用户登录状态 . 有了AOP之后 , 我们只需要在某一处配置一下 . 就可以实现所有需要判断用户登录状态的页面的功能.

因此, AOP 就可以用在 : 对于功能统一 , 且使用的地方较多的功能 , 就可以考虑 AOP 来统一处理了 .

AOP 可以实现 :

1. 统一日志记录 2. 统一方法执行时间统计 3. 统一的返回格式设置 4. 统一的异常处理 5. 事务的开启和提交等 .

AOP 组成

AOP 由 切面 , 切点 , 连接点 , 通知 组成 .

切面 : 简单来说 , 切面在程序中就是一个处理某方面具体问题的一个类 , 类里面包含了很多方法 , 而这些方法就是切点和通知 . (这个类是专门处理某一个问题的, 例如 :登录问题)

切点 : 用来进行主动拦截的规则(配置) .

连接点 : 可能会触发 AOP 规则的所有点 (所有请求).

通知 : (AOP 具体的执行动作 ).

           前置通知 : 在执行目标方法之前执行的方法就叫做前置通知 .

           后置通知 : 在执行了目标方法之前执行的方法就叫做后置通知 .

           异常通知 : 在执行目标方法出现异常时 , 执行的通知 .

           返回通知 : 目标方法执行了返回数据 (return) 时 , 执行的通知 .

           环绕通知 : 在目标方法执行的周期范围内 (执行之前 , 执行时 , 执行后) 都可以执行的方法叫做环绕通知 .

Spring AOP 实现

实现步骤 :

1 . 添加 Spring AOP 依赖

2 . 定义切面 (创建切面类)

3 . 定义切点 (配置拦截规则)

4 . 定义通知的实现

1 . 添加依赖

注意 : 依赖中的版本号要对应Spring Boot 的版本号.

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
            <!--对应Spring Boot 的版本-->
			<version>2.7.13</version>
		</dependency>

2 . 定义切面

使用 @Aspect 注解 : 意思就是告诉了框架这是一个切面类

使用 @Componet 注解 : 加上五大类注解 让这个类随着框架的启动而启动 .

@Aspect // 表示这是一个切面类
@Component // 随着框架一起启动
public class UserAspect {
    
}

3 . 定义切点 与 通知

切点 : @Pointcut 关键字来配置拦截的规则

通知 : 前置通知 @Before 通知方法会在目标方法调用之前执行 .

          后置通知 @After 通知方法会在目标方法返回或者 抛出异常后调用 .

          返回之后通知 @AfterReturning 通知方法会在目标方法返回之后调用.

          抛异常后通知 @AfterThrowing 通知方法会在目标方法抛出异常后调用 .

          环绕通知 @Around 通知包括了 通知方法之前 , 方法执行时 , 调用方法后 .

@Aspect // 表示这是一个切面类
@Component // 随着框架一起启动
public class UserAspect {
    /**
     * 切点 (拦截配置)
     */
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    public void pointcut() {
    }

    /**
     * 前置通知
     */
    @Before("pointcut()")
    public void beforeAdvice() {
        System.out.println("前置通知已执行.");
    }

    /**
     * 后置通知
     */
    @After("pointcut()")
    public void afterAdvice() {
        System.out.println("后置通知已执行.");
    }
}

通过网页去访问 UserController 类中的方法 :

 

    /**
     * 环绕通知
     */
    @Around("pointcut()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("进入环绕通知");
        Object obj = null;
        // 执行目标方法
        obj = joinPoint.proceed();
        System.out.println("退出环绕通知");
        return obj;
    }

 动态代理

动态代理在Spring 中有两种实现方式 :  JDK ProxyCFLIB

JDK Proxy实现 , 要求被代理类必须实现接口 , 之后是通过 InvocationHandler 及 Proxy , 在运行时动态的在内存中生成了代理类对象 , 该代理对象是通过实现同样的接口实现 (类似静态代理接口实现的方式) , 只是该代理类是在运行期时 , 动态的织入统一的业务逻辑字节码来完成 .

CGLIB 实现 , 被代理类可以不实现接口 , 是通过继承被代理类 , 在运行时动态的生成代理类对象 .

Spring 拦截器实现

步骤 : 首先要实现 HandlerInterceptor 接口 ,重写 preHeadler 方法 , 在方法中编写自己的业务代码 . 其次 将拦截器添加到配置文件中 , 并且设置拦截的规则 .

Spring 拦截器 充当了这个代理对象 , 也是通过动态代理和环绕通知的思想实现的 .

拦截器的实现 :

/**
 * 自定义一个拦截器
 */
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 返回一个 boolean 值 , true表示 验证成功了, false 表示验证失败
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 用户登录业务判断
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute("userinfo") != null) {
            // 证明用户已经登录
            return true;
        }
        // 可以调整到登录页面
        response.sendRedirect("/login.html");
        // 或者去返回一个 401/403 没有权限码
//        response.setStatus(401);
        return false;
    }
}

将拦截器添加到配置文件中 , 设置拦截规则 .

@Configuration
public class AppConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**") // 拦截所有请求
                .excludePathPatterns("/user/login"); // 排除的url地址 (不拦截的url 地址)
    }
}

统一的异常处理

统一异常处理的实现是由 @ControllerAdvice + @ExceptionHandler 来实现的 .

@ControllerAdvice 表示控制器通知类.

@ExceptionHandler 是异常处理器 .

@ControllerAdvice
@ResponseBody
public class MyExHandler {
    @ExceptionHandler(NullPointerException.class)
    public HashMap<String,Object> nullException(NullPointerException e) {
        HashMap<String,Object> result = new HashMap<>();
        result.put("code","-1");
        // 错误码的秒睡信息
        result.put("msg","空指针异常" + e.getMessage());
        result.put("data",null);
        return result;
    }
}

统一的格式返回

实现步骤 : 创建类 , 添加 @ControllerAdvice , 实现 ResponseBodyAdvice 接口 . 

并重写 supports 和 beforeBodyWrite 方法.

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        HashMap<String,Object> result = new HashMap<>();
        result.put("code",200);
        result.put("msg"," ");
        result.put("data",body);
        if (body instanceof String) {
            // 当 body是 String时,需要进行特殊转换 .
            try {
                return objectMapper.writeValueAsString(result);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return result;
    }
}

End.... 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值