Spring AOP原理及拦截器
来源:互联网 发布:mac 粉色口红 编辑:程序博客网 时间:2024/05/22 17:12
原理
AOP(Aspect Oriented Programming),也就是面向方面编程的技术。AOP基于IoC基础,是对OOP的有益补充。
AOP将应用系统分为两部分,核心业务逻辑(Core business concerns)及横向的通用逻辑,也就是所谓的方面Crosscutting enterprise concerns,例如,所有大中型应用都要涉及到的持久化管理(Persistent)、事务管理(Transaction Management)、安全管理(Security)、日志管理(Logging)和调试管理(Debugging)等。
AOP正在成为软件开发的下一个光环。使用AOP,你可以将处理aspect的代码注入主程序,通常主程序的主要目的并不在于处理这些aspect。AOP可以防止代码混乱。
Spring framework是很有前途的AOP技术。作为一种非侵略性的、轻型的AOP framework,你无需使用预编译器或其他的元标签,便可以在Java程序中使用它。这意味着开发团队里只需一人要对付AOP framework,其他人还是像往常一样编程。
AOP概念
让我们从定义一些重要的AOP概念开始。
— 方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的Advisor或拦截器实现。
— 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
— 通知(Advice):在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。
— 切入点(Pointcut):指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点,例如,使用正则表达式。
— 引入(Introduction):添加方法或字段到被通知的类。Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现IsModified接口,来简化缓存。
— 目标对象(Target Object):包含连接点的对象,也被称作被通知或被代理对象。
— AOP代理(AOP Proxy):AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。
— 编织(Weaving):组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。
各种通知类型包括:
—
—
—
—
Around通知是最通用的通知类型。大部分基于拦截的AOP框架(如Nanning和Jboss 4)只提供Around通知。
如同AspectJ,Spring提供所有类型的通知,我们推荐你使用最为合适的通知类型来实现需要的行为。例如,如果只是需要用一个方法的返回值来更新缓存,你最好实现一个after returning通知,而不是around通知,虽然around通知也能完成同样的事情。使用最合适的通知类型使编程模型变得简单,并能减少潜在错误。例如,你不需要调用在around通知中所需使用的MethodInvocation的proceed()方法,因此就调用失败。
切入点的概念是AOP的关键,它使AOP区别于其他使用拦截的技术。切入点使通知独立于OO的层次选定目标。例如,提供声明式事务管理的around通知可以被应用到跨越多个对象的一组方法上。因此切入点构成了AOP的结构要素。
- <span
style= "font-size:medium;" >packagecom.test.TestSpring3; - public
interface UserService //被拦截的接口 - ...{
public void printUser(String user); - }
- </span>
- <span
style= "font-size:medium;" >packagecom.test.TestSpring3; - public
class UserServiceImp implementsUserService //实现UserService接口 - ...{
public void printUser(String user) ...{ System.out.println("printUser user:" + //user); 显示user } - }
- </span>
- <span
style= "font-size:medium;" >packagecom.test.TestSpring3; - import
org.aopalliance.intercept.MethodInterceptor; - import
org.aopalliance.intercept.MethodInvocation; - public
class UserInterceptor implementsMethodInterceptor - //
AOP方法拦截器 - ...{
public Object throwsinvoke(MethodInvocation arg0) Throwable ...{ try ...{ if (arg0.getMethod().getName().equals( "printUser"))// 拦截方法是否是UserService接口的printUser方法 ...{ Object[] args = arg0.getArguments();// 被拦截的参数 System.out.println("user:" + 0]);args[ arg0.getArguments()[0] = "hello!";//修改被拦截的参数 } System.out.println(arg0.getMethod().getName() + "---!"); return arg0.proceed(); //运行UserService接口的printUser方法 } catch (Exception e) ...{ throw e; } } - }
- </span><span
style="font-size: medium;" > - </span>
- <span
style= "font-size:medium;" >packagecom.test.TestSpring3; - import
org.springframework.beans.factory.BeanFactory; - import
org.springframework.beans.factory.xml.XmlBeanFactory; - import
org.springframework.context.ApplicationContext; - import
org.springframework.context.support.ClassPathXmlApplicationC ontext; - import
org.springframework.context.support.FileSystemXmlApplication Context; - import
org.springframework.core.io.ClassPathResource; - import
org.springframework.core.io.Resource; - import
org.springframework.web.context.support.WebApplicationContextUti ls; - public
class TestInterceptor ...{ public static void main(String[] args) ...{ ApplicationContext ctx = new FileSystemXmlApplication Context( "classpath:applicationContext.xml"); - //
ApplicationContext ctx = new ClassPathXmlApplicationC ontext("applicationContext.xml"); UserService us = (UserService) ctx.getBean("userService"); us.printUser("shawn"); } - }</span><span
style="font-size: medium;" > - </span>
- <span
style="font-size: medium;" ><?xmlversion="1.0" encoding="UTF-8"?> - <!DOCTYPE
beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> - <beans>
<bean id="userServiceImp" class="com.test.TestSpring3.UserServiceImp" /> <bean id="userInterceptor" class="com.test.TestSpring3.UserInterceptor" /> <bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 代理接口 --> <property name="proxyInterfaces"> <value>com.test.TestSpring3.UserService</value> </property> <!-- 目标实现类 --> <property name="target"> <ref local="userServiceImp" /> </property> <!-- 拦截器 --> <property name="interceptorNames"> <list> <value>userInterceptor</value> </list> </property> </bean> - </beans>
- </span>
结论:调用方法的时候
拦截器中的事务管理(事务拦截机)
- 自动提交
这是JDBC驱动默认的模式,每次数据库操作(CRUD)成功完成后,都作为一个单独的事务自动提交,如果未成功完成,即抛出了 SQLException 的话,仅最近的一个操作将回滚。 - 非自动提交
这是想更好的控制事务时需要程序地方式进行控制: - 在进行该事务单元的任何操作之前
setAutoCommit(false) - 在成功完成事务单元后
commit() - 在异常发生后
rollback()
- 在进行该事务单元的任何操作之前
自动提交模式是不被推荐的,因为每个操作都将产生一个事务点,这对于大的应用来说性能将受到影响;再有,对于常见的业务逻辑,这种模式显得无能为力。比如:
转帐,从A帐户取出100元,将其存入B帐户;如果在这两个操作之间发生了错误,那么用户A将损失了100元,而本来应该给帐户B的,却因为失败给了银行。
所以,建议在所有的应用中,如果使用 JDBC 都将不得不采用非自动提交模式(你们要能发现了在我们的 JDBC 那个例子中,我们采用的就是自动提交模式,我们是为了把精力放在JDBC上,而不是事务处理上),即我们不得不在每个方法中:
- <span
style= "font-size:medium;" >try{ // 在获得连接后,立即通过调用 setAutoCommit(false) 将事务处理置为非自动提交模式 // Prepare Query to fetch the user Information pst = conn.prepareStatement(findByName); // ... conn.commit(); } catch(Exception ex) { conn.rollback(); throw ex; }finally { try { // Close Result Set and Statement if (rset null)!= rset.close(); if (pst null)!= pst.close(); } catch (Exception ex) { ex.printStackTrace(); throw new Exception( "SQLError while closing objects = " + ex.toString()); - }
- }
- </span>
我先看看 Spring 是怎么做到拦截的:
Spring 内置支持的事务处理拦截机
这里因为要用到JpetStore项目中的代码,我们将 applicationContext.xml 全部内容列出:
<?xml version="1.0" encoding="UTF-8"?>
<!-- - Application context definition for JPetStore's business layer. - Contains bean references to the transaction manager and to the DAOs in - dataAccessContext-local/jta.xml (see web.xml's "contextConfigLocation"). Jpetstore 的应用上下文定义,包含事务管理和引用了在 dataAccessContext-local/jta.xml(具体使用了哪个要看 web.xml 中的 'contextConfigLocation' 的配置)中注册的DAO --> <beans xmlns="http://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/beans 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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<!-- ========================= GENERAL DEFINITIONS ========================= -->
<!-- Configurer that replaces ${...} placeholders with values from properties files 占位符的值将从列出的属性文件中抽取出来 --> <!-- (in this case, mail and JDBC related properties) -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfi
</bean>
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
</bean>
<!-- 不需要,因为被 SpringMVC 的实现使用 Generic validator for Account objects, to be used for example by the Spring web tier -->
<bean id="orderValidator" class="org.springframework.samples.jpetstore.domain.logic.OrderValidator"/>
<!-- 主要的商业逻辑对象,即我们所说的门面对象注入了所有的DAO,这些DAO是引用了 dataAccessContext-xxx.xml 中定义的DAO 门面对象中的所有方法的事务控制将通过下面的 aop:config 来加以控制 - JPetStore primary business object (default implementation). - Transaction advice gets applied through the AOP configuration below. -->
<bean id="petStore" class="org.springframework.samples.jpetstore.domain.logic.PetStoreImpl">
<!-- ========================= ASPECT CONFIGURATION ======================== -->
<!-- AOP配置,用来控制哪些方法将需要进行事务处理,采用了AspectJ 的语法 -->
<aop:config>
<!-- This definition creates auto-proxy infrastructure based on the given pointcut, expressed in AspectJ pointcut language. Here: applying the advice named "txAdvice" to all methods on classes named PetStoreImpl. -->
<aop:advisor pointcut="execution(* *..PetStoreFacade.*(..))" advice-ref="txAdvice"/>
<!-- This definition creates auto-proxy infrastructure based on the given pointcut, expressed in AspectJ pointcut language. Here: applying the advice named "emailAdvice" to insertOrder(Order) method of PetStoreImpl -->
<!-- 当执行 PetStoreFacade.insertOrder方法,该方法最后一个参数为Order类型时(其实我们的例子中只有一个 insertOrder 方法,但这告诉了我们,当我们的接口或类中有重载了的方法, 并且各个重载的方法可能使用不同的拦截机机制时,我们可以通过方法的参数加以指定),将执行emailAdvice(在最后定义的那个元素)-->
<aop:advisor pointcut="execution(* *..PetStoreFacade.insertOrder(*..Order))" advice-ref="emailAdvice"/>
</aop:config>
<!-- 事务方针声明,用于控制采用什么样的事务策略 Transaction advice definition, based on method name patterns. Defaults to PROPAGATION_REQUIRED for all methods whose name starts with "insert" or "update", and to PROPAGATION_REQUIRED with read-only hint for all other methods. -->
</tx:advice>
<!-- 拦截机,用于在适当的时机(通过AOP配置,如上面)在方法执行成功后发送邮件 AOP advice used to send confirmation email after order has been submitted -->
<!-- -->
<bean id="emailAdvice" class="org.springframework.samples.jpetstore.domain.logic.SendOrderConfirmationEma
<!-- ========================= 忽略 REMOTE EXPORTER DEFINITIONS ======================== -->
</beans>
这个配置比想象的要简单的多:
- <span
style="font-size: medium;" ><aop:config> <!-- This definition creates auto-proxy infrastructure based on the given pointcut, expressed in AspectJ pointcut language. - Here:
applying the advice named "txAdvice" to all methods on classes named PetStoreImpl. 指出在 PetStoreFacade - 的所有方法都将采用
txAdvice(在紧接着的元素中定义了)事务方针,注意,我们这里虽然指定的是接口 PetStoreFacace, 但其暗示着其所有的实现类也将同样具有这种性质,因为本身就是实现类的方法在执行的,接口是没有方法体的。 --> <aop:advisor pointcut="execution(* *..PetStoreFacade.*(..))" advice-ref="txAdvice"/> <!-- 其它拦截机--> - </aop:config>
- </span>
1. 所有的拦截机配置都放在 <aop:config> 配置元素中.
2. 下面还是需要理解一下几个有关AOP的专用名词,不过,是挺抽象的,最好能会意出其的用意
- pointcut 切入点,比如:updateAccount 方法需要进行事务管理,则这个切入点就是“执行方法体”(execution)。Spring 所有支持的切入点类型在都在 Spring reference: 6.2.3.1. Supported Pointcut Designators 中列出了。
- advice
要对这个切入点进行什么操作,比如事务控制 - advisor
Spring 特有的概念,将上两个概念合到一个概念中来,即一个 advisor 包含了一个切入点及对这个切入点所实施的操作。
因为 方法执行切入点 execution 为最常见的切入点类型,我们着重介绍一下,execution 的完全形式为:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
这是一个正则表达式,其中由 '?' 结尾的部分是可选的。翻译过来就是:
执行(方法访问修饰符? 方法返回类型 声明类型? 方法名(方法参数类型) 抛出异常?)
所有的这些都是用来定义执行切入点,即那些方法应该被侯选为切入点:
- 方法访问修饰符
即 public, private 等等 - 方法返回类型
即方法返回的类型,如 void, String 等等 - 声明类型
1.5的语法,现在可以先忽略它 - 方法名
方法的名字 - 方法参数类型
方法的参数类型 - 抛出异常
方法声明的抛出的异常
例如,所有dao代码被定义在包 com.xyz.dao 及子包 com.xyz.dao.hibernate, 或者其它,如果还有的话,子包中, 里面定义的是提供DAO功能的接口或类,那么表达式:
execution(* com.xyz.dao..*.*(..))
表示切入点为:执行定义在包 com.xyz.dao 及其子包(因为
详细情况可以参见 Spring refernce:
<aop:advisor pointcut="execution(* *..PetStoreFacade.*(..))" advice-ref="txAdvice"/>
Spring 自定拦截机
来为了进行事务控制,我们只需简单地配置几下,所有的工作都由 Spring 来做。这样固然很好,但有时我们需要有我们特有的控制逻辑。因为Spring 不可能包含所有人需要的所有拦截机。所以它提供了通过程序的方式加以定制的方式。我们的项目中就有这么一个拦截机,在用户确认付款后,将定单信息通过 email 的方式发送给注册用户的邮箱中。
<aop:config> ... <!--当执行 PetStoreFacade.insertOrder方法,该方法最后一个参数为Order类型时(其实我们的例子中只有一个 insertOrder 方法,但这告诉了我们,当我们的接口或类中有重载了的方法,
并且各个重载的方法可能使用不同的拦截机机制时,我们可以通过方法的参数加以指定),将执行emailAdvice(在最后定义的那个元素)--> <aop:advisor pointcut="execution(* *..PetStoreFacade.insertOrder(*..Order))" advice-ref="emailAdvice"/> </aop:config>
红色的注释已经说的很清楚这个 Advisor 了,它的切入点(pointcut) 为 PetStoreFacade 的 void insertOrder(Order order) 方法,采取的动作为引用的 emailAdvice, 下面我们就来看看 emailAdvice:
<bean id="emailAdvice" class="org.springframework.samples.jpetstore.domain.logic.SendOrderConfirmationEma
它给了这个 advice 的实现类为 logic 包中 SendOrderConfirmationEma
下面看看这个实现类:
public class SendOrderConfirmationEma
this.mailSender = mailSender;
} public void setMailFrom(String mailFrom) { this.mailFrom = mailFrom; } public void setSubject(String subject) { this.subject = subject; } public void throws Exception { if (this.mailSender == null) { throw new IllegalStateException("mailSender is required"); } } public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable {
1.
2.
3.
接口AfterReturningAdvice
方法代码的工作其实并不重要,只要我们理解这些“魔法”一样的技术后,实现代码是很简单的。值得提及的是这个方法的参数,这些参数是封装了切入点的所有信息,请见上面的注释。在我们的实现中只使用了被拦截方法的参数,在复杂的 Advice 实现中可能会用到切入点所有信息。
- Spring AOP原理及拦截器
- Spring Aop原理及拦截器
- Spring AOP原理及拦截器
- Spring AOP原理及拦截器
- Spring AOP原理及拦截器
- Spring AOP原理及拦截器
- Spring AOP原理及拦截器
- Spring AOP原理及拦截器
- Spring AOP原理及拦截器
- Spring AOP原理及拦截器
- Spring AOP原理及拦截器
- Spring AOP原理及拦截器
- Spring AOP原理及拦截器
- AAA Spring AOP原理及拦截器(aop配置最简单例子)
- Spring事务拦截器 AOP原理
- spring AOP 原理与拦截器
- Spring 拦截器原理:什么是AOP
- Spring学习笔记(13)——aop原理及拦截器
- 利用施瓦茨排序获得范围数据的并集
- iOS中的生成随机数方法
- 东软学习,oracle函数与触发器2
- TabHost导致百度定位功能失效
- 企业inhouse网页发布流程
- Spring AOP原理及拦截器
- 常犯错误总结
- TIPTOP GP5打印报表时如何在水晶报表中显示服务器上的图片
- Thinkphp ajax返回值
- C#Winform无边框窗体如何点击系统任务栏显示和隐藏窗体
- POJ 2723 Get Luffy Out(图论-2SAT,搜索-二分)
- JAVA基础回顾2 遍历指定文件目录 遍历删除目录 指定文件清单列表
- 你是正确额
- hdu 3308 线段树