目录
一、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())"/>
测试类中的代码:
@Test
public 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>
运行结果和第二节上面相同