Spring中的AOP以及切入点表达式和各种通知

Spring中的AOP以及切入点表达式和各种通知上篇讲了动态代理:Java中动态代理的两种方式JDK动态代理和cglib动态代理以及区别我们用上篇的做法去实现目标方法的增强,实现代码的解耦,是没有问题的,但是还是需要自己去生成代理对象,自己手写拦截器,在拦截器里自己手动的去把要增强的内容和目标方法结合起来,这用起来还是有点繁琐,有更好的解决方案吗?答案是:有的!那就是Spring的AOP,这才是咱们最终想引出来的重点!有了Sprin…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

视频功能审核通过了,可以看视频啦!记得点关注啊~

注意:因为网络原因,视频前一两分钟可能会比较模糊,过一会儿就好了

记得点关注啊,视频里的wx二维码失效了,wx搜索:“聊5毛钱的java 或 扫码关注公众号,欢迎一起学习交流

Spring中的AOP以及切入点表达式和各种通知

快扫码关注啦!关注可领取博主的Java学习视频+资料,保证都是干货

SpringAOP系列,帮你了解SpringAOP的来龙去脉

上篇讲了动态代理: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 id="personService" class="com.cj.study.spring.aop.PersonServiceImpl"></bean>
	<!-- 导入切面 -->
	<bean id="myTransaction" class="com.cj.study.spring.aop.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>

可以断点看一下

Spring中的AOP以及切入点表达式和各种通知

发现返回的对象是$Proxy4,说明返回的是代理类的对象

最后的运行结果

Spring中的AOP以及切入点表达式和各种通知

效果和咱们自己去写的一样,但是Spring AOP它做成了配置化的,用起来非常的爽。

讲完例子后,下面讲一下具体的细节

3、AOP配置文件内容讲解

Spring中的AOP以及切入点表达式和各种通知

这是咱们上述例子里的配置文件

(1)、导入目标类和切面类,就是把目标类和切面类放入Spring容器里,让Spring帮你生成

(2)、aop:pointcut 是指切入点

(3)、expression 是指切入点表达式

(4)、aop:aspect 是指上边讲的切面类

(5)、aop:before、aop:after 是指上边讲的通知,通知有很多种,前置通知、后置通知、环绕通知、最终通知、异常通知,等下会详细讲

3.1、切入点表达式execution

切入点和切入点表达式是用来告诉Spring你的哪些类需要Spring给你生成代理对象,这个很重要

要彻底了解这个表达式的意思,首先需要知道Java里一个方法最完整的描述长什么样子

Spring中的AOP以及切入点表达式和各种通知

截图上的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
           在配置文件中
              <aop:after-throwing method="throwingMethod" pointcut-ref="perform" throwing="ex"/>
   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的一些理解和总结

别人一问,spirng aop的原理,都知道是动态代理,但是具体的不太清楚

通过前两篇文章:

Java中的代理模式——静态代理以及分析静态代理的缺点

Java中动态代理的两种方式JDK动态代理和cglib动态代理以及区别

再加上本篇,这三篇下来,基本上可以清楚Spring AOP来龙去脉了,希望这几篇文章对大家理解AOP有所帮助

本篇讲的是配置文件形式的AOP,除了用配置文件的形式来实现,用注解也可以实现

下一篇会讲一下注解形式的AOP:SpringAOP的注解形式

了解了Spring AOP后,再去了解Spring的声明式事务就比较简单了,Spring的声明式事务只是对AOP的一种应用

所以,讲完注解形式的AOP后,接着会抽时间继续讲一下Spring的声明式事务,下面是一个声明式事务的配置,可以大概先看一下

       <!-- 事务配置  在需要事务管理的地方加@Transactional 注解或者AOP进行事务处理-->
	   <bean id="transactionManager" 
	     class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
			<property name="dataSource" ref="dataSource" />
	   </bean>
        <!-- 配置事务 -->
		<tx:advice id="txAdvice" transaction-manager="transactionManager">
			<tx:attributes>
				<!-- 注入事务策略 -->
				<tx:method name="query*" read-only="true" />
				<tx:method name="insert*" propagation="REQUIRED"/>
				<tx:method name="update*" propagation="REQUIRED"/>
				<tx:method name="del*" propagation="REQUIRED"/>
			</tx:attributes>
		</tx:advice>
		
		<!-- 配置AOP切点 -->
		<aop:config>
			<!-- 配置AOP切点策略 -->
			<aop:pointcut expression="execution(public * service.impl.*.*(..))" id="service"/>
			<!-- 注入切点,切点策略 -->
			<aop:advisor advice-ref="txAdvice" pointcut-ref="service"/>
		</aop:config>

铁子们,如果觉得文章对你有所帮助,可以点关注,点赞

也可以关注下公众号:扫码 或 wx搜索:“聊5毛钱的java ,欢迎一起学习交流,关注公众号可领取博主的Java学习视频+资料,保证都是干货

Spring中的AOP以及切入点表达式和各种通知

3Q~

纯手敲原创不易,如果觉得对你有帮助,可以打赏支持一下,哈哈,感谢~

Spring中的AOP以及切入点表达式和各种通知           Spring中的AOP以及切入点表达式和各种通知

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/157843.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • mysql表锁与行锁_考核机制表

    mysql表锁与行锁_考核机制表MySQL表锁和行锁机制行锁变表锁,是福还是坑?如果你不清楚MySQL加锁的原理,你会被它整的很惨!不知坑在何方?没事,我来给你们标记几个坑。遇到了可别乱踩。通过本章内容,带你学习MySQL的行锁,表锁,两种锁的优缺点,行锁变表锁的原因,以及开发中需要注意的事项。还在等啥?经验等你来拿!MySQL的存储引擎是从MyISAM到InnoDB,锁从表锁到行锁。后者的出现从某种程度上是弥补前者的…

  • Redis 缓存穿透 + 缓存雪崩 + 缓存击穿的原因和解决方案「建议收藏」

    Redis 缓存穿透 + 缓存雪崩 + 缓存击穿的原因和解决方案「建议收藏」在生产环境中,会因为很多的原因造成访问请求绕过了缓存,都需要访问数据库持久层,虽然对Redsi缓存服务器不会造成影响,但是数据库的负载就会增大,使缓存的作用降低一、缓存穿透缓存穿透是指查询一个根本不存在的数据,缓存层和持久层都不会命中。在日常工作中出于容错的考虑,如果从持久层查不到数据则不写入缓存层,缓存穿透将导致不存在的数据每次请求都要到持久层去查询,失去了缓…

  • Unity 3D 入门基础[通俗易懂]

    Unity 3D 入门基础[通俗易懂]1.1菜单栏File(文件):打开和保存场景、项目、以及创建游戏。Edit(编辑):主要用于Unity内部功能、快捷键设置。Assets(资源):用于资源的创建、导入和导出。GameObject(游戏对象):用于游戏对象的创建。Component:(游戏组件):为游戏对象等添加组件来实现部分功能。Window(窗口):显示特定视图。Help(帮助):主要包含使用手册、资源商店、论坛等。1.2五个视图层级视图(Hierarchy):主要存放游戏场景中的具体的游戏对象。场

  • ldc1614 c语言编程,LDC1614读回来的数据为固定值不变[通俗易懂]

    ldc1614 c语言编程,LDC1614读回来的数据为固定值不变[通俗易懂]OtherPartsDiscussedinThread:LDC1614,LDC1314,LDC1614EVM求教一下各位前辈,硬件是用的LDC1614的评估板,ch0和ch1上接了两个线圈,并联的电容为100pF,器件ID和装配ID读出来为0x3055和0x5449,和手册上的一致,而且我写寄存器再读出来数据都是对的,排除了软件驱动上的问题,现在可能是配置上有哪里不对,或者芯片有问题(…

  • LaTeX参考文献类型

    LaTeX参考文献类型@article:期刊文章@book:有明确出版商的书@booklet:没有指定出版商或赞助商的印刷品@conference:会议文章,与inproceedings相同@inbook:书的一部分,可以是章节等@incollection:Apartofabookhavingitsowntitle@inproceedings:会议文章@manual:技术文档@mastersthesis:硕士论文@misc:大杂烩,当没有其他适合的时候使用这个类型@phdt

  • JDK1.8新特性(二):Collectors收集器类

    JDK1.8新特性(二):Collectors收集器类一.什么是Collectors?Java8API添加了一个新的抽象称为流Stream,我们借助StreamAPI可以很方便的操作流对象。Stream中有两个方法collect和collec

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号