`
gao_xianglong
  • 浏览: 461773 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

详解SSJ(Spring3.x mvc + Spring3.x Core + JPA2.x)轻量级集成开发—第5章 剖析Spring3.x AOP特性01

阅读更多

上一章 详解SSJ(Spring3.x mvc + Spring3.x Core + JPA2.x)轻量级集成开发—第4章 剖析Annotations特性

下一章 详解SSJ(Spring3.x mvc + Spring3.x Core + JPA2.x)轻量级集成开发—第6章 剖析Spring3.x AOP特性02

 

目录

一、AOP简介;

二、为什么需要使用AOP;

三、设计模式之代理模式;

四、JDK动态代理实现;

五、Spring AOP简介;

六、Spring AOP术语;

七、基于Schema配置文件形式的Spring AOP;

 

前言

经过前面几个章节的学习,笔者已经详细的为大家讲解了关于Spring的内核技术(IOC)。那么从本章开始,笔者将会为大家讲解关于Spring AOP的一些技术。由于AOP的内容涉及范围较广,所以笔者仍然打算分成多章进行逐步讲解,希望大家能够耐心进行阅读。

笔者收到较多私信,很多朋友都是询问关于后期章节的内容安排,或者直接帖一大堆调试代码让笔者解释原因。对此笔者只能表示无奈,由于时间有限(高质量的博文需要笔者花费大量的时间去构思),加之年前项目周期较紧,所以笔者将不能及时回复,希望大家给予谅解。在此衷心的祝愿大家,新年快乐,奖金丰收。

 

一、AOP简介与实现

AOP(Aspect Oriented Programming,面向切面编程)简单来说就是在不改变源代码的前提下,通过前后横切的方式,动态添加新功能,这就是AOP。笔者经常听到很多朋友喜欢拿AOP与OOP(Object Oriented Programming,面向对象编程)进行比较,而且还分析得津津有味(曾出现在某些高校的学术论文上)。在此笔者希望大家能够明白AOP仅仅只是作为OOP的一种特性补充,如果脱离OOP,AOP将一无是处

OOP延伸自POP(Procedure-Oriented Programming,面向过程编程),这才是一种创新的软件设计思想。换句话来说AOP其实是基于OOP的,算是一种改进,一种补充,但绝对算不上是一次实质性的飞跃。OOP是针对领域模型中的组件或者组件单元进行封装或抽象,其结构是按照顺序结构从上至下逐一执行。并且组件之间存在着相互关联,相互依赖,以高耦合的方式展现领域对象的职责,但这同时也暴露了OOP思想的缺陷(OOP本身就是基于依赖式设计)。而OOA的特性是模块横切点,与OOP不同的是,OOA更加关注内聚性。也就是说OOA是针对业务逻辑或控制逻辑中存在的通用模块进行抽取,所抽取出来的单元模块我们称之为横切点。并且在许多情况下,这些横切点并不属于业务逻辑或业务控制成员,AOP要做的事情就是对其进行逐步分离,然后让这些横切点以可插拔式的设计与其进行耦合。从这一点来看AOP和OOP的思想是截然不同的,所以从理论上来说你完全可以将OOA当做是对OOP的一种解耦优化

 

二、为什么需要使用AOP

随着Object Oriented思想的大行其道。上至架构人员,下至是开发人员,心里边每天都在不停的思考着,如何让我的设计,我的代码更好的解耦,或许这便是软件工程永恒的话题。但是笔者同时也要告诉你,世界上没有最好的解耦方式。只要我们能够尽可能做到面向接口编程,遵循类型单一原则设计,善用设计模式,这样我们的设计才更具复用性、维护性、扩展性及伸缩性

笔者上述章节已经提到过,AOP可以当做是OOP的一种解耦优化,那么接下来咱们就来看看AOP到底是以何种形式对OOP进行解耦的。首先来看基于OOP的设计,假设笔者有2个控制层(实际情况可能会更多),都需要请求同一业务操作响应处理。但是在请求之前,控制逻辑有必要对其进行一系列的处理操作,这些处理操作可能包含:权限检查、粗粒度日志记录等。如果按照OOP的设计思想咱们应该怎么做呢?很可能你的第一反应会首先编写一个通用的控制逻辑接口,然后指定其派生类实现重写所需的一系列处理操作。最后控制层在调用具体的业务操作之前(或者业务操作自身在执行之前),务必先要调用封装好的通用控制逻辑。如果你也是这么设计的,那么恭喜你,OOA(Object-Oriented Analysis,面向对象分析)和OOD(Object-Oriented Design,面向对象设计)你确实掌握的不错。

基于OOP的类图设计:

 

当你为上述设计沾沾自喜的时候,请花一分钟时间思考一下。控制层组件与通用控制逻辑完全是处于高耦合状态,先不论控制逻辑接口是否还具备有效性,只要控制逻辑稍稍发生些许变化,便会直接影响到所有控制层,这便基于依赖式设计的弊端。有办法可以解决吗?千万别告诉笔者Object Oriented只能这样,如果你真的是这么想的,那么你就可以考虑结合AOP的方式重构上述设计。

如果使用AOP的设计方式咱们应该怎么做呢?笔者前面提到过,AOP的特性是模块横切点,在这里这个横切点其实就是通用的控制逻辑,现在要做的事情就是需要将其抽取出来,使之与我们的控制层组件分离,这样既提升了内聚性,同时也保证了设计的低耦合。

基于OOA思想重构上述设计图:

 

 

通过基于上述AOP的设计重构,咱们完全实现了相关处理与控制层组件之间的分离。在这里笔者要给大家提示一下,如果想在程序中完全解除组件耦合是做不到的,且是不实际的。因为Object Oriented本身就是基于依赖式设计,如果希望设计出来的东西没有任何耦合性,恐怕你只能退回到POP才能满足你的需求。

对于目前而言,基于AOP的技术实现可以应用在诸多领域,比如:粗粒度日志记录、性能统计、安全控制、事务处理或者异常处理上。目前优秀的AOP产品其实也挺多的,除了Spring以外,Struts的Interceptor(拦截器)其实也是基于AOP思想的实现。 

 

三、设计模式之代理模式

其实AOP的概念并不是近几年才诞生的新东西,早在GOF提出23种设计模式的时候,我们已经可以看见AOP的雏形身影。不知大家是否记得熟悉的代理模式?你完全可以把它当做是AOP的特定实现,当然如果你本身并不了解代理模式,请仔细的阅读笔者接下来的讲义。

回顾第1章,笔者为大家讲解了23种设计模式之中的工厂模式系列(包括:简单工厂模式、工厂方法模式和抽象工厂模式),本章节笔者则会为大家讲解关于代理模式的使用方式。

代理模式(Proxy Pattern)我们又称之为静态代理,该模式的特征定义是为委托对象提供一种代理访问机制,以控制其它对象对其进行访问。使用代理模式最大的好处在于,实际的角色只需关注具体的业务既可,其余操作则全权委托给代理对象负责,并且代理操作既可以在业务执行之前执行,同样也可以在业务执行完成之后再执行。通过这样的设计,我们的代码将更具扩展性,代码结构也更加清晰。

代理模式类图示例:

 

 

通过上述代理模式示例图,我们可以发现代理对象与委托对象都需要实现同一目标接口,客户端访问的时候,首先会访问代理类,最后由代理类去访问委托对象

在这里笔者设计的代理对象,仅仅只是负责相关业务执行前后的粗粒度的日志记录。

代理类型代码如下:

public class LoginServiceProxy implements LoginService {
	private LoginServiceImpl loginServiceImpl;
	private boolean login;
	public boolean login() throws Exception {
		// TODO Auto-generated method stub
		System.out.println("日志记录1...");
		loginServiceImpl = new LoginServiceImpl();
		login = loginServiceImpl.login();
		System.out.println("日志记录2...");
		return login;
	}
}

 

代理模式并不复杂,相对而言笔者觉得它应该是23种设计模式之中最好理解的设计模式之一。如果你对该模式还是无法理解,笔者建议你参考其他相关书籍。

 

四、JDK动态代理实现

JDK动态代理与静态代理(代理模式)不同的是, 动态代理会在程序运行时,通过反射机制动态生成代理对象。并且使用动态代理后,咱们再也不必手动编写代理类。这样不仅能够简化开发,相对于静态代理而言,程序将具备更好的扩展性。

在java.lang.reflect包下,Proxy类型和InvocationHandler接口将用于生成动态代理类。其中Proxy用于创建代理类型或者代理实例,来看看Proxy的常用方法。

用于生成代理类型的静态getProxyClass方法:

public static Class<?> getProxyClass(ClassLoader loader,
			Class<?>... interfaces) throws IllegalArgumentException;

 

使用Proxy的静态getProxyClass()方法,我们可以得到一个代理类型。其中“loader”属性用于指定委托类型的类装载器,“interfaces”属性则用于指定委托类型需要实现的所有接口。

 用于生成代理实例的静态newProxyInstance()方法:

public static Object newProxyInstance(ClassLoader loader,
			Class<?>[] interfaces, InvocationHandler h)
			throws IllegalArgumentException;

  

使用Proxy的静态newProxyInstance()方法,我们可以得到一个代理实例。其中“loader”属性用于指定委托类型的类装载器,“interfaces”属性用于指定委托类型需要实现的所有接口,而属性“h”则用于指定InvocationHandler实现(如果想使用JDK动态代理,那么代理类务必需实现InvocationHandler接口)。

使用JDK动态代理生成代理对象:

public class LoginServiceProxy<T> implements InvocationHandler {
	/* 需要被代理的委托对象 */
	private T obj;

	public LoginServiceProxy(T obj) {
		this.obj = obj;
	}

	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		// TODO Auto-generated method stub
		return method.invoke(obj, args);
	}

	public boolean loginProxy() {
		boolean login = false;
		try {
			/* 反射生成代理对象 */
			LoginService proxy = (LoginService) Proxy.newProxyInstance(obj
					.getClass().getClassLoader(), obj.getClass()
					.getInterfaces(), this);
			System.out.println("日志记录1....");
			login = proxy.login();
			System.out.println("日志记录2....");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return login;
	}
}

 

提示:

使用JDK动态代理后,代理类型的名称则会以“$Proxy”开后。我们通过使用Debug来看一下代理类型的名称:

 

五、Spring AOP简介

关于Spring AOP中牵扯的概念较多,但笔者并不打算像其他技术文章一样故弄玄虚的瞎扯,尽可能的为大家带来最直接的表述方式。上述章节笔者详细的为大家阐述了AOP的一些相关概念和特定实现方式,从本章开始笔者将会为大家讲解如何在Spring中使用AOP。当然在正式开始讲解之前,你完全没有必要把Spring AOP想的过于复杂化,因为在Spring中使用AOP是一件极其轻松的事情,我们除了需要把对象管理(依赖建立、依赖维护、依赖销毁)交由Spring的IOC容器去负责外,代理方式则全部委派给Spring的AOP去完成即可。

从大致上来说使用Spring AOP其实有3种方式,第一种是采用实现MethodBeforeAdvice、AfterReturningAdvice、MethodInterceptor等接口的方式(在Spring1.x版本中使用最多)。第二种是基于Schema风格的配置文件方式。最后一种便是基于Annotation的实现方式,至于具体使用哪一种就看你自己喜好或项目需要。本章笔者仅围绕配置文件和Annotation的方式进行讲解,如果大家想了解基于指定接口的方式实现Spring AOP,笔者推荐你参考Spring官方帮助文档。

Spring AOP底层实现方式采用了2种代理机制,分别为:JDK动态代理和CGLib动态代理。至于为什么需要使用这2种代理机制,很大程度上是因为JDK自身只支持基于接口的代理,而不支持类型的代理。对于开发人员而言,要做的事情仅仅只是使用Spring封装好的AOP实现即可,不必太过于关注底层实现细节。 

 

六、Spring AOP术语

在学习Spring AOP的时候,笔者不得不为大家提及一些AOP相关的术语概念,因为理解这些概念或许能够使你更快的掌握Spring AOP。笔者在很多年前学习Spring AOP术语时,曾经被国内一些“高手”的所谓翻译弄得晕头转向。在此笔者不得不“佩服”国内某些IT人员,把通俗易懂的英文翻译得连鬼都看不懂,笔者实在不解,所以笔者将会以自己的理解对以下AOP术语进行概念性总结:

1、切面(Aspect ):用于执行代理业务的具体内容;

2、连接点(Join point ):用于执行切面的具体代码位置;

3、通知(Advice ):用于执行代理业务的具体内容实现;

4、切入点(Pointcut):定义了通知应该拦截的方法;

5、目标对象(Target object):委托对象;

6、AOP代理(Aop proxy):代理对象;

7、前置通知(Before advice):代理业务执行于被拦截的方法之前;

8、后置通知(After advice):代理业务执行于被拦截的方法之后;

9、环绕通知(Around advice):代理业务执行于被拦截的方法之前或之后

10、异常通知(After throwing advice):代理业务执行于被拦截的方法抛出异常后;

11、返回时通知(After returning advice):代理业务执行于被拦截的方法返回之前;

 

  七、基于Schema配置文件形式的Spring AOP

笔者在前面章节中曾经提到过,如果想使用Spring AOP其实是有3种方式的。但笔者仅仅只会围绕Annotation和基于Schema风格的配置文件方式进行讲解。咱们先来看看如何使用配置文件的方式实现Spring AOP,或许这并不复杂,你完全可以轻松掌握并使用。

由于笔者的项目是基于Maven3.x进行管理的,所以首先需要添加Spring AOP所需的依赖构件:

<!-- AspectJ构件依赖 -->
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjrt</artifactId>
   <version>1.6.11</version>
  </dependency>
  <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.6.11</version>
  </dependency>
  <dependency>
   <groupId>cglib</groupId>
   <artifactId>cglib</artifactId>
   <version>2.1</version>
  </dependency>
  <!-- spring构件依赖 -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-aop</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>
 </dependencies>

 

当然如果你只是使用传统工程构建Spring应用的话,你仅需要下载Spring AOP的相关构件引入至项目中即可,这些构件包括:

1、aspectjrt-version.jar;

2、spring-aop-version.jar;

3、aopalliance-version.jar;

4、aspectjweaver.jar;

5、cglib-version.jar;

6、asm-all-version.jar;

 

当我们成功添加Spring AOP的所需构件后,还需导入AOP命名空间:

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd

 

成功导入AOP命名空间后,接下来我们还需要在配置文件中进行切面配置。配置切面我们使用的是<aop:aspect/>标签,该标签的作用就是将一个普通的POJO转换成切面类进行相应的代理服务

使用<aop:aspect/>标签配置切面:

<aop:config>
	<aop:aspect id="logAspect" ref="logBean"/>
</aop:config>
<bean name="logBean" class="org.johngao.bean.LogBean" />

 

在上述配置文件中,<aop:config/>标签作为Spring AOP的切面根标签。该标签中包含有<aop:advisor/>、<aop:aspect/>、<aop:pointcut/>等3的子标签元素,其中<aop:config/>标签用于定义切面服务,<aop:advisor/>标签类似于自包含切面,<aop:pointcut/>标签则用于定义切入点。大家在使用这3个标签的时候需要注意,必须按照pointcut-->advisor-->aspect的顺序进行声明。配置文件中允许定义多个<aop:config/>标签,且该标签内部仍然允许定义多个子标签元素。

我们首先来看<aop:aspect/>标签的使用。笔者前面也曾提及过,该标签用于定义切面,其中属性“ref”用于引用POJO作为切面类。此外<aop:aspect/>标签中还包含有2个可选属性,分别为“id”和“order”。其中属性“id”定义了切面的标识名。而属性“order”定义了切面类的优先级属性值越低,切面的执行优先级就越高

使用属性“order”定义切面执行优先级:

<aop:config>
	<aop:aspect id="logAspect2" ref="logBean2" order="2"/>
	<aop:aspect id="logAspect1" ref="logBean1" order="1"/>
</aop:config>
<bean name="logBean1" class="org.johngao.bean.LogBean1" />
<bean name="logBean2" class="org.johngao.bean.LogBean2" />

 

上述配置文件中,笔者定义了2个切面。其中切面1的执行优先级为“1”,所以切面1必然是优先于切面2执行。

 

提示

使用属性“order”定义切面类的执行优先级,最常用的使用场景是多个切面指定拦截同一切入点

本章内容到此结束,由于时间仓库,本文或许有很多不尽人意的地方,希望各位能够理解和体谅。关于下一章的内容,笔者打算继续讲解Spring AOP相关的内容。

9
3
分享到:
评论
28 楼 linuxp363 2013-02-22  
gao_xianglong 写道
linuxp363 写道
快过年了,祝楼主年终大奖收获。楼主不要管那些杂毛的闲言碎语,以免添堵。做自己的事,收获的总是自己,也为别人提供帮助,特别是新手可以少走弯路,这也是一件功德。望楼主坚持讲完,而且要讲的更好更深入。期待年后新作。

谢谢支持,新作已经发布。


已拜读
27 楼 gao_xianglong 2013-02-21  
jay263566831 写道
博主写的不错,都看完了
!还有,少与喷子争论,以免拉低自己

谢谢支持,喷子爱喷喷,不搭理他们。
26 楼 gao_xianglong 2013-02-21  
linuxp363 写道
快过年了,祝楼主年终大奖收获。楼主不要管那些杂毛的闲言碎语,以免添堵。做自己的事,收获的总是自己,也为别人提供帮助,特别是新手可以少走弯路,这也是一件功德。望楼主坚持讲完,而且要讲的更好更深入。期待年后新作。

谢谢支持,新作已经发布。
25 楼 jay263566831 2013-02-21  
博主写的不错,都看完了
!还有,少与喷子争论,以免拉低自己
24 楼 linuxp363 2013-02-07  
快过年了,祝楼主年终大奖收获。楼主不要管那些杂毛的闲言碎语,以免添堵。做自己的事,收获的总是自己,也为别人提供帮助,特别是新手可以少走弯路,这也是一件功德。望楼主坚持讲完,而且要讲的更好更深入。期待年后新作。
23 楼 txf_7337 2013-02-06  
博主对spring了解的很深入,科普下别人也好,期待下一章
22 楼 gao_xianglong 2013-02-05  
minstrel 写道
楼主要善待胡搅蛮缠的人,不要跟他们理论,这些人也是你火起来的一根柴呀

相互尊重,相互理解才是美得。
21 楼 minstrel 2013-02-05  
楼主要善待胡搅蛮缠的人,不要跟他们理论,这些人也是你火起来的一根柴呀
20 楼 gao_xianglong 2013-02-05  
程序猿_星 写道
好文章,很受启发,对于这些经典框架研究一下,一通则百通。

19 楼 程序猿_星 2013-02-05  
好文章,很受启发,对于这些经典框架研究一下,一通则百通。
18 楼 duoduodeai 2013-02-05  
gao_xianglong 写道
txf_7337 写道
还有spring3.x core里有aop的吗?
jpa不就是jpatemplate替代hibernatetemplate(jdbctemplate)么
讲那么多屁话!

你嘴巴干净点,别跟吃了火药似的。这里没人得罪你,也没人欠你什么,何必一张嘴就不友好。如果你觉得jpatemplate替代hibernatetemplate就是你所谓的JPA那么笔者无话可说。

哎,楼主跟这种鸟人何必费劲解释,畜生的思维是永远无法理解正常人人的。技术皮毛对于他来说就是他最渴望得到的和追求的
17 楼 gao_xianglong 2013-02-05  
txf_7337 写道
还有spring3.x core里有aop的吗?
jpa不就是jpatemplate替代hibernatetemplate(jdbctemplate)么
讲那么多屁话!

你嘴巴干净点,别跟吃了火药似的。这里没人得罪你,也没人欠你什么,何必一张嘴就不友好。如果你觉得jpatemplate替代hibernatetemplate就是你所谓的JPA那么笔者无话可说。
16 楼 duoduodeai 2013-02-05  
txf_7337 写道
还有spring3.x core里有aop的吗?
jpa不就是jpatemplate替代hibernatetemplate(jdbctemplate)么
讲那么多屁话!

你既然这么聪明,那你还进来看什么呢???那岂不是玷污你的狗眼吗????你是猪胎嘛??。。aop楼主不算核心就不可以抽取出来讲???你学过spring嘛??。既然你对所有框架这么熟悉,你倒是去写一个啊,比如你就写CNMtemplate了????不招人待见的昏。
15 楼 duoduodeai 2013-02-05  
txf_7337 写道
这不是什么新技术,何况LZ是站在巨人的肩膀上,LZ说的不就是SPRINGMVC+JPA么,套那么大的帽子。当然LZ想出本书那就继续写吧。

你这个叼毛很无耻啊,大清早就到处满嘴喷粪。巨人的肩膀,你倒是也去站一站啊??没这个本事就别放出豪言壮语。。。。来这里找骂啊。。。。。
14 楼 txf_7337 2013-02-05  
还有spring3.x core里有aop的吗?
jpa不就是jpatemplate替代hibernatetemplate(jdbctemplate)么
讲那么多屁话!
13 楼 txf_7337 2013-02-05  
这不是什么新技术,何况LZ是站在巨人的肩膀上,LZ说的不就是SPRINGMVC+JPA么,套那么大的帽子。当然LZ想出本书那就继续写吧。
12 楼 gao_xianglong 2013-02-04  
minstrel 写道
楼主要火

 
11 楼 minstrel 2013-02-04  
楼主要火
10 楼 gao_xianglong 2013-02-04  
duoduodeai 写道
看完了,很精辟~~~楼主很强~~~~~

9 楼 gao_xianglong 2013-02-04  
longfor5 写道
持续关注,请问博主,过年期间还会有更新吗

过年和写博文不冲突,呵呵。

相关推荐

Global site tag (gtag.js) - Google Analytics