SpringBoot-AOP深入浅出通俗易懂—看不懂你捶鹅

目录

前言

AOP总体思想

AOP图解

AOP-Aspect-代码举例

1、定义Service

2. 定义LoginController

3. 定义UserLoginAspect切面

AOP-Handler拦截器-代码举例

1、定义拦截器

2、注册拦截器

总结


前言

        Spring最重要的两个思想就是IOC、AOP,之前的文章SpringBoot自动装配分析了IOC思想并进行了源码详解。这次看一下AOP思想。

        网上有很多AOP思想的讲解,五花八门,最重要的几点都会讲到,但是很多文章不会给人恍然大悟的感觉。我这次就把我对AOP的理解总结一下。

        最近整理了汇总了⼀些 Java ⾯试相关的⾼质量 PDF 资料和免费Idea账号,在公众号:Java小白,回复“⾯试” 和“idea破解”即可获取!

AOP总体思想

        AOP(面向切面编程)是一种编程范式,它在软件开发中用于将横切关注点与核心业务逻辑进行解耦。

        想象一下,你正在开发一个大型的 Web 应用程序。你可能会有很多模块和功能需要实现,例如点赞模块、收藏模块、评论模块等等。同时,你还需要处理一些共性的任务,例如用户信息校验、记录日志、权限验证、异常处理等。这些共性任务可能会散布在各个模块和功能的代码中,如果要逐一添加则会导致代码的重复和冗余,增加了维护成本。(这段中的红色标字就是要插入模块中的功能)

        这时候,AOP 就派上了用场。AOP 允许你在不改变原有模块代码的情况下,将这些共性任务从核心业务逻辑中抽离出来,形成一个独立的横切关注点。这个横切关注点就像一个切面,可以被动态地织入到不同的模块和功能中。

以下是AOP专用名词的解释:

  • Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
  • Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
  • Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
  • Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
  • Target(目标对象):织入 Advice 的目标对象.。
  • Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程。

以下是Advice中方法名词解释:

  • before advice, 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)
  • after return advice, 在一个 join point 正常返回后执行的 advice
  • after throwing advice, 当一个 join point 抛出异常后执行的 advice
  • after(final) advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.
  • around advice, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.
  • introduction,introduction可以为原有的对象增加新的属性和方法。

AOP图解

        我画一张图来描述:

SpringBoot-AOP深入浅出通俗易懂—看不懂你捶鹅_第1张图片

          这时想在前端和controller2模块中间增加用户信息校验,没有用户信息不能点赞、收藏、评论,并跳到登陆页面。

SpringBoot-AOP深入浅出通俗易懂—看不懂你捶鹅_第2张图片

        现在,我们看到,我把Aspect放在了前端和controller中间,Joint Ponit就是负责抓取链路的,符合controller2的链路就被抓取到Pointcut中,进行Advice-Before处理这个意思我们看图也能知道,就是在进入controller方法前执行的操作,那么,Advice-After就是在controller方法结束后执行的操作。

        这样的话,就在不改动原来代码的情况下,织入了登录校验步骤。

AOP-Aspect-代码举例

        现在我们就从SpringBoot系统的模块中,添加一个登陆验证的AOP切面。

        假设我们有一个LikeService类,其中包含了一个方法 `likePost` 用于实现用户点赞功能,以及LoginController 类,用于处理用户登录逻辑,我们将在这个LoginController方法中应用 AOP 来校验用户登录状态。

1、定义Service

import org.springframework.stereotype.Service;

@Service
public class LikeService {

    public void likePost() {
        // 点赞操作的逻辑
        System.out.println("Post liked!");
    }
}

2. 定义LoginController

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class LoginController {

    @GetMapping("/login")
    public String showLoginPage() {
        // 展示登录页面的逻辑
        return "login";
    }
}

3. 定义UserLoginAspect切面

        定义UserLoginAspect切面,并指定切点对象

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Aspect
@Component
public class UserLoginAspect {

    @Pointcut("execution(* com.example.demo.service.LikeService.likePost(..))")
    public void likePostPointcut() {}

    @Before("likePostPointcut()")
    public void checkUserLogin() throws IOException {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 这里假设用户登录状态通过 Session 中的一个标志位来判断
        if (request.getSession().getAttribute("loggedInUser") == null) {
            // 用户未登录,跳转到登录页面
            System.out.println("Please login to like the post.");
            // 使用 redirect 实现页面跳转到登录页面
            request.sendRedirect("/login");
        } else {
            // 用户已登录,执行点赞操作
            System.out.println("Post liked!");
        }
    }
}

        在上述代码中,我们定义了一个Aspect命名为UserLoginAspect用于切面,Pointcut命名为likePostPointcut用于指定切点对象,这里指定为LikeService类中的likePost方法。

        然后,在checkUserLogin方法上使用 @Before注解,并指定了 likePostPointcut() 作为切点表达式,表示该切面将在 `LikeService` 的 `likePost` 方法执行前执行。

        在 `checkUserLogin` 方法中,我们进行用户登录状态的校验,如果用户未登录,则抛出一个运行时异常并提示用户登录;如果用户已登录,则继续执行点赞操作。

        请注意,在实际应用中,我们需要根据具体的业务需求,实现真正的用户登录校验逻辑,例如从 Session、Token 或数据库中验证用户登录状态。

AOP-Handler拦截器-代码举例

        也可以使用 Spring MVC 的拦截器(Interceptor)来实现用户登录状态的校验和页面跳转。拦截器在 Spring MVC 中是一种更通用的方式来实现横切关注点的处理,包括用户登录校验、日志记录、权限验证等。

以下是使用拦截器实现用户登录状态校验和页面跳转的代码示例:

1、定义拦截器

定义拦截器类 `UserLoginInterceptor`:

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class UserLoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        // 这里假设用户登录状态通过 Session 中的一个标志位来判断
        if (request.getSession().getAttribute("loggedInUser") == null) {
            // 用户未登录,跳转到登录页面
            System.out.println("Please login to like the post.");
            // 使用 redirect 实现页面跳转到登录页面
            response.sendRedirect("/login");
            return false; // 返回 false 阻止后续请求处理
        }
        // 用户已登录,放行继续执行后续请求
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        // 在处理完请求后进行拦截器的后处理操作
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        // 在视图渲染完成后进行拦截器的后处理操作
    }
}

在上述拦截器中,我们重写了 `preHandle` 方法,在该方法中进行用户登录状态的校验。如果用户未登录,则使用 `response.sendRedirect("/login");` 将请求重定向到登录页面,并返回 false 阻止后续请求处理。如果用户已登录,则返回 true 放行继续执行后续请求。

2、注册拦截器

注册拦截器,创建一个配置类,用@Configuration注解来标注:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册自定义的拦截器
        registry.addInterceptor(new UserLoginInterceptor())
                .addPathPatterns("/like"); // 设置需要拦截的请求路径,这里假设点赞请求的路径为 /like
    }
}

        在上述配置中,我们通过 `addInterceptor` 方法注册了自定义的拦截器 `UserLoginInterceptor`,并使用 `addPathPatterns` 方法指定了需要拦截的请求路径为 `/like`,这样只有当请求路径为 `/like` 时才会触发拦截器的执行。

        上述代码可以作为模板直接复制使用,但是在实际应用中,你需要根据具体的业务需求,设置拦截器的拦截路径和校验逻辑。

总结

        在使用AOP的时候,我们要注意,我们织入的功能一定要是共性任务,不然如果只是对一个模块进行织入,那就是大题小作了,不如直接对那个模块进行改造。

        对您有帮助的话,点赞收藏,嘿嘿!

 

最后我还整理汇总了⼀些 Java ⾯试相关的⾼质量 PDF 资料和免费Idea账号

公众号:Java小白,回复“⾯试” 和“idea破解”即可获取!

 

你可能感兴趣的:(SpringBoot,Java,spring,boot,mybatis,后端,分布式,mvc)