Spring -- 4.0
一些Spring AOP的基础
AOP的应用场合是受限的,一般只适用于那些具有横切逻辑的场合:如性能监测、访问控制、事物管理和日志记录。
我很认可书中的这么一个观点(横向和纵向的观点),这个观点也很好的帮助我们理解什么是AOP的思想:按照软件重构的思想,如果多个类中出现相同的代码,我们应该考虑定义一个它们共同使用的父类,将这些公用的代码提取到父类中,通过继承减少重复的代码。但是像事务管理、日志记录我们无法通过抽象父类的方式消除重复性的横切代码,这些横切逻辑的代码存在业务方法中,我们不能把它抽象到父类中。AOP通过横向抽取机制为这类无法通过纵向继承体系抽取重复性代码的场合提供了解决方案。
AOP把分散在各个业务逻辑代码中的共同代码通过横向切割的方式抽取到一个个独立的模块中,还业务逻辑一个清新的世界。
AOP就是要把这些独立的横切逻辑编织到业务逻辑代码中。
AOP的术语
- 连接点(Joinpoint)
程序执行的某个特定的位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。需要指出的是Spring仅支持方法的连接点。 - 切点(Pointcut)
AOP通过“切点”定位到具体的连接点上。可以把切点理解为查找连接点的规则条件。一个切点可以匹配多个连接点,定位连接点。
尼玛,这个切点就是连接点只不过是符合一定规则的连接点。
在Spring中,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件,Spring的规则解析引擎负责解析切点所设定的查询条件,找到应的连接点。后面这句话让我们更清楚:确切的说查找到的是执行点(指方法吧),而连接点是方法执行前、后等包括方位信息的执行点,切点只是定位到某个方法上,所以希望定位到具体的连接点上还需要方位信息。 - 增强(Advice)
增强是织入到目标类连接点上的一段代码程序。为什么叫增强呢,尼玛应该就是对原来的代码增加一定的功能,所以翻译过来就叫做增强,英语叫Advice或者Enhance。
但是增强又不仅仅指目标连接点上的一段执行逻辑,还包括定位连接点方位信息(就是连接点前、后)。故Spring提供的增强接口都是带有方位名的:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。 - 目标对象(Target)
增强逻辑织如的目标类。
AOP可以把横切逻辑动态的织入到特定的连接点上。 - 引介(Introduction)
引介是一种特殊的增强,它为类添加一些属性和方法。我们可以通过引介功能,动态的为业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。 - 织入(Weaving)
将增强添加到目标类具体连接点上的过程。
AOP就像一台织布机,将目标类、增强或者引介天衣无缝的编织到一起。根据不同的实现技术,AOP有三种织入方式:编译器织入,类载入器织入,动态代理织入。
Spring AOP采用动态代理织入,而AspectJ采用编译器和类载入期织入。 - 代理(Proxy)
一个类被AOP织入增强之后,就产生出来一个结果。它融合了原类和增强逻辑的代理类(所谓代理就是她替原类去实现功能)。根据不同的代理方式,代理类既可能可能是和原类具有相同接口的类,也可能是原类的子类,我们可以和原类一样的方法调用代理类。 - 切面(Aspect)
切面由切点和增强(引介)组成,它既包括横切逻辑的定义,也包括连接点的定义。Spring AOP就是负责实施切面的框架,它将切面定义的横切逻辑编织入到切面指定的连接点中(由切点定义)
AOP的工作重心就是如何把增强逻辑应用到目标对象的连接点上,这里包括两个工作:第一,如何通过切点定位到连接点;第二,如何在增强中编写切面代码。
学习实际上就是接触和理解新的概念和事物的过程,如果你对一个新领域的概念很清楚了,那也说明你已经学习的差不多了。
AOP的这几个概念,以前一直模模糊糊的,现在感觉到清楚了很多。
AOP的实现有
- AspectJ
- JBoss AOP
- Spring AOP
Spring
AOP使用纯Java实现,不需要专门的编译器和载入器,它在运行期通过代理方式向目标类织入代码增强。
它有两种代理机制:一种是基于JDK的动态代理;另一种是一语CGLib的动态代理。之所以需要两种代理机制,很大程度上因为JDK本身只提供接口的代理(代理类和原类实现同一接口)而不支持类的代理(代理类为原类的子类)。
JDK动态代理
JDK代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。
InvocationHandler是一个接口,可以通过实现改接口定义横切逻辑,并通过反射机制调用目标类的代码,动态的将横切逻辑和业务逻辑编织在一起。
Proxy是利用InvocationHandler动态的创建一个符合某一接口的实例,生成的目标类的代理对象,书上写了一个例子,我简化了一下(好懒有木有,本来就很简单了):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
public class PerformanceHandler implements
InvocationHandler { private
Object target; //目标对象,业务类。 public
PerformanceHandler(Object target){ this .target = target; } @Override public
Object invoke(Object proxy, Method method, Object[] args) throws
Throwable { System.out.println( "======可以把这里认为是织入的代码(横切逻辑),加在方法执行前=======" ); //通过反射方法调用业务类的目标方法。 Object obj = method.invoke(target, args); System.out.println( "======可以把这里认为是织入的代码(横切逻辑),加在方法执行后=======" ); return
obj; } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14 |
public class ProxyTest1 { public
static void main(String[] args) { IForumService target = new
ForumServiceImpl(); //希望被代理的目标类。 PerformanceHandler handler = new
PerformanceHandler(target); //根据编织了目标业务类逻辑和横切逻辑的InvocationHandler实例创建代理实例。 IForumService proxy = (IForumService) Proxy.newProxyInstance(target .getClass().getClassLoader(), target.getClass().getInterfaces(), handler); proxy.removeForum( 100 ); System.out.println(); proxy.removeTopic( 999 ); } }<br> //执行结果 |
======可以把这里认为是织入的代码(横切逻辑),加在方法执行前=======
========模拟删除Forum记录:100============
======可以把这里认为是织入的代码(横切逻辑),加在方法执行后=======
======可以把这里认为是织入的代码(横切逻辑),加在方法执行前=======
========模拟删除Topic记录:999============
======可以把这里认为是织入的代码(横切逻辑),加在方法执行后=======
代码可以运行。这里代理类对ForumServiceImpl中的所有方法进行代理(编织增强逻辑)。
后续。