AOP ,即面向切面编程。其作用为对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,将它们独立到非业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。
Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。Joint point 是所有可能被织入 Advice 的候选的点, 在 Spring AOP中, 则可以认为所有方法执行点都是 Joint point,通俗的讲,连接点即表示类里面可以被增强的方法。
切点定义了何处,切点的定义会匹配通知所要织入的一个或多个连接点,我们通常使用明确的类的方法名称来指定这些切点,或是利用正则表达式定义匹配的类和方法名称来指定这些切点。PointCut 的作用就是提供一组规则来匹配 joinpoint, 给满足规则的 joinpoint 添加 Advice。
织入 Advice 的目标对象.。
将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
AOP 技术应该如何实现?这就不得不说我们以前学习过的一种设计模式了 – 代理模式。实际上实现 AOP 的方法有两种,一是静态织入,使编译器在编译期间织入有关方面的代码;二是动态代理,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行。
Spring AOP 采用的是动态代理方式,提供了JDKProxy 和 Cglib 两种方式来生成代理对象。
AOP 在 Spring 中有两种配置方式,一是 xml 配置的方式,二是自动注解的模式。我们平时一般不会使用 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.0</modelVersion> <groupId>edu.szu</groupId> <artifactId>AOPTest</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>AOPTest</name> <url>http://maven.apache.org</url> <properties> <spring.version>5.1.5.RELEASE</spring.version> <aspectj.version>1.9.0</aspectj.version> <cglib.version>2.2</cglib.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <!-- AspectJ --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>${aspectj.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>${cglib.version}</version> </dependency> </dependencies> </project>然后我们新建一个配置文件 applicationContext.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" 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"> <context:component-scan base-package="edu.szu.AOPTest"/> <!-- 激活自动代理功能 --> <aop:aspectj-autoproxy /> </beans>然后创建一个类
@Component public class BookManager { public void addBook() { System.out.println("增加书本"); } public void deleteBook() { System.out.println("删除书本"); } public void updateBook() { System.out.println("修改书本"); } public void selectBook() { System.out.println("查询书本"); } }新建一个切面,@Aspect 注解表示这是一个切面类。
@Aspect @Component public class AspectJAdvice { // 配置切点 @Pointcut("execution(* edu.szu.AOPTest.BookManager.addBook(..))") private void aspectJMethod(){}; // 配置连接点 方法开始执行时通知 @Before("aspectJMethod()") public void doBefore(JoinPoint joinPoint){ System.out.println("doBefore()"); } // 环绕通知 @Around("aspectJMethod()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("doAround()开始"); //核心逻辑 Object retval = pjp.proceed(); System.out.println("doAround()结束"); return retval; } // 方法执行完后通知 @After(value="aspectJMethod()") public void doAfter(JoinPoint joinPoint){ System.out.println("doAfter()"); } // 执行成功后通知 @AfterReturning(value="aspectJMethod()") public void doReturn(JoinPoint joinPoint){ System.out.println("doReturn()"); } // 抛出异常后通知 @AfterThrowing(value="aspectJMethod()", throwing="e") public void doThrowing(JoinPoint joinPoint,Exception e){ System.out.println("doThrowing() " + e); } }测试一下
public class Test { public static void main(String[] args) { System.out.println("开始测试!"); BeanFactory factory=new ClassPathXmlApplicationContext("applicationContext.xml"); BookManager bookManager=(BookManager) factory.getBean(BookManager.class); bookManager.addBook(); } }结果如图
可以看到,如果增强的方法中不抛出异常的话,@AfterThrowing 标注的方法不会执行,其他四种执行的顺序如图所示。
现在我们修改一下 addBook()方法,增加一个异常,看看执行的结果如何。
public void addBook() { System.out.println("增加书本"); int i = 1/0; }我们可以清晰的看到,当增强的方法抛出异常时,环绕通知后面那一半跟执行成功通知不再执行,并执行抛出异常后通知。
參考:细说Spring——AOP详解(AOP概览)