Spring AOP的实现原理

Posted jtz-79879

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring AOP的实现原理相关的知识,希望对你有一定的参考价值。

AOP(Aspect Orient Programming),我们一般称为面向方面(切面)编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等等。AOP实现的关键在于AOP框架自动创建的AOP代理,AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表。本文会分别对AspectJ和Spring AOP的实现进行分析和介绍。

 

实现原理

AOP的原理实际上就是动态代理

 

代理类

public class LoggingProxy {

private Calculate calculate;

LoggingProxy(Calculate calculate) {
this.calculate = calculate;
}

public Calculate getCalculateLog() {
Calculate calculateLog = null;

//代理对象由哪一个类加载器负责加载
ClassLoader loader = this.calculate.getClass().getClassLoader();
//代理对象的类型,有哪些方法
Class[] interfaces = new Class[]{Calculate.class};
InvocationHandler h = new InvocationHandler() {
/**
* proxy: 代理对象。 一般不使用该对象
* method: 正在被调用的方法
* args: 调用方法传入的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
//打印日志
System.out.println("调用方法 " + methodName + " 参数 " + Arrays.asList(args));

//调用目标方法
Object result = null;

try {
//前置通知

result = method.invoke(calculate, args);

//返回通知, 可以访问到方法的返回值
} catch (NullPointerException e) {
e.printStackTrace();
//异常通知, 可以访问到方法出现的异常
}

//后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值

//打印日志
System.out.println("结果: " + result);
return result;
}
};

calculateLog = (Calculate) Proxy.newProxyInstance(loader, interfaces, h);
return calculateLog;
}
}

定义的接口
public interface Calculate {
int add(int i, int j);
int sub(int i, int j);

int mul(int i, int j);
int div(int i, int j);
}

实现类
public class CalculateImpl implements Calculate {
@Override
public int add(int i, int j) {
return i + j;
}

@Override
public int sub(int i, int j) {
return i - j;
}

@Override
public int mul(int i, int j) {
return i * j;
}

@Override
public int div(int i, int j) {
return i / j;
}

public static void main(String[] args) {
Calculate calculate = new CalculateImpl();
Calculate arit = new LoggingProxy(calculate).getCalculateLog();
arit.add(1,3);
arit.sub(4,3);
arit.mul(4,5);
arit.div(6,3);
arit.div(6,0);

}


运行结果:

技术分享图片

AOP应用

/**
* 可以使用 @Order 注解指定切面的优先级, 值越小优先级越高
*/
@Order(2)
@Aspect
@Component
public class LoggingAspect {

/**
* 定义一个方法, 用于声明切入点表达式. 一般地, 该方法中再不需要添入其他的代码.
* 使用 @Pointcut 来声明切入点表达式.
* 后面的其他通知直接使用方法名来引用当前的切入点表达式.
*/
@Pointcut("execution(public int *.*(..))")
public void declareJointPointExpression(){}

/**
* 在 接口的每一个实现类的每一个方法开始之前执行一段代码
*/
@Before("declareJointPointExpression()")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
Object [] args = joinPoint.getArgs();

System.out.println("方法名 " + methodName + " 参数 " + Arrays.asList(args));
}

/**
* 在方法执行之后执行的代码. 无论该方法是否出现异常
*/
@After("declareJointPointExpression()")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("方法名 " + methodName + " 结束");
}

/**
* 在方法法正常结束受执行的代码
* 返回通知是可以访问到方法的返回值的!
*/
@AfterReturning(value="declareJointPointExpression()",
returning="result")
public void afterReturning(JoinPoint joinPoint, Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("方法名 " + methodName + " 返回结果 " + result);
}

/**
* 在目标方法出现异常时会执行的代码.
* 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
*/
@AfterThrowing(value="declareJointPointExpression()",
throwing="e")
public void afterThrowing(JoinPoint joinPoint, Exception e){
String methodName = joinPoint.getSignature().getName();
System.out.println("方法名 " + methodName + " 异常:" + e);
}

/**
* 环绕通知需要携带 ProceedingJoinPoint 类型的参数.
* 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
* 且环绕通知必须有返回值, 返回值即为目标方法的返回值
*/

@Around("declareJointPointExpression()")
public Object aroundMethod(ProceedingJoinPoint pjd){

Object result = null;
String methodName = pjd.getSignature().getName();

try {
//前置通知
System.out.println("方法名 " + methodName + " 参数 " + Arrays.asList(pjd.getArgs()));
//执行目标方法
result = pjd.proceed();
//返回通知
System.out.println("方法名 " + methodName + " 返回值 " + result);
} catch (Throwable e) {
//异常通知
System.out.println("方法名 " + methodName + " 异常:" + e);
throw new RuntimeException(e);
}
//后置通知
System.out.println("方法名 " + methodName + " 结束");

return result;
}
}
xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.atguigu.spring.aop"></context:component-scan>

<!-- 配置自动为匹配 aspectJ 注解的 Java 类生成代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>



































































































































































































以上是关于Spring AOP的实现原理的主要内容,如果未能解决你的问题,请参考以下文章

复习Spring第二课--AOP原理及其实现方式

Spring aop的实现原理

Spring中aop原理

spring源码剖析AOP实现原理剖析

Spring AOP 的使用和实现原理

Spring AOP 的使用和实现原理