AOP(Aspect Oriented Programming)

AOP(Aspect Oriented Programming)

1.何为AOP(面向切面编程)?

是一种编程思想,通过预编译方式和动态代理实现程序功能的统一维护技术,是spring框架中的一个重要内容,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑的耦合度降低,可以提高程序的可重用性,同时提高开发效率,是OOP思想的扩展。

应用场景:主要使用在对一些公共操作进行统一的处理,例如登录校验、权限控制、统一日志记录、统一返回格式、统一一次处理等。

2.AOP底层原理?

AOP底层使用的是动态代理,若类为接口实现类,默认使用JDK动态代理,否则使用CGLIB动态代理。

关于代理类的生成时期:

  1. 编译期:在代码编译时,将代理代码织入到目标类中。
  2. 类加载期:当目标类被加载时,修改目标类的字节码。
  3. 运行期:当程序运行的某一时刻时,为目标对象创建一个代理对象。Spring AOP采用此种方式织入切面。

3.AOP的组成

  1. 连接点(JoinPoint):目标业务代码代码,可以是方法、异常处理等。
  2. 切点(Pointcut):定义AOP拦截规则(拦截哪些方法),对连接点进行筛选。
  3. 通知/增强(Advice):增强目标的业务逻辑部分(定义在连接点上应用的额外代码)。
  4. 切面(Aspect):定义AOP的业务类型(比如用于登录校验)。

3.1通知的种类

  • 前置通知(前置通知会在执行目标方法之前调用)
  • 后置通知(在执行正常方法返回后或抛出异常执后调用)
  • 异常通知(当出现异常时,才会执行)
  • 最终通知(在方法返回之后执行)
  • 环绕通知(可在目标方法前/后执行,可获取修改目标方法的参数,返回值,还可以决定目标方法是否执行)

3.2案例演示

使用AOP实现对用户登录进行校验。

3.2.1引入框架

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-aopartifactId>
dependency>

3.2.2定义连接点

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
    @RequestMapping("/login")
    public Object login(HttpServletRequest request){
        //生成session
        HttpSession session = request.getSession(true);

        log.info("用户session生成成功 id:"+session.getId());
        return "登录成功";
    }

    //连接点
    @RequestMapping("/udetail")
    public Object getUser(HttpServletRequest request){
        //验证登录~~~使用AOP
        log.info("获取用户详细成功");
        return "获取用户详细信息成功";
    }
}

3.2.3定义切面

/**
 * 定义切面,登录拦截
 */
@Component
@Aspect
@Slf4j
public class LoginAOP {
    /**
     * @Pointcut:定义切点
     * 对UserController使用规则
     * execution<修饰符>(返回类型 包.类.方法(参数)<异常>)
     */
    @Pointcut("execution(* com.example.demo.controller.UserController.getUser(..))")
    public void pointCut(){

    }

    //环绕通知
    @Around("pointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint){
        //判定用户是否登录
        log.info("验证用户是否登录中···");
        Object[] args = joinPoint.getArgs();
        HttpServletRequest request = (HttpServletRequest) args[0];
        HttpSession session = request.getSession(false);
        if(session == null){
            log.info("登录验证失败返回");
            return "登录失败";
        }
        log.info("登录验证成功");

        Object obj = null;
        try {
            //执行目标方法
            obj = joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }

        return obj;
    }
}

在这里插入图片描述

其他通知:@After(前置通知)、@Before(后置通知)、@AterReturning(最终通知)、@AfterThrowing(异常通知)

4.Spring MVC统一功能处理

使用传统的AOP来实现登录校验的功能还是比较繁琐的,为了简化开发,Spring MVC对AOP进行了进一步的封装,产生了拦截器。

使用Spring MVC拦截器的基本规则如下:

  1. 拦截器是基于Java的反射机制进行工作的,所以它能够拦截到声明了特定注解(如@Controller、@Service等)或特定路径(@RequestMapping等)的方法。
  2. 拦截器的执行顺序是按照配置文件中的顺序进行的,可以在配置文件中设定多个拦截器的执行顺序。例如,我们可以先执行一个权限验证的拦截器,然后再执行一个日志记录的拦截器。
  3. 拦截器可以在请求到达控制器之前和之后进行操作。拦截器是在DispatcherServlet之前执行的。当请求到达时,拦截器会先进行处理,然后再将请求传递给DispatcherServlet。所以,拦截器可以在请求到达控制器之前和之后进行操作。

4.1拦截器

4.1.1自定义拦截器

@Slf4j
@Component
public class LoginHandler implements HandlerInterceptor {
    /**
     * preHandle表示在请求到达控制器之前执行
     * @param request
     * @param response
     * @param handler
     * @return true:表示不需要拦截控制器的执行,后续执行控制器的业务逻辑;false:表示需要拦截控制器的执行
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession(false);
        if(session != null){
            log.info("已经登录,运行执行控制层业务逻辑");
            return true;
        }

        //未登录、权限验证失败
        log.info("未登录,不允许执行控制层业务逻辑");
        response.setStatus(401);
        return false;
    }
}

4.1.2将自定义拦截器添加到配置中

/**
 * 添加Spring MVC拦截器
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginHandler loginHandler;

    /**
     * 将拦截器添加到系统配置中
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration interceptorRegistration = registry.addInterceptor(loginHandler);
        //添加URL拦截规则,相当于AOP的切点
        //拦截所有的URL
        interceptorRegistration.addPathPatterns("/**");
        //排除某一个URL
        interceptorRegistration.excludePathPatterns("/admin/login");
    }
}

4.2统一异常处理

@ControllerAdvice
public class ExceptionAdvice {
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Object handler(Exception e){
        HashMap<String,Object> result = new HashMap<>();
        result.put("code","-1");
        result.put("message", "异常信息:"+e.getMessage());
        return result;
    }
}

@ControllerAdvice:用于为Controller提供一些全局的处理。

@ExceptionHandler:@ExceptionHandler注解可以添加参数,参数是某个异常类的class,代表这个方法专门处理该类异常。

@ControllerAdvice+@ExceptionHandler:表示用于捕获Controller中抛出指定类型的异常,从而达到统一异常的处理。@ResponceBody,表示异常处理后返回的是数据,非视图。

4.3统一返回结果封装

@ControllerAdvice
public class ResultAdvice implements ResponseBodyAdvice {
    @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) {
        //如果结果已经被封装了,就不需要再进行处理了
        if(body instanceof HashMap){
            return body;
        }

        //没有则对响应进一步封装
        HashMap hashMap = new HashMap();
        hashMap.put("status", "1");
        hashMap.put("msg", "");
        hashMap.put("data",body);
        return hashMap;
    }
}

@ControllerAdvice+ResponseBodyAdvice接口,表示在Controller执行完成之后,response返回给客户端之前,执行的对response的一些处理,可以实现对response数据的一些统一封装或者加密等操作。

5.总结

AOP面向切面编程是一种编程思想,和OOP面向对象编程类型,主要实现的功能是对统一功能进行统一处理,提高了代码的复用率(开发效率提示)、业务解耦合。

你可能感兴趣的:(Spring,java,spring)