Spring 核心技术 AOP 实例

Posted by 暮夏有五 on 2021-01-13
Estimated Reading Time 4 Minutes
Words 930 In Total
Viewed Times

AOP 简介


Aop(Aspect Oriented Programming),面向切面编程,这是对面向对象思想的一种补充。

面向切面编程,就是在程序运行时,不改变程序源码的情况下,动态的增强方法的功能,常见的使用场景非常多:

  1. 日志
  2. 事务
  3. 数据库操作
  4. ….

这些操作中,无一例外,都有很多模板化的代码,而解决模板化代码,消除臃肿就是 Aop 的强项。在 Aop 中,有几个常见的概念:

概念 说明
切点 要添加代码的地方,称作切点
通知(增强) 通知就是向切点动态添加的代码
切面 切点 + 通知
连接点 切点的定义

AOP 的实现

在 Aop 实际上集基于 Java 动态代理来实现的。Java 中的动态代理有两种实现方式:

  • cglib
  • jdk

动态代理


基于 JDK 的动态代理实例。

1. 定义一个计算器接口

1
2
3
4
public interface MyCalculator {

int add(int a, int b);
}

2. 定义计算机接口的实现

1
2
3
4
5
6
public class MyCalculatorImpl implements MyCalculator {

public int add(int a, int b) {
return a + b;
}
}

3. 定义代理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class CalculatorProxy {

public static Object getInstance(final MyCalculatorImpl myCalculator) {
return Proxy.newProxyInstance(CalculatorProxy.class.getClassLoader(), myCalculator.getClass().getInterfaces(), new InvocationHandler() {

/**
* @param proxy 代理对象
* @param method 代理的方法
* @param args 方法的参数
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName()+"方法开始执行啦...");
Object invoke = method.invoke(myCalculator, args);
System.out.println(method.getName()+"方法执行结束啦...");
return invoke;
}
});
}
}

Proxy.newProxyInstance 方法接收三个参数,第一个是一个 classloader,第二个是代理多项实现的接口,第三个是代理对象方法的处理器,所有要额外添加的行为都在 invoke 方法中实现。

XML 配置 AOP


1. 在 pom.xml 中引入 Spring 和 AOP 相关依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>

<!-- AOP Begin -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
<!-- AOP Over -->

2. 接下来,定义通知/增强,但是单纯定义自己的行为即可,不再需要注解

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
36
37
public class LogAspect {

public void before(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
String name = signature.getName();
System.out.println(name + "方法开始执行了...");
}

public void after(JoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
String name = signature.getName();
System.out.println(name + "方法执行结束了...");
}

public void returing(JoinPoint joinPoint,Integer r) {
Signature signature = joinPoint.getSignature();
String name = signature.getName();
System.out.println(name + "方法返回:"+r);
}

public void afterThrowing(JoinPoint joinPoint,Exception e) {
Signature signature = joinPoint.getSignature();
String name = signature.getName();
System.out.println(name + "方法抛异常了:"+e.getMessage());
}

public Object around(ProceedingJoinPoint pjp) {
Object proceed = null;
try {
//这个相当于 method.invoke 方法,我们可以在这个方法的前后分别添加日志,就相当于是前置/后置通知
proceed = pjp.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return proceed;
}
}

3. 接下来在 Spring 中配置 AOP

1
2
3
4
5
6
7
8
9
10
11
12
<bean class="com.antoniopeng.hello.spring.aop.LogAspect" id="logAspect"/>
<aop:config>
<aop:pointcut id="pc1" expression="execution(* com.antoniopeng.hello.spring.aop.commons.*.*(..))"/>

<aop:aspect ref="logAspect">
<aop:before method="before" pointcut-ref="pc1"/>
<aop:after method="after" pointcut-ref="pc1"/>
<aop:after-returning method="returing" pointcut-ref="pc1" returning="r"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="pc1" throwing="e"/>
<aop:around method="around" pointcut-ref="pc1"/>
</aop:aspect>
</aop:config>

4. 最后,在 Main 方法中加载配置文件

1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
MyCalculatorImpl myCalculator = ctx.getBean(MyCalculatorImpl.class);
myCalculator.add(3, 4);
myCalculator.min(5, 6);
}
}

更多干货请移步:https://antoniopeng.com


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !