AOP存在的主要目的就是为了解耦复杂业务,同时能够使业务具备较强的扩展性。

AOP面向切面的基本概念

  • 切面:

  • 连接点:在切入之后,需要插入代码的开始

  • 切入点:标记连接点的位置

  • 通知:在连接点之后要执行的代码,也就是要增强的部分,包括前置、后置、最终、环绕、最终五类

  • 目标对象:被代理的对象,也就是增强对象

Spring AOP 实现

实现一个用于记录日志的AOP

  1. 首先定义一个标记切入点、连接点的注解MyLog

    1
    2
    3
    4
    5
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD, ElementType.TYPE})
    public @interface MyLog {
    }
  2. 定义实际切入的Advice通知

    • 需要增加注解 @Aspect
    • 定义切入点:@Pointcut("@annotation(com.example.aopdemo.aop.anno.MyLog)")
    • 定义通知: @After@Before @Around
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    @Aspect
    @Component
    public class LogAspect {
    //这个方法定义了切入点
    @Pointcut("@annotation(com.example.aopdemo.aop.anno.MyLog)")
    public void pointCut() {}

    //这个方法定义了具体的通知
    @After("pointCut()")
    public void recordRequestParam(JoinPoint joinPoint) {
    for (Object s : joinPoint.getArgs()) {
    //打印所有参数,实际中就是记录日志了
    System.out.println("after advice : " + s);
    }
    }

    //这个方法定义了具体的通知
    @Before("pointCut()")
    public void startRecord(JoinPoint joinPoint) {
    for (Object s : joinPoint.getArgs()) {
    //打印所有参数
    System.out.println("before advice : " + s);
    }
    }

    //这个方法定义了具体的通知
    @Around("pointCut()")
    public Object aroundRecord(ProceedingJoinPoint pjp) throws Throwable {
    for (Object s : pjp.getArgs()) {
    //打印所有参数
    System.out.println("around advice : " + s);
    }
    return pjp.proceed();
    }
    }
  3. 在代理目标上加入切入点

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @RestController
    public class MockController {
    @RequestMapping("/mock")
    @MyLog
    public String helloAop(@RequestParam String key) {
    System.out.println("do something...");
    return "hello world";
    }
    }
  4. 测试Test

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @SpringBootTest
    public class MockControllerTest {
    @Autowired
    MockController mockController;

    @Test
    void helloAop() {
    mockController.helloAop("aop");
    }
    }

    image.png

实现一个类似登录验证的demo

1
2
3
4
5
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface LoginAnno {
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Aspect
@Component
public class LoginAspect {

private boolean res = true;

// 定义切入点
@Pointcut("@annotation(com.example.aopdemo.aop.anno.LoginAnno)")
public void pointCut() {}

// 定义具体的通知
@After("pointCut()")
public void adviceBoolean(JoinPoint joinPoint){
System.out.println("after : " + res);
}

@Before("pointCut()")
public void checkLogin(JoinPoint joinPoint){
List<Object> param = new ArrayList<>();
for (Object s: joinPoint.getArgs()){
param.add(s);
}
res = param.get(0).equals("admin") && param.get(1).equals("124");
System.out.println("before check login.....");
}

@Around("pointCut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
for (Object s : pjp.getArgs()){
System.out.println(s);
}
return pjp.proceed();
}
}
1
2
3
4
5
6
7
8
9
10
11
@RestController
public class ServiceController {

@RequestMapping("/service")
@LoginAnno
public String service(@RequestParam String username,
@RequestParam String password) {
System.out.println("验证成功");
return "service";
}
}

JDK动态代理实现

JDK动态代理要求被代理对象需要实现一个接口。

我们这里假设我们有一个Smms服务需要增强,其主要服务使发送验证码的消息。

SmmsService

1
2
3
interface SmmsService{
String sendMessage(String message);
}

SmmsServiceImpl

1
2
3
4
5
6
7
class SmmsServiceImpl implements SmmsService{
@Override
public String sendMessage(String message) {
System.out.println(message);
return message;
}
}

动态代理类 SmmsServiceProxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class SmmsServiceProxy extends InvocationHandler {
// 获得一个被代理的真实对象
private final Object target;
public SmmsServiceProxy(Object target) {
this.target = target;
}

// advice 增强方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 执行之前
System.out.println("before execute");
// 执行
Object res = method.invoke(target, args);
// 执行之后
System.out.println("after execute");
return res;
}
}

生成动态代理对象的工厂 JdkProxyFactory

1
2
3
4
5
class JdkProxyFactory {
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new SmmsServiceProxy(target));
}
}

实际使用

1
2
3
4
5
6
public class DynamicProxys {
public static void main(String[] args) {
SmmsService proxy = (SmmsService) JdkProxyFactory.getProxy(new SmmsServiceImpl());
proxy.sendMessage("hello");
}
}

image.png