在CSDN上看了很多关于配置AOP的文章,不是很复杂就是省略了很多步骤。本文用最简单并且最清晰的方法从导入依赖到AOP底层原理。逐步讲解SpringAOP的相关操作帮你快速了解AOP。
目录
通过Maven仓库导入相关依赖
文件结构:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com</groupId> <artifactId>GarsonSpring</artifactId> <version>1.0-SNAPSHOT</version> <properties> <spring.version>5.2.2.RELEASE</spring.version> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>9</source> <target>9</target> </configuration> </plugin> </plugins> </build> <name>GarsonSpring</name> <dependencies> <!--测试需要的依赖--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>compile</scope> </dependency> <!--SpringAOP--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</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-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.12</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.12</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2</version> </dependency> </dependencies> </project>
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" 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"> <!--开启Aspect生成代理对象--> <aop:aspectj-autoproxy /> <!--开启注解扫描--> <context:component-scan base-package="SpringAOP.aop" /> </beans>
User:
package SpringAOP.aop; import org.springframework.stereotype.Component; @Component public class User { public void add(){ System.out.println("add..."); } }
UserProxy:
package SpringAOP.aop; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; //增强的类 @Component @Aspect//生成代理对象 public class UserProxy { //前置通知 @Before("execution(* SpringAOP.aop.*.*(..))") public void before(){ System.out.println("before"); } }
AOP(开始)
成功完成本模块中的<实际操作>就代表可以正确运行AOP
1、 切入点表达式:
(1)知道对哪个类里面的哪个方法进行增强
(2)语法结构:
此表达式表示对SpringAOP.aop包中的所有类的所有方法进行增强
@Pointcut("execution(* SpringAOP.aop.*.*(..))")
此表达式表示对SpringAOP.aop包中User类的add方法进行增强
@Pointcut("execution(* SpringAOP.aop.User.add(..))")
2、进行通知的配置
(1) 在spring配置文件中,开启注解扫描
·在“通过Maven仓库导入相关依赖”中就已设置
(2)使用注解创建User和UserProxy对象
·即在类前面增加@Component
·使得可以使用xml中的注解创建对象
(3)在增强类(UserProxy)上面添加注解@Aspect
·如果有AspectJ就生成代理对象
(4)在Spring配置文件中开启生成代理对象
·在“通过Maven仓库导入相关依赖”中就已设置
<实际操作(代码复制可直接运行)>
类:
package SpringAOP.aop; import org.springframework.stereotype.Component; @Component public class User { public void add(){ System.out.println("add..."); } }
增强类:
package SpringAOP.aop; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; //增强的类 @Component @Aspect//生成代理对象 public class UserProxy { //前置通知 @Before("execution(* SpringAOP.aop.*.*(..))") public void before(){ System.out.println("before"); } }
测试方法:
@Test public void test1() { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); User user = applicationContext.getBean("user", User.class); user.add(); }
测试结果:
总结:
AOP其实就是在一个固定的类外面套一层代理类,在代理类可以增加你希望的方法及操作但却不需要改变原始类。达到了解耦合的目的,更利于维护。
AOP术语
在结束上面的AOP引入,相信我们已经对AOP有一些了解了
接下来我们再了解更深入一点的AOP术语
1、横切关注点
从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个不同方面的增强。
这个概念不是语法层面天然存在的,而是根据附加功能的逻辑上的需要:有十个附加功能,就有十个横切关注点。
2、通知
每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。
· 前置通知:在被代理的目标方法前执行
· 返回通知:在被代理的目标方法成功结束后执行(寿终正寝)
· 异常通知:在被代理的目标方法异常结束后执行(死于非命)
· 后置通知:在被代理的目标方法最终结束后执行(盖棺定论)
· 环绕通知:使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对· 应的所有位置
3、切面
封装通知的类
4、目标
被代理的目标对象
5、代理
向目标对象应用通知之后创建的代理对象。
6、连接点
向目标对象应用通知之后创建的代理对象。
7、切入点
定位连接点的方式。
每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物(从逻辑上说)。
如果把连接点看作数据库中的记录,那么切入点就是查询记录的 SQL 语句。Spring 的 AOP 技术可以通过切入点定位到特定的连接点。切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
AOP五种通知
上文我们使用到了@Before。有了在指定方法前执行,
那就肯定有在后面、在异常抛出后、前后都执行...等,下面我们将介绍AOP的五种通知
@Before, 在 join point 前被执行的 advice. 虽然@Before是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常。
@AfterReturning, 在一个 join point 正常返回后执行的 advice
@AfterThrowing, 当一个 join point 抛出异常后执行的 advice
@After( = final), 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice
@Around, 在 join point 前和 joint point 退出后都执行AOP相同切入点的抽取
(很多人这一点讲的很复杂)
我用流程来说明:其实@Before的对象现在是pointCutDemo()方法了,但是pointCutDemo指向的是SpringAOP.aop.User类中的add()方法。所以Before还是会在add()方法之前执行
一句话:before() 在 pointCutDemo = User.add() 方法前执行
@Pointcut("execution(* SpringAOP.aop.User.add(..))") public void pointCutDemo(){} //前置通知 @Before("pointCutDemo()") public void before(){ System.out.println("before"); }
有多个增强类对同一个同一个方法进行增强
设置增强类优先级
就是有很多男生约一个女生吃饭,那女生就要给男生们排序号啦
有多个代理类想代理,我们给代理类设置一个优先级
在增强类上面添加注解@Order(数字类型值),数字类型值越小等级越高