Spring AOP

Spring AOP

  • 1.什么是Spring AOP?
  • 2.为什么要用Spring AOP?
  • 3.AOP的组成
  • 4.Spring AOP实现
  • 5.Spring AOP实现原理
  • 6.JDK和CGLIB实现的区别
  • 7.Spring AOP用户统一登录验证的问题
  • 8.统一异常处理
  • 9.统一数据返回格式

1.什么是Spring AOP?

AOP:面向切面编程,它是一种思想,对某一类事情集中处理。
OOP:面向对象编程。
AOP是一种思想,Spring AOP是一个框架,提供了一种对AOP思想的实现。

2.为什么要用Spring AOP?

举例:一个系统中,很多页面都需要先验证用户登录,这种情况下,如果采用都写一遍用户登录验证,当功能越来越多,登录验证也越来越多,代码修改和维护成本就比较高了,对于功能统一且使用地方较多的,就可以考虑AOP统一处理。
AOP可以实现:
1.统一的用户登录判断;
2.统一方法执行时间统计;
3.统一日志记录;
4.统一的返回格式设置;
5.统一的异常处理;
6.事务的开启和提交等;
AOP可以扩充多个对象的某个能力,AOP可以说是OOP的补充和完善。

3.AOP的组成

AOP的组成:
1.切面:针对与某一个功能的具体定义,某一个功能可能是登录验证功能,也有可能是日志记录功能。
2.切点:切面中的某一个方法。
3.连接点:用来匹配切面的多个点。
4.通知:在AOP的术语当中,切面的工作被称为通知。
通知可以分为:
前置通知使用@Before:通知方法会在目标方法调用之前执行;
后置通知使用@After:通知方法会在目标方法返回或者抛出异常后调用。
返回之后通知使用@AfterReturning:通知方法会在目标方法返回后调用。
抛出异常后通知使用@AfterThrowing:通知方法会在目标方法抛出异常后调用。
环绕通知使用@Around:通知包裹了被通知的方法,在被通知的方法通知之前和调用之后执行自定义的行为。

4.Spring AOP实现

Spring AOP实现步骤:
1.添加Spring AOP框架支持;
2.定义切面和切点;
3.定义通知。

1.添加Spring AOP框架支持;

Spring AOP_第1张图片

  <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.6.6</version>
        </dependency>

2.定义切面和切点

在一个类上面加注解@Aspect:表明当前类是一个切面
Spring AOP_第2张图片

在这里插入图片描述
切点表达式说明:
AspectJ支持三种通配符
1*:匹配任意字符,只匹配一个元素(包、类、方法、参数)
2 … :匹配任意字符,可以匹配多个元素,在表示类时,必须和*联合使用。
3 +:表示按照类型匹配指定类的所有类,必须跟在类名后面,如com.cad.Car+,表示继承该类的所有子类包括本身
切点表达式由切点函数组成,其中execution()是最常用的切点函数,用来匹配方法,语法为:
execution(<修饰符><返回类型><包.类.方法(参数)><异常>)
修饰符和异常可以省略

3.定义通知(前置通知、后置通知、环绕通知)

Spring AOP_第3张图片

5.Spring AOP实现原理

1.Spring AOP是构建在动态代理基础上,因此Spring对AOP的支持局限于方法级别的拦截。
2.动态代理分为两类:
1).JDK Porxy:JDK动态代理机制
2).CGLIB动态代理
3.代理的生成时机:
a)编译期;
b)类加载期;
c)运行期.

6.JDK和CGLIB实现的区别

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

7.Spring AOP用户统一登录验证的问题

用户登录拦截器实现:
1.创建一个拦截器:(判断用户是否登录,实现HandlerInterceptor接口)并重写preHandle
Spring AOP_第4张图片

package com.example.demo.config;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * 登录拦截器
 */
public class LoginInterretcept implements HandlerInterceptor {
    /**
     * 拦截方法
     */
    @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;
        }
        //401表示没有权限
        response.setStatus(401);
        return false;
    }
}

2.配置拦截器和拦截规则
Spring AOP_第5张图片

package com.example.demo.config;

/**
 * 全局配置文件
 */

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

@Configuration
public class AppConfig implements WebMvcConfigurer {
    /**
     * 配置拦截器和拦截规则
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterretcept())   //创建一个拦截器
                .addPathPatterns("/**")    //拦截所有请求
                .excludePathPatterns("/**/*.html")
                .excludePathPatterns("/**/*.css")
                .excludePathPatterns("/**/*.js")
                .excludePathPatterns("/user/*.login")
                .excludePathPatterns("/user/reg");
    }
    //设置api统一的访问前缀
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.addPathPrefix("api",c->true);
    }
}

8.统一异常处理

统一异常处理使用的是@ControllerAdvice+@ExceptionHandler来实现的,@ControllerAdvice表示控制器通知类,@ExceptionHandler是异常处理器,两个结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件。

1.先给类上添加一个@ControllerAdvice注解,标识当前的类为一个控制器的增强类。
2.添加具体的异常返回业务代码,并标识为@ExceptionHandler为异常统一处理方法。

Spring AOP_第6张图片

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;

/**
 * 统一异常处理
 */
@ControllerAdvice  //1.标识为控制器增强类
@ResponseBody
public class ErrorAdvice {
    //2.添加异常统一处理方法
    @ExceptionHandler(NullPointerException.class)
    public HashMap<String,Object> nullException(NullPointerException e){
        HashMap<String,Object> result=new HashMap<>();
        result.put("succ",200);     //http请求成功了,(大状态)
        result.put("state",-1);     //报错了(业务状态,小状态)
        result.put("message",e.toString());
        return result;
    }
    @ExceptionHandler(Exception.class)
    public HashMap<String,Object> exception(Exception e){
        HashMap<String,Object> result=new HashMap<>();
        result.put("succ",200);
        result.put("state",-1);
        result.put("message",e.toString());
        return result;
    }
}

9.统一数据返回格式

1.为什么需要统一数据返回格式?
a)方便前端程序员更好的接收和解析后端数据接口返回的数据;
b)减低前端程序员和后端程序员的沟通成本,按照某个格式实现就行了,因为所有接口都是这样返回的;
c)有利于项目统一数据的维护和修改;
d)有利于后端技术部门的统一规范的标准制定。
2.统一数据返回格式的实现:@ControllerAdvice+ResponseBodyAdvice的方式实现
Spring AOP_第7张图片

package com.example.demo.config;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.HashMap;

/**
 * 统一数据格式处理
 */
@ControllerAdvice       //1.
public class ResponseAdvice 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) {
        //统一数据格式的封装
        HashMap<String,Object> result=new HashMap<>();
        result.put("succ",200);     //返回大状态
        result.put("state",1);
        result.put("data",body);
        return result;

    }
}

你可能感兴趣的:(spring)