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

1、AOP概念及原理

1.1、什么是AOP

OOP:Object Oriented Programming面向对象编程

AOP:Aspect 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、借助动态代理技术,实现面向切面编程思想来控制事务

2、Spring中的AOP

2.1、基本概念

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

切入点(Pointcut):

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

通知(Advice)

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

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

目标对象(Target Object)

AOP代理(AOP Proxy)

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

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

2.2、具体配置

2.2.1、Spring中的AOP

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

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

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

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

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

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

2.2.2、基于XML的配置

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

AOP有关的jar包:4个

b、引入aop名称空间

c、配置切面
 <!-- AOP的配置 -->
<aop:config>
<!-- 配置所有切面通用的切入点表达式 -->
<aop:pointcut expression="execution(* cn.itcast.service.impl.*.*(..))" id="pt1"/> <!-- 配置切面:切面就是关心的某一个方面
id:指定的是通知的唯一标识
ref:指定的通知的bean的id
-->
<aop:aspect id="loggerAdvice" ref="logger">
<!-- 抽取当前切面的通用切入点表达式
expression属性:指定的就是切入点表达式
id属性:是把切入点表达式赋予唯一标识 <aop:pointcut expression="execution(* cn.itcast.service.impl.*.*(..))" id="pt1"/>
-->
<!-- 配置通知的类型:指的就是在业务核心方法的什么时候执行
aop:before:指定的是前置通知 method属性:指的是通知里面的哪个方法执行
pointcut属性:指定的是切入点表达式。表明从哪里切入业务主线。
把哪些连接点变成切入点。
切入点表达式:
关键字:execution(表达式)
表达式语法:
访问修饰符 返回值 包名.类名.方法名(参数列表)
全匹配方式:
execution(public void cn.itcast.service.impl.UserServiceImpl.saveUser())
访问修饰符可以不写:
execution(void cn.itcast.service.impl.UserServiceImpl.saveUser())
返回值可以使用通配符:通配符*
execution(* cn.itcast.service.impl.UserServiceImpl.saveUser())
包名可以使用通配符:当使用通配符时,有几级包,写几个*
execution(* *.*.*.*.UserServiceImpl.saveUser())
包名称可以使用..,表明的是当前包及其子包
execution(* cn..UserServiceImpl.saveUser())
类名可以使用通配符:
execution(* cn..*.saveUser())
方法名可以使用通配符:
execution(* cn..*.*())
参数列表可以指定类型:注意的是,如果是基本类型直接写类型名称,如果是引用类型,需要些包名.类名。lang也必须写。例如:java.lang.String
execution(* *..*.*(int))
参数列表可以使用通配符:注意:不能表示有无参数均可。只能表示参数的数据类型是任意的
execution(* *..*.*(*))
有无参数均可,可以使用..来表示:
execution(* *..*.*(..))
最简版:
execution(* *..*.*(..))
建议的写法:
切到业务层具体实现类即可:
execution(* cn.itcast.service.impl.*.*(..)) pointcut-ref:指定的是通用切入点表达式的唯一标识
-->
<aop:before method="printLog" pointcut-ref="pt1"/>
</aop:aspect>
</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:

 <aop:pointcut expression="execution(* cn.itcast.service.impl.*.*(..))" id="pt1"/>
<aop:before method="printLog" pointcut-ref="pt1"/>
2.2.2.4、通知的类型
 <!-- 配置所有切面通用的切入点表达式 -->
<aop:pointcut expression="execution(* cn.itcast.service.impl.*.*(..))" id="pt1"/>
<aop:aspect id="loggerAdvice" ref="logger">
<!-- 指定通知的类型-->
<!-- 前置通知:永远在切入点方法执行之前执行
它可以获取切入点方法的参数 <aop:before method="beforePrintLog" pointcut-ref="pt1"/> --> <!-- 后置通知:切入点方法正确执行之后执行。它和例外通知只会有一个执行
它可以获取切入点方法的返回值
除了在通知的方法中提供一个参数之外,还需要在配置文件中提供一个属性
returning属性:指定的是后置通知中方法参数的变量名称,严格区分大小写。 <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1" returning="rtValue"/>--> <!-- 例外通知:切入点方法执行时产生异常后执行。它和后置通知只会有一个执行
除了在通知的方法中提供一个Throwable类型的参数之外,还需要在配置文件中提供一个属性
throwing属性:指定的是例外通知中方法参数的变量名称。严格区分大小写。
-->
<aop:after-throwing method="afterThrowingprintLog" pointcut-ref="pt1" throwing="th"/> <!-- 最终通知:无论切入点方法是否正确执行了,它都会在最后执行
<aop:after method="printLog" pointcut-ref="pt1"/>--> <!-- 环绕通知:详情请见Logger类中的注释
<aop:around method="aroundPrintLog" pointcut-ref="pt1"/>-->
</aop:aspect>
     /**
* 前置通知:它可以获取切入点方法的参数
* Spring框架给我们提供了一个接口:JoinPoint
* 在程序执行到前置通知方法时,spring框架会为我们注入该接口的实现类。
* 该接口中有一个方法:
* Object[] args = jp.getArgs();
*/
public static void beforePrintLog(JoinPoint jp){
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
System.out.println("Logger的beforePrintLog方法开始记录日志。。。。。。。");
} /**
* 后置通知:它可以获取切入点方法的返回值
* 需要我们在后置通知的方法中提供一个参数,参数的类型是Object
* 参数的变量名称必须和配置文件中的returning属性保持一致
*/
public static void afterReturningPrintLog(Object rtValue){
System.out.println(rtValue);
System.out.println("Logger的afterReturningPrintLog方法开始记录日志。。。。。。。");
} /**
* 例外通知:它可以获取切入点方法的异常对象
* 需要我们在例外通知的方法中提供一个参数,参数的类型是Throwable
* 参数的变量名称必须和配置文件中的throwing属性保持一致
*/
public static void afterThrowingprintLog(Throwable th){
System.out.println("异常信息是:"+th);
System.out.println("Logger的afterThrowingprintLog方法开始记录日志。。。。。。。");
} /**
* 环绕通知详细说明:
* 它不是在配置切入点方法的前后执行,而是spring给我们提供的一种方式。让我们自己决定该通知是前置还是后者,例外还是最终。
* 问题:
* 当我们执行时,只会看见通知方法的语句输出,而看不到业务核心方法的输出语句。
* 分析原因:
* 在我们使用动态代理控制事务时,有明确的一句话调用业务核心方法:method.invoke(obj,args);
* 而此时我们没有明确的调用业务核心方法。
* 所以只能看到通知的语句输出,而无法看到业务核心方法的语句输出。
* 解决办法:
* Spring给我们提供的一个接口:ProceedingJoinPoint
* 当Spring框架在执行通知时,会为我们提供该接口的实现类,我们在使用时,直接用即可。无须关心谁给我们提供。
* ProceedingJoinPoint接口有一个方法:proceed(),它就相当于method的invoke方法。
* pjp.proceed(); ======== method.invoke(obj,args);
*
*/
public static void aroundPrintLog(ProceedingJoinPoint pjp){
try {
// System.out.println("Logger的aroundPringLog方法开始记录日志。。。。。。。");
pjp.proceed();
// System.out.println("Logger的aroundPringLog方法开始记录日志。。。。。。。");
} catch (Throwable e) {
// System.out.println("Logger的aroundPringLog方法开始记录日志。。。。。。。");
e.printStackTrace();
}finally{
System.out.println("Logger的aroundPringLog方法开始记录日志。。。。。。。");
} }

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

2.2.2.5、前置通知获取参数

2.2.2.6、后置通知获取返回值

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

2.2.3、基于注解的配置

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

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

b、AOP有关的注解配置

开启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、多个相同类型的通知执行顺序

核心业务代码:

Java实战之03Spring-03Spring的核心之AOP(Aspect Oriented Programming 面向切面编程)的更多相关文章

  1. java aop面向切面编程

    最近一直在学java的spring boot,一直没有弄明白aop面向切面编程是什么意思.看到一篇文章写得很清楚,终于弄明白了,原来跟python的装饰器一样的效果.http://www.cnblog ...

  2. Java笔记——面向切面编程(AOP模式)

    原文:http://www.cnblogs.com/yanbincn/archive/2012/06/01/2530377.html Aspect Oriented Programming  面向切面 ...

  3. Java 面向切面编程(Aspect Oriented Programming,AOP)

    本文内容 实例 引入 原始方法 装饰者模式 JDK 动态代理和 cglib 代理 直接使用 AOP 框架--AspectWerkz 最近跳槽了,新公司使用了 AOP 相关的技术,于是查点资料,复习一下 ...

  4. 03-spring框架—— AOP 面向切面编程

    3.1 动态代理 动态代理是指,程序在整个运行过程中根本就不存在目标类的代理类,目标对象的代理对象只是由代理生成工具(不是真实定义的类)在程序运行时由 JVM 根据反射等机制动态生成的.代理对象与目标 ...

  5. Spring核心AOP(面向切面编程)总结

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/75208354冷血之心的博客) 1.AOP概念: 面向切面编程,指扩 ...

  6. Spring的三大核心思想:IOC(控制反转),DI(依赖注入),AOP(面向切面编程)

    Spring核心思想,IoC与DI详解(如果还不明白,放弃java吧) 1.IoC是什么?    IoC(Inversion of Control)控制反转,IoC是一种新的Java编程模式,目前很多 ...

  7. Java中的面向切面编程(AOP)

    一.什么是AOP? Aspect Oriented Programming ,即面向切面编程. AOP是对面向对象编程的一个补充. 它的目的是将复杂的需求分解为不同的切面,将散布在系统中的公共功能集中 ...

  8. Spring核心思想:IOC(控制反转)、DI(依赖注入)和AOP(面向切面编程)

    Spring有三大核心思想,分别是控制反转(IOC,Inversion Of Controller),依赖注入(DI,Dependency Injection)和面向切面编程(AOP,Aspect O ...

  9. Spring框架系列(4) - 深入浅出Spring核心之面向切面编程(AOP)

    在Spring基础 - Spring简单例子引入Spring的核心中向你展示了AOP的基础含义,同时以此发散了一些AOP相关知识点; 本节将在此基础上进一步解读AOP的含义以及AOP的使用方式.@pd ...

随机推荐

  1. iOS动画详解(一)

    Core Graphics Framework是一套基于C的API框架,使用了Quartz作为绘图引擎.它提供了低级别.轻量级.高保真度的2D渲染.该框架可以用于基于路径的绘图.变换.颜色管理.脱屏渲 ...

  2. Python Telnet弱口令爆破脚本及遇到的错误与问题

    写得时候遇到了一个很大的问题,就是我在发送用户名,接受用户名就会一直卡住.然后等了好久后提示 recv ‘\r\nSession timed out.\r\n\r\nTelnet Server has ...

  3. 判断一个Bitmap图像是否是.9图

    见BitmapFactory的源码中 byte[] np = bm.getNinePatchChunk();  final boolean isNinePatch = np != null & ...

  4. cocos2dx中android下动态更新.so文件

    作者:HU 转载请注明,原文链接:http://www.cnblogs.com/xioapingguo/p/4037595.html  因为没用lua脚本写游戏,所以每次发布出去后,发现在bug,需要 ...

  5. ORM之三:DbProvider与DbFactory

    这里涉及到两个关键对象,一个是DbProvider,另一个就是DbFactory.粗略草图如下:   从上图可以看出,开放给消费者的接口就是DbProvider类,不过他主要继承IDbProvider ...

  6. [Effective C++ --026]尽可能延后变量定义式的出现时间

    引言 每一次构造和析构都需要成本,因此我们在设计代码的时候,应该尽可能考虑到构造和析构的成本. 第一节 延后实现 考虑有以下的代码: void encrypt(string& s); stri ...

  7. 读取XML帮助类

    using System; using System.Data; using System.Configuration; using System.Linq; using System.Web; us ...

  8. ajax检查用户名

    Ajax实现的效果 究竟Ajax能实现什么功能呢?今天下午学习了一下Ajax,现在跟大家分享一下我的学习心得.Ajax是什么?工作机制又是什么?可能不大准确,只是我个人看了视频学习后的一点点看法. A ...

  9. Understanding the Router

    Understanding the Router Our module is coming along nicely. However, we're not really doing all that ...

  10. Android View的绘制机制流程深入详解(三)

    本系列文章主要着重深入介绍Android View的绘制机制及流程,第三篇主要介绍并分析视图状态以及重绘流程,首先剖析了 视图的几种状态,然后在深入分析视图的重绘机制流程. 真题园网:http://w ...