SpringAOP原理及拦截器版.docx
《SpringAOP原理及拦截器版.docx》由会员分享,可在线阅读,更多相关《SpringAOP原理及拦截器版.docx(24页珍藏版)》请在冰豆网上搜索。
SpringAOP原理及拦截器版
原理
AOP(AspectOrientedProgramming),也就是面向方面编程的技术。
AOP基于loC基础,是对OOF的有益补充。
AOP将应用系统分为两部分,核心业务逻辑(Corebusinessconcerns)及横向的通用逻辑,也就是所谓的方面Crosscuttingenterpriseconcerns,例如,所有大中型应用都要涉及到的持久化管理(Fersistent)、事务管理(TransactionManagement)、安全管理(Security)、日志管理(Logging)和调试管理(Debugging)等。
AOP正在成为软件开发的下一个光环。
使用AOP你可以将处理
aspect的代码注入主程序,通常主程序的主要目的并不在于处理这些aspect。
AOF可以防止代码混乱。
Springframework是很有前途的AOF技术。
作为一种非侵略性的、轻型的AOPframework,你无需使用预编译器或其他的元标签,便可以在Java程序中使用它。
这意味着开发团队里只需一人要对付AOFframework,其他人还是像往常一样编程。
AOP既念
让我们从定义一些重要的AOP概念开始。
—方面(Aspect):
一个关注点的模块化,这个关注点实现可能另外横切多个对象。
事务管理是J2EE应用中一个很好的横切关注点例子。
方面用Spring的Advisor或拦截器实现。
—连接点(Joinpoint):
程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
—通知(Advice):
在特定的连接点,AOP匡架执行的动作。
各种类型的通知包括“around”、“before”和“throws”通知。
通知类型将在下面讨论。
许多AOP匡架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。
—切入点(Pointcut):
指定一个通知将被引发的一系列连接点的集合。
AOP匡架必须允许开发者指定切入点,例如,使用正则表达式。
—引入(Introduction):
添加方法或字段到被通知的类。
Spring允许引入新的接口到任何被通知的对象。
例如,你可以使用一个引入使任何对象实现IsModified接口,来简化缓存。
—目标对象(TargetObject):
包含连接点的对象,也被称作被通知或被代理对象。
—AOP代理(AOPProxy):
AOPf!
架创建的对象,包含通知。
在
Spring中,AOP代理可以是JDK动态代理或CGLIB代理。
—编织(Weaving):
组装方面来创建一个被通知对象。
这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。
Spring和其他纯JavaAOP框架一样,在运行时完成织入。
各种通知类型包括:
—Around通知:
包围一个连接点的通知,如方法调用。
这是最强大的通知。
Aroud通知在方法调用前后完成自定义的行为,它们负责选择继续执行连接点或通过返回它们自己的返回值或抛出异常来短路执行。
—Before通知:
在一个连接点之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。
—Throws通知:
在方法抛出异常时执行的通知。
Spring提供强制类型的Throws通知,因此你可以书写代码捕获感兴趣的异常(和它的子类),不需要从Throwable或Exception强制类型转换。
—Afterreturning通知:
在连接点正常完成后执行的通知,例如,一个方法正常返回,没有抛出异常。
Around通知是最通用的通知类型。
大部分基于拦截的AOP匡架(如Nanning和Jboss4)只提供Around通知。
如同AspectJ,Spring提供所有类型的通知,我们推荐你使用最为合适的通知类型来实现需要的行为。
例如,如果只是需要用一个方
法的返回值来更新缓存,你最好实现一个afterreturning通知,而
不是around通知,虽然around通知也能完成同样的事情。
使用最合适的通知类型使编程模型变得简单,并能减少潜在错误。
例如,你不
需要调用在around通知中所需使用的Methodinvocation的proceed。
方法,因此就调用失败。
切入点的概念是AOP勺关键,它使AOP区别于其他使用拦截的技术。
切入点使通知独立于00的层次选定目标。
例如,提供声明式事务管理的around通知可以被应用到跨越多个对象的一组方法上。
因此
切入点构成了AOP的结构要素。
拦截器(也称拦截机)
拦截机(Interceptor),是AOP(Aspect-Oiented
Programming)的另一种叫法。
AOP本身是一门语言,只不过我们使用的是基于JAVA的集成到Spring中的SpringAOP。
同样,我们将通过我们的例子来理解陌生的概念。
接口类
Java代码站“
1.medium;">packagecom.test.TestSprin
g3;
2.
2.publicinterfaceUserService//被拦截的接口
4•…{
5.publicvoidprintUser(Stringuser);
6.}
7.
实现类
Java代码$
1.medium;">packagecom.test.TestSprin
g3;
2.
3.publicclassUserServiceImpimplementsUserService//实现UserService接口
4•…{
5.publicvoidprintUser(Stringuser)...{
6.System.out.println("printUseruser:
"+user
);//显示user
7.}
8.}
9.
9.
AOP兰截器
Java代码呼
1.medium;">packagecom.test.TestSprin
g3;
2.
2.importorg.aopalliance.intercept.Methodlnterceptor;
3.importorg.aopalliance.intercept.Methodlnvocation;
5.
4.publicclassUserInterceptorimplementsMethodInterceptor
5.//AOP方法拦截器
6....{
9.
10.
public
Object
invoke(MethodInvocationarg0)throws
Throwable..
.{
11.
12.
try..
.{
13.
14.
if
(argO.getMethod().getName().equals
("printUser"))
15.
//
拦截方法是否是UserService接口的
printUser方法
16.
...{
17.
Object[]args=arg0.getArg
uments();〃
被拦截的参数
18.
System.out.println("user:
"+
args[0]);
19.
argO.getArguments()[0]="he
llo!
";//
修改被拦截的参数
20.
21.
}
22.
23.
System.out.println(argO.getMethod().ge
tName()+
"---!
");
24.
returnarg0.proceed();//运行
UserService
接口的printUser方法
25.
26.
}catch(Exceptione)...{
27.
throwe;
28.
}
29.
}
30.}
31.medium;"〉
32.v/span>
测试类
Java代码站-
1.medium;">packagecom.test.TestSprin
g3;
2.
2.importorg.springframework.beans.factory.BeanFactory;
4.
3.importorg.springframework.beans.factory.xml.XmlBeanFactory;
4.importorg.springframework.context.ApplicationContext;
5.importorg.springframework.context.support.ClassPathXmlApplic
ationContext;
6.importorg.springframework.context.support.FileSystemXmlAppli
cationContext;
7.importorg.springframework.core.io.ClassPathResource;
10.importorg.springframework.core.io.Resource;
11.importorg.springframework.web.context.support.WebApplication
ContextUtils;
12.
13.publicclassTestInterceptor...{
14.
15.publicstaticvoidmain(String[]args)...{
16.ApplicationContextctx=newFileSystemXmlApplicationContext(
17."classpath:
applicationContext.xml");
18.//ApplicationContextctx=newClassPathXm
lApplicationContext("applicationContext.xml");
19.
UserServiceus=(UserService)
us.printUser("shawn");
style="font-size:
medium;"〉
配置文件
Xml代码
1.medium;">
xmlversion="1.0"encod
ing="UTF-8"?
>
2.
DOCTYPEbeansPUBLIC"-//SPRING//DTDBEAN//EN""http:
//www.springframework.org/dtd/spring-beans.dtd">
3.
4.5.class="com.test.TestSpring3.UserServiceImp"/
>
6.
6.g3.UserInterceptor"/>
8.
7.8.class="org.springframework.aop.framework.ProxyFactoryBean">
9.
--代理接口-->
10.
13.
com.test.TestSpring3.UserServic
e
14.
15.
--目标实现类-->
16.
17.
18.
19.
--拦截器-->
20.
21.
22.
userInterceptor
23.
24.
25.
26.
27.
28.
输出:
user:
shawn
printUser---!
printUseruser:
hello!
结论:
调用方法的时候传入的值被拦截修改了.拦截器中的事务管理(事务拦截机)
如果不采用拦截机的机制时,在使用JDBC进行数据库访问时,存在
两种情况:
•自动提交这是JDBCW动默认的模式,每次数据
库操作(CRUD成功完成后,都作为一个单独的事务自动提交,如果未成功完成,即抛出了SQLException的话,仅最近的一个操作将回滚。
•非自动提交这是想更好的控制事务时需要程序地方式进
行控制:
o在进行该事务单元的任何操作之前
setAutoCommit(false)
o在成功完成事务单元后commit()
o在异常发生后rollback。
自动提交模式是不被推荐的,因为每个操作都将产生一个事务点,这对于大的应用来说性能将受到影响;再有,对于常见的业务逻辑,这种模式显得无能为力。
比如:
转帐,从A帐户取出100元,将其存入B帐户;如果在这两个操作之间发生了错误,那么用户A将损失了100元,而本来应该给帐户B的,却因为失败给了银行。
所以,建议在所有的应用中,如果使用JDBC都将不得不采用非自动
提交模式(你们要能发现了在我们的JDBC那个例子中,我们采用的就是自动提交模式,我们是为了把精力放在JDBC上,而不是事务处理上),即我们不得不在每个方法中:
1.Java代码站”
medium;">try{
2.//在获得连接后,立即通过调用setAutoCommit(false)将事务处理置为非自动提交模
式
//
Prepare
Querytofetchthe
userInformation
3.
pst=
conn.prepareStatement(findByName);
4.
//
...
mit();
5.
}catch(Exceptionex){
6.
conn.rollback();
7.
throw
ex;
8.
}finally
{
9.
try{
10.
//CloseResult
SetandStatement
11.
if(rset!
=null)
rset.close();
12.
if(pst!
=null)pst.close();
13.
}
catch
(Exceptionex){
14.
ex.printStackTrace();
15.
thrownewException("SQL
Errorwhileclosin
g
objects
="
+ex.toString());
16.}
17.}
18.
这样代码在AOP勺倡导者看来是“肮脏”的代码。
他们认为,所有的与事务有关的方法都应当可以集中配置(见声明性事务控制),并自动拦截,程序应当关心他们的主要任务,即商业逻辑,而不应和事务处理的代码搅和在一起。
我先看看Spring是怎么做到拦截的:
1Spring内置支持的事务处理拦截机这里因为要用到JpetStore项目中的代码,我们将applicationContext.xml全部内容列出:
xmlversion="1.0"encoding="UTF-8"?
>
--
-ApplicationcontextdefinitionforJPetStore'sbusinesslayer.
-ContainsbeanreferencestothetransactionmanagerandtotheDAOsin
-dataAccessContext-local/jta.xml(seeweb.xml's"contextConfigLocation").
Jpetstore的应用上下文定义,包含事务管理和引用了在dataAccessContext-local/jta.xml(具体使用了哪个要看web.xml中的'contextConfigLocation'的配置)中注册的DAO-->
//www.springframework.org/schema/beans"xmlns:
xsi="http:
//www.w3.org/2001/XMLSchema-instance"xmlns:
aop="http:
//www.springframework.org/schema/aop"xmlns:
tx="http:
//www.springframework.org/schema/tx"
xsi:
schemaLocation="http:
//www.springframework.org/schema/be
ans
http:
//www.springframework.org/schema/beans/spring-beans-2.0.xsd
http:
//www.springframework.org/schema/aop
http:
//www.springframework.org/schema/aop/spring-aop-2.0.xsdhttp:
//www.springframework.org/schema/txhttp:
//www.springframework.org/schema/tx/spring-tx-2.0.xsd">
--
GENERALDEFINITIONS
=========================-->
--Configurerthatreplaces${...}placeholderswithvaluesfrompropertiesfiles
占位符的值将从列出的属性文件中抽取出来
-->
--(inthiscase,mailandJDBCrelatedproperties)-->
WEB-INF/mail.properties
WEB-INF/jdbc.properties
--MailSenderusedbyEmailAdvice
指定用于发送邮件的javamail实现者,这里使用了
spring自带的实现。
此bean将被emailAdvice使用
-->
class="org.springframework.mail.javamail.JavaMailSenderImpl"
>
--
========================B=USINESSOBJECTDEFINITIONS========================-->
--不需要,因为被SpringMVC的实现使用Generic
validatorforAccountobjects,tobeusedforexamplebytheSpringwebtier-->
--不需要,因为被SpringMVC的实现使用Generic
validatorforOrderobjects,tobeusedforexamplebytheSpringwebtier-->
--主要的商业逻辑对象,即我们所说的门面对象注入了所有的DAO这些DAO是引用了dataAccessContext-xxx.xml中定义的DAO门面对象中的所有方法的事务控制将通过下面的aop:
config来加以控制
-JPetStoreprimarybusinessobject(defaultimplementation).
-TransactionadvicegetsappliedthroughtheAOPconfigurationbelow.
-->
class="org.springframework.samples.jpetstore.domain.logic.PetStoreImpl">