1.AOP
简介
AOP
(
Aspect Orient Programming
),面向切面编程。面向切面编程是从动态角度考虑程
序运行过程。
AOP
底层,就是采用动态代理模式实现的。采用了两种代理:
JDK
的动态代理
,与
CGLIB
的动态代理
。
AOP
为
Aspect Oriented Programming 的缩写,意为:面向切面编程,可通过运行期动态
代理实现程序功能的统一维护的一种技术。
AOP
是
Spring
框架中的一个重要内容。利用
AOP
可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程
序的可重用性,同时提高了开发的效率。
面向切面编程,就是将交叉业务逻辑封装成切面,利用
AOP
容器的功能将切面织入到
主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、
事务、日志、缓存等。
若不使用
AOP
,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样,
会使主业务逻辑变的混杂不清。
例如,转账,在真正转账业务逻辑前后,需要权限控制、日志记录、加载事务、结束事
务等交叉业务逻辑,而这些业务逻辑与主业务逻辑间并无直接关系。但,它们的代码量所占
比重能达到总代码量的一半甚至还多。它们的存在,不仅产生了大量的“冗余”代码,还大
大干扰了主业务逻辑
---
转账。
2.面向切面编程有什么好处?
1.
减少重复;
2.
专注业务;
注意:面向切面编程只是面向对象编程的一种补充。
使用
AOP
减少重复代码,专注业务实现:
3.AOP 编程术语
(1) 切面(Aspect)
切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面
是通知(
Advice
)。实际就是对主业务逻辑的一种增强。
(
2
) 连接点(
JoinPoint
)
连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
(
3
) 切入点(
Pointcut
)
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。
被标记为
final
的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不
能被增强的。
(4) 目标对象(Target)
目 标 对 象 指 将 要 被 增 强 的 对 象 。 即 包 含 主 业 务 逻 辑 的 类 的 对 象 。 上 例 中 的
StudentServiceImpl
的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然,
不被增强,也就无所谓目标不目标了。
(
5
) 通知(
Advice
)
通知表示切面的执行时间,
Advice
也叫增强。上例中的
MyInvocationHandler
就可以理
解为是一种通知。换个角度来说,
通知定义了增强代码切入到目标代码的时间点
,是目标方
法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。
切入点定义切入的位置,通知定义切入的时间
。
4.AspectJ
对
AOP
的实现
对于
AOP
这种编程思想,很多框架都进行了实现。
Spring
就是其中之一,可以完成面向
切面编程。然而,
AspectJ
也实现了
AOP
的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,
Spring
又将
AspectJ
的对于
AOP
的实现也引入到了自己的框
架中。
在
Spring
中使用
AOP
开发时,一般使用
AspectJ
的实现方式。
AspectJ 简介 :
AspectJ
是一个优秀面向切面的框架,它扩展了
Java
语言,提供了强大的切面实现。
官网地址:
http://www.eclipse.org/aspectj/
a seamless aspect-oriented extension to the Javatm programming language
(一种基于
Java
平台的面向切面编程的语言)
Java platform compatible
(兼容
Java
平台,可以无缝扩展)
easy to learn and use
(易学易用)
4.1 AspectJ
的通知类型
AspectJ
中常用的通知有五种类型:
(
1
)前置通知
(
2
)后置通知
(
3
)环绕通知
(
4
)异常通知
(
5
)最终通知
4.2AspectJ
的切入点表达式
AspectJ
定义了专门的表达式用于指定切入点。表达式的原型是:
execution(modifiers-pattern? ret-type-pattern
declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
解释:
modifiers-pattern
访问权限类型
ret-type-pattern
返回值类型
declaring-type-pattern
包名类名
name-pattern(param-pattern)
方法名
(
参数类型和参数个数
)
throws-pattern
抛出异常类型
?表示可选的部分
以上表达式共
4
个部分。
execution(
访问权限
方法返回值 方法声明
(
参数
)
异常类型
)
切入点表达式要匹配的对象就是目标方法的方法名。所以,
execution
表达式中明显就是方法的签名。注意,表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可以使用以下符号:
举例:
execution(public * *(..))
指定切入点为:任意公共方法。
execution(* set*(..))
指定切入点为:任何一个以“
set”
开始的方法。
execution(* com.xyz.service.*.*(..))
指定切入点为:定义在
service
包里的任意类的任意方法。
execution(* com.xyz.service..*.*(..))
指定切入点为:定义在
service
包或者子包里的任意类的任意方法。“
..”
出现在类名中时,后面必须跟“*
”,表示包、子包下的所有类。
execution(* *..service.*.*(..))
指定所有包下的
serivce
子包下所有类(接口)中所有方法为切入点
execution(* *.service.*.*(..))
指定只有一级包下的
serivce
子包下所有类(接口)中所有方法为切入点
execution(* *.ISomeService.*(..))
指定只有一级包下的
ISomeSerivce
接口中所有方法为切入点
execution(* *..ISomeService.*(..))
指定所有包下的
ISomeSerivce
接口中所有方法为切入点
execution(* com.xyz.service.IAccountService.*(..))
指定切入点为:
IAccountService
接口中的任意方法。
execution(* com.xyz.service.IAccountService+.*(..))
指定切入点为:
IAccountService
若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类,则为该类及其子类中的任意方法。
execution(* joke(String,int)))
指定切入点为:所有的
joke(String,int)
方法,且
joke()
方法的第一个参数是
String
,第二个参数是 int
。如果方法中的参数类型是
java.lang
包下的类,可以直接使用类名,否则必须使用全限定类名,如 joke( java.util.List, int)
。
execution(* joke(String,*)))
指定切入点为:所有的
joke()
方法,该方法第一个参数为
String
,第二个参数可以是任意类型,如joke(String s1,String s2)
和
joke(String s1,double d2)
都是,但
joke(String s1,double d2,Strings3)
不是。
execution(* joke(String,..)))
指定切入点为:所有的
joke()
方法,该方法第一个参数为
String
,后面可以有任意个参数且参数类型不限,如 joke(String s1)
、
joke(String s1,String s2)
和
joke(String s1,double d2,String s3)都是。
execution(* joke(Object))
指定切入点为:所有的
joke()
方法,方法拥有一个参数,且参数是
Object
类型。
joke(Object ob)是,但,joke(String s)
与
joke(User u)
均不是。
execution(* joke(Object+)))
指定切入点为:所有的
joke()
方法,方法拥有一个参数,且参数是
Object
类型或该类的子类。不仅 joke(Object ob)
是,
joke(String s)
和
joke(User u)
也是。