大家好,我们又见面了,我是你们的朋友全栈君。
视频功能已经通过审核,大家可以观看视频了!记得关注哦~
注意:由于网络原因,视频的前一两分钟可能会有些模糊,稍等一会儿就会清晰。
Spring AOP系列将帮助你深入了解Spring AOP的背景和原理。
在上一篇中,我们讨论了动态代理:Java中动态代理的两种方式——JDK动态代理和cglib动态代理,以及它们的区别。
我们使用上一篇的方法来增强目标方法,实现代码的解耦是可行的,但仍然需要自己生成代理对象,手动编写拦截器,在拦截器中手动将增强内容与目标方法结合起来,这样的操作还是有些繁琐。那么,有没有更好的解决方案呢?
答案是:有的!这就是Spring的AOP,这是我们最终想要引出的重点!
有了Spring的AOP后,就不需要自己编写代码了,只需在配置文件中进行配置,告诉Spring哪些类需要生成代理类,哪个类是增强类,以及是在目标方法执行前还是执行后进行增强。配置好这些后,Spring会根据你的配置生成代理对象,并将增强内容与目标方法结合起来。这相当于自己编写代码也能实现与AOP类似的功能,但有了Spring AOP后,Spring帮你做了这些事情,而且Spring将其做成了可配置化,使用起来非常简单且灵活。
我们之前使用的JDK动态代理和cglib动态代理,这两种在Spring的AOP中都有应用。Spring会根据不同的情况决定是使用JDK的动态代理生成代理对象,还是使用cglib生成代理对象,本篇会详细讲解这些内容。
在讨论Spring AOP之前,需要了解一些涉及到的术语。
1、Spring AOP中的术语概念 翻阅Spring AOP相关的文档,会发现里面有很多概念性的东西,很多术语和概念都写得很玄乎,读好几遍都读不懂。我个人认为以下几个术语是关键,必须掌握:
切面类:就是要执行的增强方法所在的类,比如我们例子中的MyTransaction类。
通知:切面类中的增强方法,比如我们例子中的beginTransaction()方法和commit()方法。
目标方法:要执行的目标方法,比如我们例子中的savePerson()方法。
织入:将通知和目标方法结合,形成代理对象的过程称为织入。
代理对象的方法 = 通知(增强方法) + 目标方法
上述解释是我个人的理解。
了解这些术语后,下面我们通过代码来实现一下,感受一下Spring AOP的魅力。
2、使用Spring AOP的代码实现
package com.cj.study.spring.aop;
public interface PersonService {
public String savePerson();
public void updatePerson();
public void deletePerson();
}package com.cj.study.spring.aop;
public class PersonServiceImpl implements PersonService {
@Override
public String savePerson() {
System.out.println("添加");
return "保存成功!";
}
@Override
public void updatePerson() {
System.out.println("修改");
}
@Override
public void deletePerson() {
System.out.println("删除");
}
}package com.cj.study.spring.aop;
//切面类
public class MyTransaction {
//切面里的通知方法
public void beginTransaction(){
System.out.println("开启事务 ");
}
//切面里的通知方法
public void commit(){
System.out.println("提交事务");
}
}package com.cj.study.spring.aop;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AopTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("com/cj/study/spring/aop/applicationContext.xml");
PersonService proxyPersonService = (PersonService) context.getBean("personService");
String returnValue = proxyPersonService.savePerson();
System.out.println(returnValue);
}
}<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean class="com.cj.study.spring.aop.PersonServiceImpl" id="personService"></bean>
<bean class="com.cj.study.spring.aop.MyTransaction" id="myTransaction"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.cj.study.spring.aop.*.*(..))" id="perform"/>
<aop:aspect ref="myTransaction">
<aop:before method="beginTransaction" pointcut-ref="perform"/>
</aop:aspect>
<aop:aspect ref="myTransaction">
<aop:after method="commit" pointcut-ref="perform"/>
</aop:aspect>
</aop:config>
</beans>可以设置断点查看:
发现返回的对象是$Proxy4,说明返回的是代理类的对象。
最终的运行结果:
效果和我们自己编写的一样,但Spring AOP将其做成了配置化的,使用起来非常方便。
讲完例子后,下面讲解具体的细节。
3、AOP配置文件内容讲解
这是我们上述例子中的配置文件。
(1)、导入目标类和切面类,就是将目标类和切面类放入Spring容器中,让Spring帮你生成。
(2)、aop:pointcut 是指切入点。
(3)、expression 是指切入点表达式。
(4)、aop:aspect 是指上边讲的切面类。
(5)、aop:before、aop:after 是指上边讲的通知,通知有很多种,前置通知、后置通知、环绕通知、最终通知、异常通知,下面会详细讲解。
3.1、切入点表达式execution 切入点和切入点表达式是用来告诉Spring哪些类需要Spring为你生成代理对象,这个非常重要。
要彻底了解这个表达式的意思,首先需要知道Java中一个方法最完整的描述是什么样的。
截图上的execution是AOP文档中给出的表达式示例,下面这一行是Java中一个方法最完整的描述(以Object中的wait方法为例)。
对应的含义我进行了标注,并且与图上的execution表达式做了一一对应。
下面举几个表达式的例子,进一步帮助大家理解(*代表通配符):
execution(public * *(..)) 所有的公共方法
execution(* set*(..)) 以set开头的任意方法
execution(* com.ci.service.AccountService.*(..)) com.cj.service.AccountService类中的所有的方法
execution(* com.cj.service.*.*(..)) com.cj.service包中的所有的类的所有的方法
execution(* com.cj.service..*.*(..)) com.cj.service包及子包中所有的类的所有的方法
execution(* com.cj.spring.aop..*.*(String,?,Integer)) com.cj.spring.aop包及子包中所有的类的有三个参数
且第一个参数为String,第二个参数为任意类型,
第三个参数为Integer类型的方法理解了这些后,我们就可以根据自己想配置的路径进行配置了。
3.2、AOP中的各种通知
通知:
1、前置通知
1、在目标方法执行之前执行
2、无论目标方法是否抛出异常,都执行,因为在执行前置通知的时候,目标方法还没有执行,还没有遇到异常
2、后置通知
1、在目标方法执行之后执行
2、当目标方法遇到异常,后置通知将不再执行
3、后置通知可以接受目标方法的返回值,但是必须注意:
后置通知的参数的名称和配置文件中returning="var"的值是一致的
3、最终通知:
1、在目标方法执行之后执行
2、无论目标方法是否抛出异常,都执行,因为相当于finally
4、异常通知
1、接受目标方法抛出的异常信息
2、步骤
在异常通知方法中有一个参数Throwable ex
在配置文件中
<after-throwing method="throwingMethod" pointcut-ref="perform" throwing="ex"></after-throwing>
5、环绕通知
1、如果不在环绕通知中调用ProceedingJoinPoint的proceed,目标方法不会执行
2、环绕通知可以控制目标方法的执行3.3、Spring AOP的具体加载步骤,理解这个非常重要!!!
springAOP的具体加载步骤:
1、当spring容器启动的时候,加载了spring的配置文件
2、为配置文件中所有的bean创建对象
3、spring容器在创建对象的时候它会解析aop:config的配置
解析切入点表达式,用切入点表达式和纳入spring容器中的bean做匹配
如果匹配成功,则会为该bean创建代理对象,代理对象的方法=目标方法+通知
如果匹配不成功,则为该bean创建正常的对象
其实就是你通过表达式告诉Spring哪些bean需要它帮你生成代理对象而不是生成原有的正常对象
理解这一点相当重要!
4、在客户端利用context.getBean获取对象时,如果该对象有代理对象则返回代理对象,如果没有代理对象,则返回目标对象
注意:如果目标类实现了接口,spring容器会采用jdk的动态代理产生代理对象,产生的代理类和目标类实现了相同的接口;如果目标类没实现接口,spring容器会采用cglib的方式产生代理对象,产生的代理类是目标类的子类以上就是Spring中的AOP以及切入点表达式和各种通知的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号