目录
前言
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(面向切面编程)是一种编程范式,它在软件开发中用于将横切关注点与核心业务逻辑进行解耦。
想象一下,你正在开发一个大型的 Web 应用程序。你可能会有很多模块和功能需要实现,例如点赞模块、收藏模块、评论模块等等。同时,你还需要处理一些共性的任务,例如用户信息校验、记录日志、权限验证、异常处理等。这些共性任务可能会散布在各个模块和功能的代码中,如果要逐一添加则会导致代码的重复和冗余,增加了维护成本。(这段中的红色标字就是要插入模块中的功能)
这时候,AOP 就派上了用场。AOP 允许你在不改变原有模块代码的情况下,将这些共性任务从核心业务逻辑中抽离出来,形成一个独立的横切关注点。这个横切关注点就像一个切面,可以被动态地织入到不同的模块和功能中。
以下是AOP专用名词的解释:
以下是Advice中方法名词解释:
我画一张图来描述:
这时想在前端和controller2模块中间增加用户信息校验,没有用户信息不能点赞、收藏、评论,并跳到登陆页面。
现在,我们看到,我把Aspect放在了前端和controller中间,Joint Ponit就是负责抓取链路的,符合controller2的链路就被抓取到Pointcut中,进行Advice-Before处理这个意思我们看图也能知道,就是在进入controller方法前执行的操作,那么,Advice-After就是在controller方法结束后执行的操作。
这样的话,就在不改动原来代码的情况下,织入了登录校验步骤。
现在我们就从SpringBoot系统的模块中,添加一个登陆验证的AOP切面。
假设我们有一个LikeService类,其中包含了一个方法 `likePost` 用于实现用户点赞功能,以及LoginController 类,用于处理用户登录逻辑,我们将在这个LoginController方法中应用 AOP 来校验用户登录状态。
import org.springframework.stereotype.Service;
@Service
public class LikeService {
public void likePost() {
// 点赞操作的逻辑
System.out.println("Post liked!");
}
}
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class LoginController {
@GetMapping("/login")
public String showLoginPage() {
// 展示登录页面的逻辑
return "login";
}
}
定义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 或数据库中验证用户登录状态。
也可以使用 Spring MVC 的拦截器(Interceptor)来实现用户登录状态的校验和页面跳转。拦截器在 Spring MVC 中是一种更通用的方式来实现横切关注点的处理,包括用户登录校验、日志记录、权限验证等。
以下是使用拦截器实现用户登录状态校验和页面跳转的代码示例:
定义拦截器类 `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 放行继续执行后续请求。
注册拦截器,创建一个配置类,用@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破解”即可获取!