当前位置 : 主页 > 编程语言 > java >

Spring AOP保姆级超详细解析(下)

来源:互联网 收集:自由互联 发布时间:2022-10-15
##前言上一篇文章我们详细介绍了使用动态代理的方式实现AOP,动态代理的方式对于不了解反射机制的小伙伴们可能比较难以理解,Spring对AOP进行了封装,可以使用面向对象的方式来实

##前言 上一篇文章我们详细介绍了使用动态代理的方式实现AOP,动态代理的方式对于不了解反射机制的小伙伴们可能比较难以理解,Spring对AOP进行了封装,可以使用面向对象的方式来实现AOP。我们这篇文章就来介绍下使用面向对象的方式来实现AOP ##面向对象实现AOP ###实现原理

Spring框架中不需要创建InvocationHandler,只需要创建一个切面对象, 将所有的非业务代码在切面对象中完成即可,Spring 框架底层会自动根据切面类以及目标类生成一个代理对象。就不需要使用动态代理来实现AOP了。 ###代码实现 ####接我们上一篇文章的代码。 我们先定位切面的位置。

首先,在方法执行之前,我们需要打印方法的参数。

其次,方法返回返回值之前,打印计算结果。

然后,方法执行完成后,输出日志执行完成。

最后,如果方法有异常,则抛出异常。

定位完切面位置,我们需要创建一个类来统一管理非业务代码。

public class ConsoleLog {

}

####根据我们所定位的切面的位置实现日志打印功能。 我们使用注解的方式来实现AOP 在方法执行之前,打印方法的参数

@Aspect
@Component
public class ConsoleLog{
@Before("execution(public int com.impl.CalImpl.*(..))")
public void before(JoinPoint joinPoint){
//获取方法名
String name = joinPoint.getSignature().getName();
//获取方法参数
String arr = Arrays.toString(joinPoint.getArgs());
System.out.println(name+"方法的参数是"+arr);
}
}

代码解析: @Before表示在方法执行之前去切入。括号中参数表示需要切入类的方法。*是通配符,表示匹配该类中的所有方法。..同样也是通配符,表示匹配该方法的所有参数。 为了获取我们切入的方法的一些信息,我们需要在before方法中传入JoinPoint对象,JoinPoint用于连接我们的目标方法。 然后,我们通过joinPoint的getSignature()方法的getName()获取到目标方法的方法名。 通过joinPoint的getArgs()方法来获取目标方法的参数,从而实现在目标方法执行前打印出方法的参数。 前面我们说过,Spring框架会根据我们的切面类自动生成一个代理对象。但实际上,Spring并不是通过切面类去解析的,而是通过切面类的对象去解析的,所以我们要在ConsoleLog切面类前面加上@Component注解。这个注解的意思就相当于我们在spring.xml文件中配置了一个ConsoleLog切面类的bean,在加这个注解之前,我们需要在pom.xml文件中配置spring-context依赖。

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.11.RELEASE</version>
</dependency>

但这样配置完,ConsoleLog还只是个普通对象,我们需要它成为切面对象,在前面的动态代理的方式中是通过实现InvocationHandler接口来成为切面对象的,我们这里直接加上@Aspect注解即可实现。 最后,我们来配置spring.xml文件,我们需要配置的是spring扫描这个类,然后添加注解。

<!--自动扫描包-->
<context:component-scan base-package="com"></context:component-scan>

<!--使Aspect注解生效,为目标类自动生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
复制代码
base-package属性值为所需要扫面包的包名。
注意:我们添加<aop:aspectj-autoproxy></aop:aspectj-autoproxy>之前需要在spring.xml文件中引入aop命名空间。
xmlns:aop="http://www.springframework.org/schema/aop"

获取代理对象并调用

import com.Cal;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App2 {
public static void main(String[] args) {
//加载配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
//获取代理对象
Cal cal = (Cal) applicationContext.getBean("calImpl");
cal.add(2,3);
}
}

日志打印结果:

add方法的参数是[2, 3]

这样我们就完成了我们第一个定位的切面的位置。 方法执行完成后,输出日志执行完成

@After("execution(public int com.impl.CalImpl.*(..))")
public void after(JoinPoint joinPoint){
String name = joinPoint.getSignature().getName();
System.out.println(name+"方法已执行完成");
}

@After表示在方法执行之后去切入。括号中的参数与前面@Before相同。 日志打印结果:

add方法已执行完成

方法返回返回值之前,打印计算结果

@AfterReturning(value = "execution(public int com.impl.CalImpl.*(..))",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result){
String name = joinPoint.getSignature().getName();
System.out.println(name+"方法执行的结果是"+result);
}

@AfterReturning表示在方法返回返回值之前切入,value属性值为需要切入类的方法,returning属性值为需要获取的返回值,该属性值需要与下面方法参数中Object名相同。 日志打印结果:

add方法执行的结果是5

如果方法有异常,则抛出异常

@AfterThrowing(value = "execution(public int com.impl.CalImpl.*(..))",throwing = "exception")
public void afterThrowing(JoinPoint joinPoint,Exception exception){
String name = joinPoint.getSignature().getName();
System.out.println(name+"方法抛出异常:"+exception);
}

@AfterThrowing表示在方法抛出异常时切入,value属性@AfterReturning相同,throwing属性值为所需要抛出的异常名,需要与下面方法中的Exception名对应。 这样我们的切面类就比较完整了,基本实现了我们所需要的日志打印功能。

上一篇:一文了解InnoDB存储结构
下一篇:没有了
网友评论