AOP(Aspect Oriented Programming):⾯向切⾯编程,它是⼀种思想,它是对某⼀类事情的集中处理。而Spring AOP是AOP具体的实现。
比如:写博客系统时,用户在进行观看和书写的时候,需要服务器去验证用户的登录状态,以前的写法是在每个执行程序的加入判断用户状态,现在用AOP就可以做到。集中处理这些。
之前的处理⽅式是每个 Controller 都要写⼀遍⽤户登录验证,然⽽当你的功能越来越多,那么你要写的登录验证也越来越多,⽽这些⽅法⼜是相同的,这么多的⽅法就会代码修改和维护的成本。
对于这种功能统⼀,且使⽤的地⽅较多的功能,就可以考虑 AOP
来统⼀处理了。
AOP可以实现。
1.切面(Aspect):定义的是事件(AOP是做啥的):比如用户登录校验(定义方向)
2.切点(Pointcut):定义具体规则:比如定义用户登录拦截规则,那些接口判断用户登录权限?哪些不判断(定制具体方案)
3.通知(Advice):AOP执行的具体方法:比如获取用户登录信息,如果获取到说明已经登录,否则未登录。(具体业务执行者)
分类:前置通知,后置通知,环绕通知,异常通知,返回通知
4.连接点(join poit):有可能触发切点的所有点:比如所有的接口,链接所有可能触发条件 的接口
(拦截)
1.添加Spring AOP依赖
2.定义切面
3.定义切点
4.实现通知
面板中不存在,不热门的插件,所以这个需要手动添加到pom.xml中
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-aopartifactId>
dependency>
使用注解 @Pointcut
AspectJ ⽀持三种通配符
@Aspect
@Component
public class UserAspect {
@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
public void pointcut(){
}
@Before("pointcut()")
public void doBefore(){
System.out.println("执行了前置通知");
}
@After("pointcut()")
public void doAfter(){
System.out.println("执行了后置通知");
}
@AfterReturning("pointcut()")
public void doAfterReturning(){
System.out.println("执行了返回之后通知");
}
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知执行之前");
//执行目标方法
Object result=joinPoint.proceed();
System.out.println("环绕通知执行之后");
return result;
}
}
环绕通知: AOP 统计 UserController 每个⽅法的执⾏时间。
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕通知执行之前");
//执行目标方法
Object result=joinPoint.proceed();
System.out.println("环绕通知执行之后");
return result;
}
动态代理组成:
1.JDK Proxy-------》代理对象必须实现接口,才能使用JDK Proxy。
2.CGLIB-------》通过是实现代理类的子类来实现动态代理(被final修饰的类是不能被代理)
JDK Proxy vs CGLIB 的区别
1.出身不同:
JDK Proxy出身于JDK
CGLIB出身第三方库
2.实现不同:
3.性能不同:
Spring 拦截器:
HandlerInterceptor,拦截器的实现分为以下两个步骤:
创建⾃定义拦截器
重写:preHandle
/*
* 自定义拦截器
* */
public class UserInterceptor implements HandlerInterceptor {
//返回 true -》拦截器校验成功,继续执行后续的方法
//返回false -> 拦截器校验失败,不会执行后续的目标方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
//业务方法
HttpSession session=request.getSession();
if (session != null&&session.getAttribute(AppVar.SESSION_KEY)!=null) {
return true;
}
return false;
}
}
将⾃定义拦截器加⼊ WebMvcConfigurer
全局配置文件
@Configuration
public class AppConfig implements WebMvcConfigurer {
@Autowired
private UserInterceptor userInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(userInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/user/reg")//拦截所有的请求,然后在选择放权限
.excludePathPatterns("/user/login")
;
}
}
控制层Controller
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/getuser")
public String getUser(){
System.out.println("do getUser");
return "get user";
}
@RequestMapping("/getUser")
public String delUser(){
System.out.println("do getUser");
return "del user";
}
@RequestMapping("/reg")
public String doReg(){
System.out.println("do reg");
return "get reg";
}
@RequestMapping("/login")
public String dologin(){
System.out.println("do login");
return "get login";
}
}
设置的全局变量
//全局变量
public class AppVar {
//session key值
public static final String SESSION_KEY="SESSION_KEY";
}
说明:以上拦截规则可以拦截此项⽬中的使⽤ URL,包括静态⽂件(图⽚⽂件、JS 和 CSS 等⽂件)。
1.@ControllerAdvice/@RestControllerAdvice
2.@ExceptionHandler(Exception.class)统一返回对象。
//一个特定的异常处理
@RestControllerAdvice
public class ExceptionAdvice {
@ExceptionHandler(NullPointerException.class)
public ResultAjax doNullPointerException(NullPointerException e){
ResultAjax resultAjax=new ResultAjax();
resultAjax.setCode(-1);
resultAjax.setMsg("空指针异常:"+e.getMessage());
resultAjax.setObject(null);
return resultAjax;
}
}
//全部的异常处理
@ExceptionHandler(Exception.class)
public ResultAjax doException(Exception e){
ResultAjax resultAjax=new ResultAjax();
resultAjax.setCode(-1);
resultAjax.setMsg("空指针异常:"+e.getMessage());
resultAjax.setObject(null);
return resultAjax;
}
统⼀的数据返回格式可以使⽤ @ControllerAdvice +
@ResponseBodyAdvice 的⽅式实现.
后置执行,无论如何都得执行
这个呢,也叫保底策略。
/*
* 实现返回值的保底实现类
*
* */
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
/*
* true-->才会调用beforBody方法
* false-->不会会调用beforBody方法
* */
@Resource
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) {
//已经包装好的对象
if (body instanceof ResultAjax){
return body;
}
//无法接收String类型
//对字符串进行单独的判断和数据
if (body instanceof String){
ResultAjax resultAjax=ResultAjax.succ(body);
try {
return objectMapper.writeValueAsString(resultAjax);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
return ResultAjax.succ(body);
}
}
总结: