Spring AOP全面详讲
目录
- 一、Spring AOP的概念
- 1、连接点JoinPoint
- 2、通知Advice
- 3、切入点PointCut
- 4、切面Aspect
- 二、xml文件实现AOP
- 1、前置通知(before)
- 2、后置通知
- 3、返回通知
- 4、异常通知
- 5、环绕通知
- 三、注解形式实现AOP
一、Spring AOP的概念
AOP:Aspect Oriented Programming(面向切面编程、面向方面编程),其实说白了,面向切面编程就是面向特定方法编程。
1、连接点JoinPoint
连接点的概念:可以被AOP控制的方法在SpringAOP提供的JoinPoint当中,封装了连接点方法在执行时的相关信息。
2、通知Advice
**通知:**指哪些重复的逻辑,也就是共性功能。
需要统计各个业务方法的执行耗时的,此时我们就需要在这些业务方法运行开始之前,先记录这个方法运行的开始时间,在每一个业务方法运行结束的时候,再来记录这个方法运行的结束时间。是在AOP面向切面编程当中,我们只需要将这部分重复的代码逻辑抽取出来单独定义。抽取出来的这一部分重复的逻辑,也就是共性的功能。
3、切入点PointCut
当通知和切入点结合在一起,就形成了一个切面。通过切面就能够描述当前aop程序需要针对于哪个原始方法,在什么时候执行什么样的操作。通过切入表达式找到切入点和通知进行配合。
4、切面Aspect
切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
当通知和切入点结合在一起,就形成了一个切面。通过切面就能够描述当前aop程序需要针对于哪个原始方法,在什么时候执行什么样的操作。
MAVEN项目如果需要使用AOP的情况,则需要再pom.xml文件中加入下面的依赖:
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.12</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!--AOP联盟--><dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version></dependency><!--Spring Aspects--><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.0.2.RELEASE</version></dependency><!--aspectj--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.3</version></dependency>
二、xml文件实现AOP
定义Cat类如下:
package com.example.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor
public class Cat {private String name;private int age;private void meow() {System.out.println("Meow!");}
}
定义People类如下:
package com.example.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class People {private String name;private int age;public void run(){System.out.println("running...");}
}
定义切面类如下:
package com.example.AOP;import org.aspectj.lang.annotation.Aspect;public class TestAOP {public static void yanzheng(){System.out.println(1);}
}
需要在spring.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:aop="http://www.springframework.org/schema/aop"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"><bean id="people" class="com.example.entity.People"></bean><bean id="cat" class="com.example.entity.Cat"></bean><bean id="testAop" class="com.example.AOP.TestAOP"/><aop:config><aop:aspect ref="testAop"><aop:before method="yanzheng" pointcut="execution(* com.example.entity.People.run())"/></aop:aspect></aop:config>
</beans>
根据上面的代码可以知道,如果想使用AOP的时候需要将类和切面类都加入到IoC容器当中。即:
<bean id="people" class="com.example.entity.People"></bean> <bean id="cat" class="com.example.entity.Cat"></bean> <bean id="testAop" class="com.example.AOP.TestAOP"/>
<aop:aspect ref="testAop">
上面这个代码块配置的是配置切面类 ref指的是切面类的bean对象。
1、前置通知(before)
xml文件配置如下:
<aop:before method="yanzheng" pointcut="execution(* com.example.entity.People.run())"/>
测试类中的代码:
@Testpublic void test1() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");People people = applicationContext.getBean("people", People.class);people.run();}
运行结果如下:
无论是否报错都会执行。
2、后置通知
<aop:after method="yanzheng" pointcut="execution(* com.example.entity.People.run())"/>
测试类运行结果如下:
无论是否报错都会执行。
3、返回通知
<aop:after-returning method="yanzheng" pointcut="execution(* com.example.entity.People.run())"/>
测试代码:
当切入点方法有错误时,将没有返回值。对people做一下修改,迫使方法报错,修改如下:
重新测试结果:
没有出现返回值,所以after-returning只有代码正确,才会返回。
4、异常通知
<aop:after-throwing method="yanzheng" pointcut="execution(* com.example.entity.People.run())"/>
当people类有错误的时候,代码执行结果如下:
After-throwing只有代码报错的时候才能执行。
5、环绕通知
<aop:around method="yanzheng" pointcut="execution(* com.example.entity.People.run())"/>
环绕通知会在切入方法的执行前运行,也会在切入方法执行后执行。因此通知方法需要做一下修改:
package com.example.AOP;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;public class TestAOP {public static void yanzheng(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println(2+"我特殊");proceedingJoinPoint.proceed();System.out.println(1+"我是AOP");}
}
运行结果如下:
三、注解形式实现AOP
需要将通知方法的类上假日
@Controller
@Aspect
使其成为切面类
通知方法定义如下:
@Around("execution(* com.example.entity.Cat.Meow())")public void yanzheng1(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {System.out.println(2+"我特殊");proceedingJoinPoint.proceed();System.out.println(1+"我是AOP");}@Before("execution(* com.example.entity.Cat.Meow())")public void yanzheng2() {System.out.println(1+"我是AOP");}@After("execution(* com.example.entity.Cat.Meow())")public void yanzheng3() {System.out.println(1+"我是AOP");}@AfterReturning("execution(* com.example.entity.Cat.Meow())")public void yanzheng4() {System.out.println(1+"我是AOP");}@AfterThrowing("execution(* com.example.entity.Cat.Meow())")public void yanzheng5() {System.out.println(1+"我是AOP");}
xml中的代码如下:
<context:component-scan base-package="com.example.entity"></context:component-scan><context:component-scan base-package="com.example.AOP"></context:component-scan><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
运行结果和第二节上面相同