赞
踩
目录
AOP(Aspect Oriented Programming):面向切面编程,它是⼀种思想,它是对某⼀类事情的
集中处理。在我们想要对某一件事情进行集中处理,就可以使用到AOP,它提供一种将程序中的横切关注点模块化的方式。在 AOP 中,我们将这些横切关注点称为“切面”,它们独立于业务逻辑模块,但是可以在程序运行的不同阶段被织入到业务逻辑中。
简单来说,AOP 就是对某一件事进行集中处理的思想方式~
切⾯(Aspect)由切点(Pointcut)和通知(Advice)组成,它既包含了横切逻辑的定义,也包
括了连接点的定义。相当于处理某方面具体问题的一个类,包含多个方法,而这些方法就是切点和通知。
Pointcut 的作⽤就是提供⼀组规则来匹配连接点(Join Point),给满足规则的连接点添加通知(Advice),可以理解为用来进行主动拦截的规则(配置)
应⽤执⾏过程中能够插⼊切⾯的⼀个点,连接点可以理解为可能会触发AOP规则的所有点。(所有请求)
在AOP术语中,切面的工作被称之为通知。通知是切面在连接点上执行的动作。它定义了在何时(例如在方法调用之前或之后)以及如何(例如打印日志或进行性能监控)应用切面的行为。即,程序中被拦截请求触发的具体动作。
Spring 切⾯类中,可以在方法上使⽤以下注解,会设置⽅法为通知方法,在满⾜条件后会通知本
⽅法进⾏调⽤:
在做任何一个系统都需要登录功能,那么几乎想要使用这个系统都需要我们进行验证用户登录状态,我们之前的处理⽅式是每个 Controller 都要写⼀遍⽤户登录验证,然⽽当你的功能越来越多,那么你要写的登录验证也越来越多,⽽这些⽅法⼜是相同的,这么多的⽅法就会代码修改和维护的成本。对于这种功能统⼀,且使⽤的地⽅较多的功能,就可以考虑 AOP来统⼀处理了。
除了统一登录判断外,使用AOP还可以实现:
Spring AOP 的实现步骤如下:
- <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-bo
- ot-starter-aop -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-aop</artifactId>
- </dependency>
使用 @Aspect
注解表明当前类为一个切面,而在切点中,我们要定义拦截的规则,具体实现如下:
- @Component // 随着框架的启动而启动
- @Aspect // 告诉框架我是一个切面类
- public class UserAspect {
-
- // 定义切点(配置拦截规则)
- @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
- public void pointcut(){
-
- }
- }
在上述实现代码中,pointcut 为一个空方法,只是起到一个“标识”的作用,标识下面的通知方法具体指的是哪个切点,切点可以有多个。
切点表达式由切点函数组成,其中 execution()
是最常⽤的切点函数,⽤来匹配⽅法,语法为:
execution(<修饰符><返回类型><包.类.⽅法(参数)><异常>)
修饰符和异常可以省略
常见的切点表达式的示例:
- package com.example.demo.controller;
-
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- @RequestMapping("/user")
- public class UserController {
-
- @RequestMapping("/hi")
- public String sayHi(String name){
- System.out.println("执行了Hi");
- return "Hi," + name;
- }
-
- @RequestMapping("/hello")
- public String sayHello(){
- System.out.println("执行了Hello");
- return "Hello,world";
- }
- }
通知定义的是被拦截方法具体要执行的业务。我们上面列出了可以使用哪些通知~这里举出例子
- package com.example.demo.aop;
-
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.*;
- import org.springframework.stereotype.Component;
-
- @Component // 随着框架的启动而启动
- @Aspect // 告诉框架我是一个切面类
- public class UserAspect {
-
-
- // 定义切点(配置拦截规则)
- @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
- public void pointcut(){
-
- }
-
- @Before("pointcut()")
- public void beforeAdvice(){
- System.out.println("执行了前置通知~");
- }
-
- @After("pointcut()")
- public void AfterAdvice(){
- System.out.println("执行了后置通知~");
- }
-
- /**
- * 环绕通知
- * @param joinPoint
- * @return
- */
- @Around("pointcut()")
- public Object aroundAdvice(ProceedingJoinPoint joinPoint){
- System.out.println("进入了环绕通知~");
- Object obj = null;
- try {
- // 执⾏拦截⽅法
- obj = joinPoint.proceed();
- } catch (Throwable e) {
- e.printStackTrace();
- }
- System.out.println("退出了环绕通知~");
- return obj;
- }
-
- }
环绕通知是在前置通知之前和后置通知之后运行的~
Spring AOP 是通过动态代理的⽅式,在运⾏期将 AOP 代码织⼊到程序中的,它的实现⽅式有两种:JDK Proxy
和 CGLIB
。因此,Spring 对 AOP 的支持局限于方法级别的拦截。
动态代理(Dynamic Proxy)是一种设计模式,它允许 在运行时创建代理对象,并将方法调用转发给实际的对象。 动态代理可以用于实现横切关注点(如日志记录、性能监控、事务管理等)的功能,而无需修改原始对象的代码。
在Java中,动态代理通常使用 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来实现。
调用者在调用方法时,会先转发给代理类创建的代理对象,随后再由代理对象转发给目标对象。
以下是使用动态代理的一般步骤:
先通过实现 InvocationHandler 接⼝创建⽅法调⽤处理器,再通过 Proxy 来创建代理类。
- import org.example.demo.service.AliPayService;
- import org.example.demo.service.PayService;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- //动态代理:使⽤JDK提供的api(InvocationHandler、Proxy实现),此种⽅式实现,要求被代理类必须实现接⼝
- public class PayServiceJDKInvocationHandler implements InvocationHandler {
- //⽬标对象即就是被代理对象
- private Object target;
- public PayServiceJDKInvocationHandler( Object target) {
- this.target = target;
- }
- //proxy代理对象
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- //1.安全检查
- System.out.println("安全检查");
- //2.记录⽇志
- System.out.println("记录⽇志");
- //3.时间统计开始
- System.out.println("记录开始时间");
- //通过反射调⽤被代理类的⽅法
- Object retVal = method.invoke(target, args);
- //4.时间统计结束
- System.out.println("记录结束时间");
- return retVal;
- }
- public static void main(String[] args) {
- PayService target= new AliPayService();
- //⽅法调⽤处理器
- InvocationHandler handler =
- new PayServiceJDKInvocationHandler(target);
- //创建⼀个代理类:通过被代理类、被代理实现的接⼝、⽅法调⽤处理器来创建
- PayService proxy = (PayService) Proxy.newProxyInstance(
- target.getClass().getClassLoader(),
- new Class[]{PayService.class},
- handler
- );
- proxy.pay();
- }
- }
- import org.springframework.cglib.proxy.Enhancer;
- import org.springframework.cglib.proxy.MethodInterceptor;
- import org.springframework.cglib.proxy.MethodProxy;
- import org.example.demo.service.AliPayService;
- import org.example.demo.service.PayService;
- import java.lang.reflect.Method;
-
- public class PayServiceCGLIBInterceptor implements MethodInterceptor {
- //被代理对象
- private Object target;
- public PayServiceCGLIBInterceptor(Object target){
- this.target = target;
- }
- @Override
- public Object intercept(Object o, Method method, Object[] args, Method
- Proxy methodProxy) throws Throwable {
- //1.安全检查
- System.out.println("安全检查");
- //2.记录⽇志
- System.out.println("记录⽇志");
- //3.时间统计开始
- System.out.println("记录开始时间");
- //通过cglib的代理⽅法调⽤
- Object retVal = methodProxy.invoke(target, args);
- //4.时间统计结束
- System.out.println("记录结束时间");
- return retVal;
- }
- public static void main(String[] args) {
- PayService target= new AliPayService();
- PayService proxy= (PayService) Enhancer.create(target.getClass(),n
- ew PayServiceCGLIBInterceptor(target));
- proxy.pay();
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。