简单的来说,就是动态的在方法的指定位置添加指定的代码。
为什么需要切面类?
在软件开发的过程中,有很多业务,特别是在编写核心业务的时候,往往需要很多其他的辅助业务,比如说身份验证(银行转账需要身份验证)、数据缓存、日志输出。这些往往在某个核心业务中处于辅助的部分。这些辅助的任务都有个特点,就是这些业务都处在核心业务的同一个切面上?
什么意思呢?
什么时候需要用切面类?
切面类有什么用?
日志的作用
日志如何实现
最简单的方法,在数据处理之前手动输出。
public void receiveMoney(int receiveMoney) throws ReceiveMoneyException {
System.out.println("[收钱]:参数为"+receiveMoney);
System.out.println("[收钱]数据处理中。。。。");
checkAmount(receiveMoney);
System.out.println("[收钱]数据处理事务完成完成");
}
这样我们的日志功能就可以实现了,但是,这只是其中一个辅助业务,一个项目中有很多业务,各种繁琐的功能和日志都实现在一个方法中,代码结构会无比的混乱,特别是一个日志功能和核心功能放在一起,很容易发生问题,并且一个业务中往往还要很多其他非核心的业务需要处理,比如说在接受钱之前,需要验明身份,来路不明的钱银行不能直接接收,若身份核验正确,那么接收到钱后还得进行数据缓存。
身份验证、数据缓存、异常处理等非核心业务如果处理不好往往会导致核心业务代码结构混乱。
那么怎样能将日志功能、身份验证加入到核心业务方法之中,但是不影响核心业务 的代码
AspectJ 支持 5 种类型的通知注解:
通知是啥?简单理解就是上面说到的辅助业务,我们在划分切面的提取辅助业务代码时候,会有以下情况
在maven的pom.xml中加入如下代码
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>Spring-AOPartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.3.14version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.3.14version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.3.14version>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.7version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
dependencies>
project>
因为我们要使用的是AspectJ中的注解,所以需要导入
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.7version>
dependency>
springconfig
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com"/>
beans>
测试类
@RunWith(SpringJUnit4ClassRunner.class)//这个需要spring-test依赖,使用后不需要创建IOC容器
@ContextConfiguration(value = "classpath:applicationContext.xml")
public class AOPTEST {
@Autowired
private Calc calc;
@Test
public void testAnnotationAOP(){
int add=calc.add(10,0);
System.out.println("外部 add"+add);
}
}
这篇文章我们先用有接口的形式来写切面类
前置通知(Before):在目标方法执行之前执行某段代码
后置通知(AfterReturning):在目标方执行完成后执行,如果目标方法异常,则后置通知不再执行某段代码
异常通知(Afterthrowing):目标方法抛出异常的时候执行某段代码
最终通知(After);不管目标方法是否有异常都会执行,相当于try…catch…finally中的finally。
环绕通知(Around):可以控制目标方法是否执行
切面类
@Aspect
@Component
public class LogAspect {
//前置通知
@Before(value = "execution(public int com.Calc.add(int ,int ))")
public void printLogBefore(){
System.out.println("[AOP前置通知]方法开始了");
}
//后置通知
@AfterReturning(value = "execution(public int com.Calc.add(int ,int ))")
//在返回通知中获取目标方法返回值分为两步,给returning设置一个名称,然后使用该名称在通知方法中声明一个对应的形参
public void printLogAfterSuccess(){
System.out.println("[AOP返回通知]方法成功返回了");
}
//异常通知
@AfterThrowing(value ="execution(public int com.Calc.add(int ,int ))")
public void printLogAfterException(){
System.out.println("[AOP异常通知]方法抛出异常");
}
//结束通知
@After("execution(public int com.Calc.add(int ,int ))")
public void printLogFinish(){
System.out.println("[AOP结束通知]方法结束了");
}
}
再来看看测试方法
@Test
public void testAnnotationAOP(){
int add=calc.add(10,0);//调用
System.out.println("外部 add"+add);
}