java实战之03spring-03spring的核心之aop(aspectorientedprogramming面向切面编程)

铭昊Coder      2022-06-08     519

关键词:

三、Spring的核心之AOP(Aspect Oriented Programming 面向切面编程)

1AOP概念及原理

1.1、什么是AOP

OOPObject Oriented Programming面向对象编程

AOPAspect Oriented Programming面向切面编程

1.2、代理

充分理解:间接

主要作用:拦截被代理对象执行的方法,同时对方法进行增强。

1.2.1、静态代理

特点:代理类是一个真实存在的类。装饰者模式就是静态代理的一种体现形式。

1.2.2、动态代理

特点:字节码是随用随创建,随用随加载。是在运行期间生成的一个类。

a、基于接口的动态代理

提供者:JDK官方的Proxy类。

要求:被代理类必须实现一个或多个接口。

b、基于子类的动态代理

提供者:第三方的CGLib,如果报asmxxxx异常,需要导入asm.jar

要求:被代理类必须是一个子类(不能用final修饰的(最终类),其余类都没问题,因为都是Object的子类)。

注意:在spring中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。

1.3、通过动态代理技术实现AOP思想

1.3.1、回顾转账案例事务支持的四个版本

a、不使用事务

b、持久层介入了业务层处理(越权操作)

c、业务层控制事务,但是出现了持久层的接口,类和相关代码

d、使用ThreadLocal进行解耦

e、借助动态代理技术,实现面向切面编程思想来控制事务

2Spring中的AOP

2.1、基本概念

连接点(Joinpoint)连接点指类中的核心业务方法

切入点(Pointcut)

连接点不一定是切入点,但切入点一定是连接点。

通知(Advice)

通知就是指那些有公用代码的类。

通知中有好多的方法:通知类型。前置通知|后置通知|异常通知|最终通知|环绕通知

目标对象(Target Object)

AOP代理(AOP Proxy)

织入(Weaving)把通知运行期间插入到核心代码中运行,过程叫做织入

切面(Aspect)公用代码所关心的某一方面(事务是一个方面;日志也是一个方面)

2.2、具体配置

2.2.1Spring中的AOP

a、开发阶段(我们做的)

1) 编写核心业务代码(开发主线):大部分程序员来做,要求熟悉业务需求。

2) 把公用代码抽取出来,制作成通知。(开发阶段最后再做):AOP编程人员来做。

3) 在配置文件中,声明切入点与通知间的关系,即切面。:AOP编程人员来做。

b、运行阶段(Spring框架完成的)

Spring框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

2.2.2、基于XML的配置

2.2.2.1AOP的基本配置
a、导入AOP相关的jar包(SpringAOP是基于IoC的,所以IoCjar也必须存在)

AOP有关的jar包:4

b、引入aop名称空间

c、配置切面
 1 <!-- AOP的配置 -->
 2     <aop:config>
 3         <!-- 配置所有切面通用的切入点表达式 -->
 4         <aop:pointcut expression="execution(* cn.itcast.service.impl.*.*(..))" id="pt1"/>
 5     
 6         <!-- 配置切面:切面就是关心的某一个方面 
 7             id:指定的是通知的唯一标识
 8             ref:指定的通知的bean的id
 9         -->
10         <aop:aspect id="loggerAdvice" ref="logger">
11             <!-- 抽取当前切面的通用切入点表达式 
12                 expression属性:指定的就是切入点表达式
13                 id属性:是把切入点表达式赋予唯一标识
14             
15             <aop:pointcut expression="execution(* cn.itcast.service.impl.*.*(..))" id="pt1"/>
16             -->
17             <!-- 配置通知的类型:指的就是在业务核心方法的什么时候执行 
18                 aop:before:指定的是前置通知
19             
20                 method属性:指的是通知里面的哪个方法执行
21                 pointcut属性:指定的是切入点表达式。表明从哪里切入业务主线。
22                             把哪些连接点变成切入点。
23                 切入点表达式:
24                     关键字:execution(表达式)
25                     表达式语法:
26                         访问修饰符  返回值  包名.类名.方法名(参数列表)
27                     全匹配方式:
28                         execution(public void cn.itcast.service.impl.UserServiceImpl.saveUser())
29                     访问修饰符可以不写:
30                         execution(void cn.itcast.service.impl.UserServiceImpl.saveUser())
31                     返回值可以使用通配符:通配符*
32                         execution(* cn.itcast.service.impl.UserServiceImpl.saveUser())
33                     包名可以使用通配符:当使用通配符时,有几级包,写几个*
34                         execution(* *.*.*.*.UserServiceImpl.saveUser())
35                     包名称可以使用..,表明的是当前包及其子包
36                         execution(* cn..UserServiceImpl.saveUser())
37                     类名可以使用通配符:
38                         execution(* cn..*.saveUser())
39                     方法名可以使用通配符:
40                         execution(* cn..*.*())
41                     参数列表可以指定类型:注意的是,如果是基本类型直接写类型名称,如果是引用类型,需要些包名.类名。lang也必须写。例如:java.lang.String
42                         execution(* *..*.*(int))
43                     参数列表可以使用通配符:注意:不能表示有无参数均可。只能表示参数的数据类型是任意的
44                         execution(* *..*.*(*))
45                     有无参数均可,可以使用..来表示:
46                         execution(* *..*.*(..))
47                     最简版:
48                         execution(* *..*.*(..))
49                     建议的写法:
50                         切到业务层具体实现类即可:
51                             execution(* cn.itcast.service.impl.*.*(..))
52                             
53                     pointcut-ref:指定的是通用切入点表达式的唯一标识
54                 -->
55             <aop:before method="printLog" pointcut-ref="pt1"/>        
56         </aop:aspect>
57     </aop:config>
2.2.2.2、切入点表达式

execution:匹配方法的执行(常用)execution(public * *(..))

基本语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))

within:匹配包或子包中的方法(了解)   within(cn.itcast.aop..*)

this:匹配实现接口的代理对象中的方法(了解) this(cn.itcast.aop.user.UserDAO)

target:匹配实现接口的目标对象中的方法(了解) target(cn.itcast.aop.user.UserDAO)

args:匹配参数格式符合标准的方法(了解) args(int,int)

Spring支持使用如下三个逻辑运算符来组合切入点表达式:

&&要求连接点同时匹配两个切点表达式

||要求连接点匹配至少一个切入点表达式

!要求连接点不匹配指定的切入点表达式

2.2.2.3、切入点的配置方式
方式1

方式2

方式3

1 <aop:pointcut expression="execution(* cn.itcast.service.impl.*.*(..))" id="pt1"/>
2 <aop:before method="printLog" pointcut-ref="pt1"/>
2.2.2.4、通知的类型
 1 <!-- 配置所有切面通用的切入点表达式 -->
 2         <aop:pointcut expression="execution(* cn.itcast.service.impl.*.*(..))" id="pt1"/>
 3         <aop:aspect id="loggerAdvice" ref="logger">
 4             <!-- 指定通知的类型-->
 5             <!-- 前置通知:永远在切入点方法执行之前执行
 6                  它可以获取切入点方法的参数
 7             
 8             <aop:before method="beforePrintLog" pointcut-ref="pt1"/>    -->
 9             
10             <!-- 后置通知:切入点方法正确执行之后执行。它和例外通知只会有一个执行 
11                 它可以获取切入点方法的返回值
12                 除了在通知的方法中提供一个参数之外,还需要在配置文件中提供一个属性
13                 returning属性:指定的是后置通知中方法参数的变量名称,严格区分大小写。
14             
15             <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1" returning="rtValue"/>-->
16             
17             <!-- 例外通知:切入点方法执行时产生异常后执行。它和后置通知只会有一个执行 
18                 除了在通知的方法中提供一个Throwable类型的参数之外,还需要在配置文件中提供一个属性
19                 throwing属性:指定的是例外通知中方法参数的变量名称。严格区分大小写。
20             -->
21             <aop:after-throwing method="afterThrowingprintLog" pointcut-ref="pt1" throwing="th"/>
22             
23             <!-- 最终通知:无论切入点方法是否正确执行了,它都会在最后执行 
24             <aop:after method="printLog" pointcut-ref="pt1"/>-->
25             
26             <!-- 环绕通知:详情请见Logger类中的注释
27             <aop:around method="aroundPrintLog" pointcut-ref="pt1"/>-->
28         </aop:aspect>
 1     /**
 2      * 前置通知:它可以获取切入点方法的参数
 3      * Spring框架给我们提供了一个接口:JoinPoint
 4      * 在程序执行到前置通知方法时,spring框架会为我们注入该接口的实现类。
 5      * 该接口中有一个方法:
 6      *         Object[] args = jp.getArgs();
 7      */
 8     public static void beforePrintLog(JoinPoint jp){
 9         Object[] args = jp.getArgs();
10         System.out.println(Arrays.toString(args));
11         System.out.println("Logger的beforePrintLog方法开始记录日志。。。。。。。");
12     }
13     
14     /**
15      * 后置通知:它可以获取切入点方法的返回值
16      * 需要我们在后置通知的方法中提供一个参数,参数的类型是Object
17      * 参数的变量名称必须和配置文件中的returning属性保持一致
18      */
19     public static void afterReturningPrintLog(Object rtValue){
20         System.out.println(rtValue);
21         System.out.println("Logger的afterReturningPrintLog方法开始记录日志。。。。。。。");
22     }
23     
24     /**
25      * 例外通知:它可以获取切入点方法的异常对象
26      * 需要我们在例外通知的方法中提供一个参数,参数的类型是Throwable
27      * 参数的变量名称必须和配置文件中的throwing属性保持一致 
28      */
29     public static void afterThrowingprintLog(Throwable th){
30         System.out.println("异常信息是:"+th);
31         System.out.println("Logger的afterThrowingprintLog方法开始记录日志。。。。。。。");
32     }
33     
34     /**
35      * 环绕通知详细说明:
36      *         它不是在配置切入点方法的前后执行,而是spring给我们提供的一种方式。让我们自己决定该通知是前置还是后者,例外还是最终。
37      * 问题:
38      *     当我们执行时,只会看见通知方法的语句输出,而看不到业务核心方法的输出语句。
39      * 分析原因:
40      *     在我们使用动态代理控制事务时,有明确的一句话调用业务核心方法:method.invoke(obj,args);
41      *     而此时我们没有明确的调用业务核心方法。
42      *     所以只能看到通知的语句输出,而无法看到业务核心方法的语句输出。
43      * 解决办法:
44      *     Spring给我们提供的一个接口:ProceedingJoinPoint
45      *     当Spring框架在执行通知时,会为我们提供该接口的实现类,我们在使用时,直接用即可。无须关心谁给我们提供。
46      *     ProceedingJoinPoint接口有一个方法:proceed(),它就相当于method的invoke方法。
47      *      pjp.proceed();    ======== method.invoke(obj,args);
48      *  
49      */
50     public static void aroundPrintLog(ProceedingJoinPoint pjp){
51         try {
52 //            System.out.println("Logger的aroundPringLog方法开始记录日志。。。。。。。");
53             pjp.proceed();
54 //            System.out.println("Logger的aroundPringLog方法开始记录日志。。。。。。。");
55         } catch (Throwable e) {
56 //            System.out.println("Logger的aroundPringLog方法开始记录日志。。。。。。。");
57             e.printStackTrace();
58         }finally{
59             System.out.println("Logger的aroundPringLog方法开始记录日志。。。。。。。");
60         }
61         
62     }

注意:相同类型的通知,执行的先后顺序是由配置文件的前后顺序决定的。

2.2.2.5、前置通知获取参数

2.2.2.6、后置通知获取返回值

2.2.2.7、异常通知获取异常对象

2.2.3、基于注解的配置

2.2.3.1使用Spring注解进行AOP配置的前提
a、资源交给Spring管理(核心业务对象,通知对象)

注意:代码中请使用@Compoment注解。

bAOP有关的注解配置

开启Spring@AspectJ注解的支持

c、常用的AOP注解

@Aspect 配置切面

@Before:前置通知

@AfterReturning:后置通知

@AfterThrowing:异常通知

@After:最终通知

@Around:环绕通知

d、配置一个前置通知的例子

2.2.3.2、切入点的配置
a、局部的:只能当前方法使用

b、使用@Pointcut:配置公用的切入点

要求:声明在无返回值方法上方,方法名即切入点名称,方法最好声明为private

c、配置全局的

2.2.3.3、前置通知获取参数

2.2.3.4、后置通知获取返回值

2.2.3.5、异常通知获取异常对象

2.2.3.6、多个相同类型的通知执行顺序

核心业务代码:

spring03-spring开发框架之控制反转

控制反转原理测试接口程序packagecn.liang.service;publicinterfaceIMessageService{publicStringgetInfo();}packagecn.liang.service.impl;importcn.liang.service.IMessageService;publicclassMessageServiceImplimplementsIMe 查看详情

java实战之03spring-01spring概述

一、Spring概述1、Spring是什么?Spring是分层的JavaSE/EE应用full-stack轻量级开源框架,以IoC(InverseOfControl:反转控制)和AOP(AspectOrientedProgramming:面向切面编程)为内核,提供了展现层SpringMVC和持久层SpringJDBC以及业务层事务管理等... 查看详情

java实战之03spring-05spring中的事务控制(基于aop)

五、Spring中的事务控制(基于AOP)1、Spring中事务有关的接口1.1、明确:JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案1.2、Spring事务管理主要包括3个接口1.2.1、PlatformTransactionManager... 查看详情

spring03

title:spring03date:2020-03-0919:06:11tags:自动装配(autowired)本部分是自动装配的内容。1、概述摘抄自秦老师自动装配是使用spring满足bean依赖的一种方法spring会在应用上下文中为某个bean寻找其依赖的bean。Spring中bean有三种装配机制,分别... 查看详情

javaee框架整合技术之spring03-springjdbctemplate模板技术和事务处理(代码片段)

SpringJdbcTemplateSpring的JdbcTemplate是一个对JDBC的模板封装,它提供了一套JDBC的模板,能让我们写持久层代码时减少多余的代码,简化JDBC代码,使代码看起来更简洁。在介绍Spring的JdbcTemplate使用方法之前我们先来讨论... 查看详情

spring03autowire属性

1.创建需要的实体类publicclassStudent{//学生实体类privateStringname;//姓名privateIntegerage;//年龄privateGradegrade;//年级@OverridepublicStringtoString(){return"Student[name="+name+",age="+age+",grade="+grade+"]";}public 查看详情

spring03

学习了spring的数据源的使用以及spring的作用域引入外部属性文件对应的bean的xml文件和properties文件如下<?xmlversion="1.0"encoding="UTF-8"?><!--spring使用外部属性文件--><beansxmlns="http://www.springframework.org/schema/beans"xmlns:x 查看详情

spring03spring的连接数据库以及jdbc模板

前言今天介绍的是关于Spring的数据库连接以及Jdbc模板的相关API方法,虽然在学习了hibernate之后,会知道实现数据库连接一般都是使用hibernate等持久化框架来实现的。但是,很多时候一些涉及到事务的东西使用这些框架并不能够... 查看详情

spring03-aop

一.AOP介绍1.Aop介绍AOP(AspectOrientedProgramming),即面向切面编程,可以说是OOP(ObjectOrientedProgramming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过O... 查看详情

spring03---ioc

一.基础概念1.什么是IOCIoc-----Inversionofcontrol,即“控制反转”,它只是一种设计思想。它意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。(1)谁控制谁     传统的是通过new来创... 查看详情

flowable入门系列文章16-整合spring03(代码片段)

SpringBootSpringBoot是一个应用程序框架,根据其网站,可以很容易地创建独立的,生产级的基于Spring的应用程序,您可以“运行”。它需要对Spring平台和第三方库有自己的看法,所以你可以从最小的烦恼开始。大... 查看详情

spring03_为什么要学习源码基础的接口spring工作流程详解

文章目录①.为什么要学习源码?②.Spring基础接口③.Spring的工作流程详解①.为什么要学习源码?①.工作上:在我们后来的企业级开发期间,调试一些疑难BUG,肯定都要深入到框架底层来进行调试,那如果懂得框架底层源码的... 查看详情

java实战之04javaweb-03会话技术

一、会话技术简介1.什么是会话,为什么需要会话技术?会话:从打开一个浏览器,访问某个网站,到关闭这个浏览器的这个过程称为一次会话。http协议是状态的。2.会话技术的分类客户端存储技术:Cookie服务端存储技术:Ses... 查看详情

spring03-springjdbctemplate模板技术和事务处理(代码片段)

SpringJdbcTemplateSpring的JdbcTemplate是一个对JDBC的模板封装,它提供了一套JDBC的模板,能让我们写持久层代码时减少多余的代码,简化JDBC代码,使代码看起来更简洁。在介绍Spring的JdbcTemplate使用方法之前我们先来讨论... 查看详情

spring03_aop的概述动态代理cglib代理相关概念基于xml配置基于注解配置(代码片段)

文章目录①.Spring的AOP简介②.动态代理③.cglib的动态代理④.AOP的相关概念⑤.基于XML的AOP开发⑥.XML配置AOP详解⑦.基于注解的AOP开发①.Spring的AOP简介①.AOP为AspectOrientedProgramming的缩写,意思为面向切面编程,是通过预编译方式和运行... 查看详情

springboot实战之使用jdbc和spring访问数据库

这里演示的是h2databse示例,所以简单的介绍普及下h2database相关知识H2数据库是一个开源的关系型数据库。H2是一个嵌入式数据库引擎,采用java语言编写,不受平台的限制,同时H2提供了一个十分方便的web控制台用于操作和管理数... 查看详情

springboot实战之使用jdbc和spring访问数据库

这里演示的是h2databse示例,所以简单的介绍普及下h2database相关知识H2数据库是一个开源的关系型数据库。H2是一个嵌入式数据库引擎,采用java语言编写,不受平台的限制,同时H2提供了一个十分方便的web控制台用于操作和管理数... 查看详情

spring_总结_03_装配bean之java配置(代码片段)

一、前言本文承接上一节:Spring_总结_03_装配Bean(一)之自动装配上一节提到,装配Bean有三种方式,首先推荐自动装配。当自动装配行不通时,就需要采用显示配置的方式了。显示配置有两种方案:Java和XML。当需要显示配置时,... 查看详情