目录搜索
前言简介概览使用场景Spring 2.0和 2.5的新特性简介控制反转(IoC)容器新的bean作用域更简单的XML配置可扩展的XML编写Annotation(注解)驱动配置在classpath中自动搜索组件面向切面编程(AOP)更加简单的AOP XML配置对@AspectJ 切面的支持对bean命名pointcut( bean name pointcut element)的支持对AspectJ装载时织入(AspectJ load-time weaving)的支持中间层在XML里更为简单的声明性事务配置对Websphere 事务管理的完整支持JPA异步的JMSJDBCWeb层Spring MVC合理的默认值Portlet 框架基于Annotation的控制器Spring MVC的表单标签库对Tiles 2 支持对JSF 1.2支持JAX-WS支持其他动态语言支持增强的测试支持JMX 支持将Spring 应用程序上下文部署为JCA adapter计划任务对Java 5 (Tiger) 支持移植到Spring 2.5改变支持的JDK版本Spring 2.5的Jar打包XML配置Deprecated(淘汰)的类和方法Apache OJBiBATISHibernateJDOUrlFilenameViewController更新的样例应用改进的文档核心技术IoC(控制反转)容器简介基本原理 - 容器和bean容器配置元数据实例化容器XML配置元数据的结构多种beanbean的命名bean的别名实例化bean用构造器来实例化使用静态工厂方法实例化使用实例工厂方法实例化使用容器依赖注入依赖构造器注入构造器参数解析构造器参数类型匹配构造参数索引Setter注入一些例子依赖配置详解直接变量(基本类型、Strings类型等。)idref元素引用其它的bean(协作者)内部bean集合集合的合并强类型集合(仅适用于Java5+)NullsXML配置文件的简写及其他XML-based configuration metadata shortcuts使用p名称空间配置属性组合属性名称使用depends-on延迟初始化bean自动装配(autowire)协作者将bean排除在自动装配之外依赖检查方法注入Lookup方法注入自定义方法的替代方案Bean的作用域Singleton作用域Prototype作用域Singleton beans和prototype-bean的依赖其他作用域初始化web配置Request作用域Session作用域global session作用域作用域bean与依赖选择创建代理的类型自定义作用域创建自定义作用域使用自定义作用域定制bean特性生命周期回调初始化回调析构回调缺省的初始化和析构方法组合生命周期机制在非web应用中优雅地关闭Spring IoC容器了解自己BeanFactoryAwareBeanNameAwarebean定义的继承容器扩展点用BeanPostProcessor定制bean使用BeanPostProcessor的Hello World示例RequiredAnnotationBeanPostProcessor示例用BeanFactoryPostProcessor定制配置元数据PropertyPlaceholderConfigurer示例PropertyOverrideConfigurer示例使用FactoryBean定制实例化逻辑The ApplicationContextBeanFactory 还是 ApplicationContext?利用MessageSource实现国际化事件底层资源的访问ApplicationContext在WEB应用中的实例化粘合代码和可怕的singleton以J2EE RAR文件的形式部署Spring ApplicationContext基于注解(Annotation-based)的配置@Autowired基于注解的自动连接微调CustomAutowireConfigurer@Resource@PostConstruct 与 @PreDestroy对受管组件的Classpath扫描@Component和更多典型化注解自动检测组件使用过滤器自定义扫描自动检测组件的命名为自动检测的组件提供一个作用域用注解提供限定符元数据注册一个LoadTimeWeaver资源简介Resource接口内置 Resource 实现UrlResourceClassPathResourceFileSystemResourceServletContextResourceInputStreamResourceByteArrayResourceResourceLoader接口ResourceLoaderAware 接口把Resource作为属性来配置Application context 和Resource 路径构造application context创建 ClassPathXmlApplicationContext 实例 - 简介Application context构造器中资源路径的通配符Ant风格的pattern潜在的可移植性classpath*: 前缀其他关于通配符的说明FileSystemResource 说明校验,数据绑定,BeanWrapper,与属性编辑器简介使用Spring的Validator接口进行校验从错误代码到错误信息Bean处理和BeanWrapper设置和获取属性值以及嵌套属性内建的PropertyEditor实现注册用户自定义的PropertyEditor使用PropertyEditorRegistrars使用Spring进行面向切面编程(AOP)简介AOP概念Spring AOP的功能和目标AOP代理@AspectJ支持启用@AspectJ支持声明一个切面声明一个切入点(pointcut)切入点指示符(PCD)的支持组合切入点表达式共享通用切入点定义示例声明通知前置通知后置通知(After returning advice)异常通知(After throwing advice)最终通知(After (finally) advice)环绕通知通知参数(Advice parameters)访问当前的连接点传递参数给通知确定参数名处理参数通知顺序引入(Introduction)切面实例化模型例子基于Schema的AOP支持声明一个切面声明一个切入点声明通知前置通知后置通知异常通知最终通知环绕通知通知参数通知顺序引入切面实例化模型Advisor例子AOP声明风格的选择Spring AOP还是完全用AspectJ?Spring AOP中使用@AspectJ还是XML?混合切面类型代理机制理解AOP代理以编程方式创建@AspectJ代理在Spring应用中使用AspectJ在Spring中使用AspectJ进行domain object的依赖注入@Configurable对象的单元测试Working with multiple application contextsSpring中其他的AspectJ切面使用Spring IoC来配置AspectJ的切面在Spring应用中使用AspectJ加载时织入(LTW)第一个例子切面'META-INF/aop.xml'相关类库(JARS)Spring配置特定环境的配置通用Java应用TomcatWebLogic更多资源Spring AOP APIs简介Spring中的切入点API概念切入点运算AspectJ切入点表达式便利的切入点实现静态切入点正则表达式切入点属性驱动的切入点动态切入点控制流切入点切入点的超类自定义切入点Spring的通知API通知的生命周期Spring里的通知类型拦截环绕通知前置通知异常通知后置通知引入通知Spring里的Advisor API使用ProxyFactoryBean创建AOP代理基础JavaBean属性基于JDK和CGLIB的代理对接口进行代理对类进行代理使用“全局”通知器简化代理定义使用ProxyFactory通过编程创建AOP代理操作被通知对象使用“自动代理(autoproxy)”功能自动代理bean定义BeanNameAutoProxyCreatorDefaultAdvisorAutoProxyCreatorAbstractAdvisorAutoProxyCreator使用元数据驱动的自动代理使用TargetSource热交换目标源池化目标源原型目标源ThreadLocal目标源定义新的Advice类型更多资源测试简介单元测试Mock对象JNDIServlet APIPortlet API单元测试支持类通用工具类Spring MVC集成测试概览使用哪个支持框架通用目标上下文管理及缓存测试fixtures依赖注入事务管理集成测试支持类JDBC测试支持常用注解JUnit 3.8遗留支持上下文管理及缓存测试fixture依赖注入字段级别(Field Level)注入事务管理JUnit 3.8 遗留支持类Java 5+ 专有支持使用注解的事务相关测试JPA支持类Spring TestContext Framework主要的抽象上下文管理和缓存测试fixture的依赖注入事务管理TestContext支持类JUnit 3.8支持类JUnit 4.4支持类定制JUnit 4.4运行器TestNG支持类TestContext框架注解支持PetClinic示例更多资源中间层数据访问事务管理简介动机关键抽象使用资源同步的事务高层次方案低层次方案TransactionAwareDataSourceProxy声明式事务管理理解Spring的声明式事务管理实现第一个例子回滚为不同的bean配置不同的事务语义<tx:advice/> 有关的设置使用 @Transactional@Transactional 有关的设置事务传播requiredRequiresNewNested通知事务操作结合AspectJ使用 @Transactional编程式事务管理使用TransactionTemplate指定事务设置使用PlatformTransactionManager选择编程式事务管理还是声明式事务管理与特定应用服务器集成IBM WebSphereBEA WebLogicOracle OC4J常见问题的解决方法对一个特定的 DataSource 使用了错误的事务管理器更多的资源DAO支持简介一致的异常层次一致的DAO支持抽象类使用JDBC进行数据访问简介选择一种工作模式Spring JDBC包结构利用JDBC核心类控制JDBC的基本操作和错误处理JdbcTemplate类一些示例查询(SELECT)更新(INSERT/UPDATE/DELETE)其他操作JdbcTemplate 的最佳实践NamedParameterJdbcTemplate类SimpleJdbcTemplate类DataSource接口SQLExceptionTranslator接口执行SQL语句执行查询更新数据库获取自动生成的主键控制数据库连接DataSourceUtils类SmartDataSource接口AbstractDataSource类SingleConnectionDataSource类DriverManagerDataSource类TransactionAwareDataSourceProxy类DataSourceTransactionManager类NativeJdbcExtractorJDBC批量操作使用JdbcTemplate进行批量操作使用SimpleJdbcTemplate进行批量操作通过使用SimpleJdbc类简化JDBC操作使用SimpleJdbcInsert插入数据使用SimpleJdbcInsert来获取自动生成的主键指定SimpleJdbcInsert所使用的字段使用SqlParameterSource提供参数值使用SimpleJdbcCall调用存储过程声明SimpleJdbcCall使用的参数如何定义SqlParameters使用SimpleJdbcCall调用内置函数使用SimpleJdbcCall返回的ResultSet/REF Cursor用Java对象来表达JDBC操作SqlQuery类MappingSqlQuery类SqlUpdate类StoredProcedure类SqlFunction类参数和数据处理的基本原则为参数设置SQL类型信息处理BLOB 和 CLOB对象在IN语句中传入一组参数值处理复杂类型的存储过程调用使用ORM工具进行数据访问简介Hibernate资源管理在Spring容器中创建 SessionFactoryThe HibernateTemplate不使用回调的基于Spring的DAO实现基于Hibernate3的原生API实现DAO编程式的事务划分声明式的事务划分事务管理策略容器资源 vs 本地资源在应用服务器中使用Hibernate的注意事项JDO建立PersistenceManagerFactoryJdoTemplate和JdoDaoSupport基于原生的JDO API实现DAO事务管理JdoDialectOracle TopLinkSessionFactory 抽象层TopLinkTemplate and TopLinkDaoSupport基于原生的TopLink API的DAO实现事务管理iBATIS SQL Maps创建SqlMapClient使用 SqlMapClientTemplate 和 SqlMapClientDaoSupport基于原生的iBATIS API的DAO实现JPA在Spring环境中建立JPALocalEntityManagerFactoryBean从JNDI中获取 EntityManagerFactoryLocalContainerEntityManagerFactoryBeanTomcat(5.0以上)加载时的织入配置使用VM代理的全局加载时织入上下文范围内的加载时织入配置处理多持久化单元JpaTemplate 和 JpaDaoSupport基于原生的JPA实现DAO异常转化事务管理JpaDialectThe WebWeb MVC framework Web框架概述与其他MVC实现框架的集成Spring Web MVC框架的特点DispatcherServlet控制器AbstractController 和 WebContentGenerator其它的简单控制器MultiActionController命令控制器处理器映射(handler mapping)BeanNameUrlHandlerMappingSimpleUrlHandlerMapping拦截器(HandlerInterceptor)视图与视图解析视图解析器(ViewResolver)视图解析链重定向(Rediret)到另一个视图RedirectViewredirect:前缀forward:前缀本地化解析器AcceptHeaderLocaleResolverCookieLocaleResolverSessionLocaleResolverLocaleChangeInterceptor使用主题简介如何定义主题主题解析器Spring对分段文件上传(multipart file upload)的支持介绍使用MultipartResolver在表单中处理分段文件上传使用Spring的表单标签库配置form标签input标签checkbox标签checkboxes标签radiobutton标签radiobuttons标签password标签select标签option标签options标签textarea标签hidden标签errors标签处理异常惯例优先原则(convention over configuration)对控制器的支持:ControllerClassNameHandlerMapping对模型的支持:ModelMap(ModelAndView)对视图的支持:RequestToViewNameTranslator基于注解的控制器配置建立dispatcher实现注解支持使用@Controller定义一个控制器使用@RequestMapping映射请求使用@RequestParam绑定请求参数到方法参数使用@ModelAttribute提供一个从模型到数据的链接使用@SessionAttributes指定存储在会话中的属性自定义WebDataBinder初始化使用@InitBinder自定义数据绑定配置一个定制的WebBindingInitializer更多资源集成视图技术简介JSP和JSTL视图解析器'Plain-old' JSPs versus JSTL 'Plain-old' JSP与JSTL帮助简化开发的额外的标签Tiles需要的资源如何集成TilesUrlBasedViewResolver类ResourceBundleViewResolver类SimpleSpringPreparerFactory 和 SpringBeanPreparerFactoryVelocity和FreeMarker需要的资源Context 配置创建模板高级配置velocity.propertiesFreeMarker绑定支持和表单处理用于绑定的宏简单绑定表单输入生成宏输入域选择输入域重载HTML转码行为并使你的标签符合XHTMLXSLT写在段首Bean 定义标准MVC控制器代码把模型数据转化为XML定义视图属性文档转换小结文档视图(PDF/Excel)简介配置和安装文档视图定义Controller 代码Excel视图子类PDF视图子类JasperReports依赖的资源配置配置ViewResolver配置View关于报表文件使用 JasperReportsMultiFormatView构造ModelAndView使用子报表配置子报表文件配置子报表数据源配置Exporter的参数集成其它Web框架简介通用配置JavaServer FacesDelegatingVariableResolverFacesContextUtilsStrutsContextLoaderPluginDelegatingRequestProcessorDelegatingActionProxyActionSupport ClassesTapestry注入 Spring 托管的 beans将 Spring Beans 注入到 Tapestry 页面中组件定义文件添加抽象访问方法将 Spring Beans 注入到 Tapestry 页面中 - Tapestry 4.0+ 风格WebWork更多资源Portlet MVC框架介绍控制器 - MVC中的C视图 - MVC中的VWeb作用范围的BeanDispatcherPortletViewRendererServlet控制器AbstractController 和 PortletContentGenerator其它简单的控制器Command控制器PortletWrappingController处理器映射PortletModeHandlerMappingParameterHandlerMappingPortletModeParameterHandlerMapping增加 HandlerInterceptorsHandlerInterceptorAdapterParameterMappingInterceptor视图和它们的解析Multipart文件上传支持使用 PortletMultipartResolver处理表单里的文件上传异常处理Portlet应用的部署整合使用Spring进行远程访问与Web服务简介使用RMI暴露服务使用RmiServiceExporter暴露服务在客户端链接服务使用Hessian或者Burlap通过HTTP远程调用服务为Hessian和co.配置DispatcherServlet使用HessianServiceExporter暴露你的bean在客户端连接服务使用Burlap对通过Hessian或Burlap暴露的服务使用HTTP Basic认证使用HTTP调用器暴露服务Exposing the service object在客户端连接服务Web Services使用JAX-RPC暴露基于servlet的web服务使用JAX-RPC访问web服务注册JAX-RPC Bean映射注册自己的JAX-RPC 处理器使用JAX-WS暴露基于servlet的web服务使用JAX-WS暴露单独web服务使用Spring支持的JAX-WS RI来暴露服务使用JAX-WS访问web服务使用XFire来暴露Web服务JMS服务端配置客户端配置对远程接口不提供自动探测实现在选择这些技术时的一些考虑Enterprise Java Beans (EJB) 集成简介访问EJB概念访问本地的无状态Session Bean(SLSB)访问远程SLSBAccessing EJB 2.x SLSBs versus EJB 3 SLSBs使用Spring提供的辅助类实现EJB组件EJB 2.x base classesEJB 3 注入拦截JMS (Java Message Service)简介使用Spring JMSJmsTemplate连接工厂目的地管理消息侦听容器SimpleMessageListenerContainerDefaultMessageListenerContainerServerSessionMessageListenerContainer事务管理发送消息使用消息转换器SessionCallback 和 ProducerCallback接收消息同步接收异步接收 - 消息驱动的POJOSessionAwareMessageListener接口MessageListenerAdapter事务中的消息处理JCA消息端点的支持JMS命名空间支持JMX介绍将Bean暴露为JMX创建MBeanServer重用原有的MBeanServer延迟初始化的MBeanMBean的自动注册控制注册行为控制Bean的管理接口MBeanInfoAssembler接口使用源码级元数据使用JDK 5.0的注解源代码级的元数据类型AutodetectCapableMBeanInfoAssembler接口用Java接口定义管理接口使用MethodNameBasedMBeanInfoAssembler控制Bean的ObjectName从Properties读取Properties使用MetadataNamingStrategy<context:mbean-export/>元素JSR-160连接器服务器端连接器客户端连接器基于Burlap/Hessian/SOAP的JMX通过代理访问MBean通知为通知注册监听器发布通知更多资源JCA CCI简介配置CCI连接器配置在Spring中配置ConnectionFactory配置CCI连接使用一个 CCI 单连接使用Spring的 CCI访问支持记录转换CciTemplate类DAO支持自动输出记录生成总结直接使用一个CCI Connection接口和Interaction接口CciTemplate 使用示例建模CCI访问为操作对象MappingRecordOperationMappingCommAreaOperation自动生成输出记录总结MappingRecordOperation 使用示例MappingCommAreaOperation 使用示例事务Spring邮件抽象层简介使用Spring邮件抽象MailSender 和 SimpleMailMessage 的基本用法使用 JavaMailSender 和 MimeMessagePreparator使用MimeMessageHelper发送附件和嵌入式资源(inline resources)附件内嵌资源使用模板来创建邮件内容一个基于Velocity的示例Spring中的定时调度(Scheduling)和线程池(Thread Pooling)简介使用OpenSymphony Quartz 调度器使用JobDetailBean使用 MethodInvokingJobDetailFactoryBean使用triggers和SchedulerFactoryBean来包装任务使用JDK Timer支持类创建定制的timers使用 MethodInvokingTimerTaskFactoryBean类最后:使用TimerFactoryBean来设置任务SpringTaskExecutor抽象TaskExecutor接口TaskExecutor类型使用TaskExecutor动态语言支持介绍第一个示例定义动态语言支持的bean公共概念<lang:language/> 元素Refreshable bean内置动态语言源文件理解dynamic-language-backed bean上下文中的构造器注入JRuby beansGroovy beans通过回调定制Groovy对象BeanShell beans场景Spring MVC控制器的脚本化Validator的脚本化Bits and bobsAOP - 通知脚本化bean作用域更多的资源注解和源代码级的元数据支持简介Spring的元数据支持注解@RequiredSpring中的其它@AnnotationsJakarta Commons Attributes集成元数据和Spring AOP自动代理基本原理声明式事务管理示例程序演示案例介绍使用动态语言实现的Spring MVC控制器构建与部署使用SimpleJdbcTemplate和@Repository实现DAO域对象Data Access Object构建XML Schema-based configurationIntroductionXML Schema-based configurationReferencing the schemasThe util schema<util:constant/>Setting a bean property or constructor arg from a field value<util:property-path/>Using <util:property-path/> to set a bean property or constructor-argument<util:properties/><util:list/><util:map/><util:set/>The jee schema<jee:jndi-lookup/> (simple)<jee:jndi-lookup/> (with single JNDI environment setting)<jee:jndi-lookup/> (with multiple JNDI environment settings)<jee:jndi-lookup/> (complex)<jee:local-slsb/> (simple)<jee:local-slsb/> (complex)<jee:remote-slsb/>The lang schemaThe jms schemaThe tx (transaction) schemaThe aop schemaThe context schema<property-placeholder/><annotation-config/><component-scan/><load-time-weaver/><spring-configured/><mbean-export/>The tool schemaThe beans schemaSetting up your IDESetting up EclipseSetting up IntelliJ IDEAIntegration issuesXML parsing errors in the Resin v.3 application serverExtensible XML authoringIntroductionAuthoring the schemaCoding a NamespaceHandlerCoding a BeanDefinitionParserRegistering the handler and the schema'META-INF/spring.handlers''META-INF/spring.schemas'Using a custom extension in your Spring XML configurationMeatier examplesNesting custom tags within custom tagsCustom attributes on 'normal' elementsFurther Resourcesspring-beans-2.0.dtdspring.tldIntroductionThe bind tagThe escapeBody tagThe hasBindErrors tagThe htmlEscape tagThe message tagThe nestedPath tagThe theme tagThe transform tagspring-form.tldIntroductionThe checkbox tagThe checkboxes tagThe errors tagThe form tagThe hidden tagThe input tagThe label tagThe option tagThe options tagThe password tagThe radiobutton tagThe radiobuttons tagThe select tagThe textarea tagSpring 2.5开发手册中文化项目声明致谢参与人员项目历程
文字

6.3. 基于Schema的AOP支持

  

如果你无法使用Java 5,或者你比较喜欢使用XML格式,Spring2.0也提供了使用新的"aop"命名空间来定义一个切面。 和使用@AspectJ风格完全一样,切入点表达式和通知类型同样得到了支持,因此在这一节中我们将着重介绍新的 语法并回顾前一节(第 6.2 节 “@AspectJ支持”)对编写一个切入点表达式和绑定通知参数的讨论。

  

使用本章所介绍的aop命名空间标签,你需要引入附录 A, XML Schema-based configuration 中提及的spring-aop schema。 参见第 A.2.7 节 “The aop schema”了解如何在aop命名空间中引入标签。

  

在Spring的配置文件中,所有的切面和通知都必须定义在<aop:config>元素内部。 (一个application context可以包含多个 <aop:config>)。 一个<aop:config>可以包含pointcut,advisor和aspect元素 (注意这三个元素必须按照这个顺序进行声明)。

警告

<aop:config>风格的配置使得Spring auto-proxying机制的使用变得很笨重。如果你已经通过 BeanNameAutoProxyCreator或类似的东西显式使用auto-proxying,它可能会导致问题 (例如通知没有被织入)。 推荐的使用模式是仅仅使用<aop:config>风格, 或者仅仅使用AutoProxyCreator风格。

6.3.1. 声明一个切面

   

有了schema的支持,切面就和常规的Java对象一样被定义成application context中的一个bean。   对象的字段和方法提供了状态和行为信息,XML文件则提供了切入点和通知信息。

 

切面使用<aop:aspect>来声明,backing bean(支持bean)通过 ref 属性来引用:

<aop:config>
  <aop:aspect id="myAspect" ref="aBean">
    ...
  </aop:aspect>
</aop:config>

<bean id="aBean" class="...">
  ...
</bean>

切面的支持bean(上例中的"aBean")可以象其他Spring bean一样被容器管理配置以及依赖注入。

6.3.2. 声明一个切入点

   

一个命名切入点可以在<aop:config>元素中定义,这样多个切面和通知就可以共享该切入点。

 

一个描述service层中所有service执行的切入点可以定义如下:

<aop:config>

  <aop:pointcut id="businessService" 
        expression="execution(* com.xyz.myapp.service.*.*(..))"/>

</aop:config>
   

注意切入点表达式本身使用了与第 6.2 节 “@AspectJ支持”中描述的相同的AspectJ切入点表达式语言。   如果你在Java 5环境下使用基于schema的声明风格,可参考切入点表达式类型(@Aspects)中定义的命名切入点,   不过这个特性在JDK1.4及以下版本中是不可用的(因为依赖于Java 5中的AspectJ反射API)。所以在JDK 1.5中,   上面的切入点的另外一种定义形式如下:  

<aop:config>

  <aop:pointcut id="businessService" 
        expression="com.xyz.myapp.SystemArchitecture.businessService()"/>

</aop:config>

假定你有一个在第 6.2.3.3 节 “共享通用切入点定义”中 描述的SystemArchitecture切面。

在切面里面声明一个切入点和声明一个顶级的切入点非常类似:

<aop:config>

  <aop:aspect id="myAspect" ref="aBean">

    <aop:pointcut id="businessService" 
          expression="execution(* com.xyz.myapp.service.*.*(..))"/>
          
    ...
    
  </aop:aspect>

</aop:config>

几乎和@AspectJ切面中的一样,使用基于schema定义风格声明的切入点可以收集(collect) 连接点上下文。例如,下面的切入点收集'this'对象作为连接点上下文并传递它给通知:

<aop:config>

  <aop:aspect id="myAspect" ref="aBean">

    <aop:pointcut id="businessService" 
          expression="execution(* com.xyz.myapp.service.*.*(..)) &amp;&amp; this(service)"/>
    <aop:before pointcut-ref="businessService" method="monitor"/>
    ...
    
  </aop:aspect>

</aop:config>
   

通过包含匹配名字的参数,通知被声明来接收收集的连接点上下文:

public void monitor(Object service) {
    ...
}
   

当需要连接子表达式的时候,'&&'在XML中用起来非常不方便,所以关键字'and', 'or' 和 'not'可以分别用来代替'&&', '||' 和 '!'。例如,上面切入点更好的写法如下:

<aop:config>

  <aop:aspect id="myAspect" ref="aBean">

    <aop:pointcut id="businessService" 
          expression="execution(* com.xyz.myapp.service.*.*(..)) and this(service)"/>
    <aop:before pointcut-ref="businessService" method="monitor"/>
    ...
    
  </aop:aspect>

</aop:config>

注意这种方式定义的切入点通过XML id来查找,并且不能定义切入点参数。在基于schema的定义风格中 命名切入点支持较之@AspectJ风格受到了很多的限制。

6.3.3. 声明通知

 

和@AspectJ风格一样,基于schema的风格也支持5种通知类型并且两者具有同样的语义。

6.3.3.1. 前置通知

    

前置通知在匹配方法执行前运行。在<aop:aspect>中使用<aop:before> 元素来声明它。

<aop:aspect id="beforeExample" ref="aBean">

    <aop:before 
      pointcut-ref="dataAccessOperation" 
      method="doAccessCheck"/>
          
    ...
    
</aop:aspect>
    

这里dataAccessOperation是一个顶级(<aop:config>)切入点的id。     而要定义内置切入点,需将pointcut-ref属性替换为pointcut属性:

<aop:aspect id="beforeExample" ref="aBean">

    <aop:before 
      pointcut="execution(* com.xyz.myapp.dao.*.*(..))" 
      method="doAccessCheck"/>
          
    ...
    
</aop:aspect>
    

正如我们在@AspectJ风格章节中讨论过的,使用命名切入点能够明显的提高代码的可读性。

Method属性标识了提供通知主体的方法(doAccessCheck)。 这个方法必须定义在包含通知的切面元素所引用的bean中。在一个数据访问操作执行之前 (一个方法执行由切入点表达式所匹配的连接点),切面中的"doAccessCheck"会被调用。

6.3.3.2. 后置通知

后置通知在匹配的方法完全执行后运行。和前置通知一样,可以在<aop:aspect> 里面声明它。例如:

<aop:aspect id="afterReturningExample" ref="aBean">

    <aop:after-returning 
      pointcut-ref="dataAccessOperation" 
      method="doAccessCheck"/>
          
    ...
    
</aop:aspect>

和@AspectJ风格一样,通知主体可以得到返回值。使用returning属性来指定传递返回值的参数名:

<aop:aspect id="afterReturningExample" ref="aBean">

    <aop:after-returning 
      pointcut-ref="dataAccessOperation"
      returning="retVal" 
      method="doAccessCheck"/>
          
    ...
    
</aop:aspect>

doAccessCheck方法必须声明一个名字叫 retVal 的参数。 参数的类型依照@AfterReturning所描述的方法强制匹配。例如,方法签名可以这样声明:

public void doAccessCheck(Object retVal) {...

6.3.3.3. 异常通知

异常通知在匹配方法抛出异常退出时执行。在<aop:aspect>中使用 after-throwing元素来声明:

<aop:aspect id="afterThrowingExample" ref="aBean">

    <aop:after-throwing
      pointcut-ref="dataAccessOperation" 
      method="doRecoveryActions"/>
          
    ...
    
</aop:aspect>

和@AspectJ风格一样,通知主体可以得到抛出的异常。使用throwing属性来指定传递异常的参数名:

<aop:aspect id="afterThrowingExample" ref="aBean">

    <aop:after-throwing 
      pointcut-ref="dataAccessOperation"
      throwing="dataAccessEx" 
      method="doRecoveryActions"/>
          
    ...
    
</aop:aspect>

doRecoveryActions方法必须声明一个名字为dataAccessEx的参数。 参数的类型依照@AfterThrowing所描述的方法强制匹配。例如:方法签名可以如下这般声明:

public void doRecoveryActions(DataAccessException dataAccessEx) {...

6.3.3.4. 最终通知

最终通知无论如何都会在匹配方法退出后执行。使用after元素来声明它:

<aop:aspect id="afterFinallyExample" ref="aBean">

    <aop:after
      pointcut-ref="dataAccessOperation" 
      method="doReleaseLock"/>
          
    ...
    
</aop:aspect>

6.3.3.5. 环绕通知

环绕通知是最后一种通知类型。环绕通知在匹配方法运行期的“周围”执行。 它有机会在目标方法的前面和后面执行,并决定什么时候运行,怎么运行,甚至是否运行。 环绕通知经常在需要在一个方法执行前后共享状态信息,并且是在线程安全的情况下使用 (启动和停止一个计时器就是一个例子)。注意选择能满足你需求的最简单的通知类型; 如果简单的前置通知能做的事情就绝对不要使用环绕通知。

Around通知使用aop:around元素来声明。通知方法的第一个参数的类型必须是 ProceedingJoinPoint类型。在通知的主体中,调用 ProceedingJoinPointproceed()方法来执行真正的方法。 proceed方法也可能会被调用并且传入一个Object[]对象 - 该数组将作为方法执行时候的参数。参见第 6.2.4.5 节 “环绕通知”中调用具有 Object[]的proceed方法。

<aop:aspect id="aroundExample" ref="aBean">

    <aop:around
      pointcut-ref="businessService" 
      method="doBasicProfiling"/>
          
    ...
    
</aop:aspect>

doBasicProfiling通知的实现和@AspectJ中的例子完全一样(当然要去掉注解):

public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    // start stopwatch
    Object retVal = pjp.proceed();
    // stop stopwatch
    return retVal;
}

6.3.3.6. 通知参数

Schema-based声明风格和@AspectJ一样,支持多种类型的通知:通过通知方法参数名字来匹配切入点参数。 参见第 6.2.4.6 节 “通知参数(Advice parameters)”获取详细信息。如果你希望显式指定通知方法的参数名 (而不是依靠先前提及的侦测策略),可以通过通知元素的arg-names属性来实现,它的处理和 在第 6.2.4.6.3 节 “确定参数名”中所描述的对通知注解中"argNames"属性的处理方式一样。 示例如下:

<aop:before
  pointcut="com.xyz.lib.Pointcuts.anyPublicMethod() and @annotation(auditable)"
  method="audit"
  arg-names="auditable"/>

arg-names属性接受由逗号分割的参数名列表。

下面是个稍微复杂的基于XSD的例子,它展示了关联了多个强类型参数的环绕通知的使用。

package x.y.service;

public interface FooService {

   Foo getFoo(String fooName, int age);
}

public class DefaultFooService implements FooService {

   public Foo getFoo(String name, int age) {
      return new Foo(name, age);
   }
}

接下来看切面。注意profile(..)方法接受多个强类型参数, 首先连接点在方法调用时执行,这个参数指明profile(..)会被用作 环绕通知:

package x.y;

import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;

public class SimpleProfiler {

   public Object profile(ProceedingJoinPoint call, String name, int age) throws Throwable {
      StopWatch clock = new StopWatch(
            "Profiling for '" + name + "' and '" + age + "'");
      try {
         clock.start(call.toShortString());
         return call.proceed();
      } finally {
         clock.stop();
         System.out.println(clock.prettyPrint());
      }
   }
}

最后这里是使得上面的通知针对一个特定连接点而执行所必需的XML配置:

<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"
      xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

   <!-- this is the object that will be proxied by Spring's AOP infrastructure -->
   <bean id="fooService" class="x.y.service.DefaultFooService"/>

   <!-- this is the actual advice itself -->
   <bean id="profiler" class="x.y.SimpleProfiler"/>

   <aop:config>
      <aop:aspect ref="profiler">

         <aop:pointcut id="theExecutionOfSomeFooServiceMethod"
                    expression="execution(* x.y.service.FooService.getFoo(String,int))
                    and args(name, age)"/>

         <aop:around pointcut-ref="theExecutionOfSomeFooServiceMethod"
                  method="profile"/>

      </aop:aspect>
   </aop:config>

</beans>

如果我们有下面的驱动脚本,我们将在标准输出上得到如下的输出:

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import x.y.service.FooService;

public final class Boot {

   public static void main(final String[] args) throws Exception {
      BeanFactory ctx = new ClassPathXmlApplicationContext("x/y/plain.xml");
      FooService foo = (FooService) ctx.getBean("fooService");
      foo.getFoo("Pengo", 12);
   }
}
StopWatch 'Profiling for 'Pengo' and '12'': running time (millis) = 0
-----------------------------------------
ms     %     Task name
-----------------------------------------
00000  ?  execution(getFoo)

6.3.3.7. 通知顺序

当同一个切入点(执行方法)上有多个通知需要执行时,执行顺序的规则如 第 6.2.4.7 节 “通知顺序”所述。切面的优先级通过给切面的支持bean增加 Order注解或者使切面的支持bean实现 Ordered接口来决定。   

6.3.4. 引入

引入(在AspectJ中称为inter-type声明)允许一个切面声明一个通知对象实现指定接口, 并且提供了一个接口实现类来代表这些对象。

引入的定义使用aop:aspect中的aop:declare-parents元素。 该元素用于声明所匹配的类型有一个新的父类型(所以有了这个名字)。 例如,给定接口UsageTracked, 以及这个接口的一个实现类 DefaultUsageTracked, 下面的切面声明所有实现service接口的类同时实现 UsageTracked 接口。(比如为了通过JMX输出统计信息)

<aop:aspect id="usageTrackerAspect" ref="usageTracking">

  <aop:declare-parents
      types-matching="com.xzy.myapp.service.*+"
      implement-interface="com.xyz.myapp.service.tracking.UsageTracked"
      default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked"/>
  
  <aop:before
    pointcut="com.xyz.myapp.SystemArchitecture.businessService()
              and this(usageTracked)"
    method="recordUsage"/>
  
</aop:aspect>

usageTracking bean的支持类可以包含下面的方法:

public void recordUsage(UsageTracked usageTracked) {
    usageTracked.incrementUseCount();
}

要实现的接口由implement-interface属性来指定。  types-matching属性的值是一个AspectJ类型模式:任何匹配类型的bean都会实现  UsageTracked 接口。注意在上面前置通知的例子中, serevice bean可以直接用作UsageTracked接口的实现。 如果以编程形式访问一个bean,你可以这样来写:

UsageTracked usageTracked = (UsageTracked) context.getBean("myService");

6.3.5. 切面实例化模型

采用Schema风格来定义切面仅支持一种实例化模型就是singlton模型。其他的实例化模型或许以后的版本会支持。

6.3.6. Advisor

"advisor"这个概念来自Spring1.2对AOP的支持,而在AspectJ中没有等价的概念。 advisor就像一个小的自包含的切面,这个切面只有一个通知。切面自身通过一个bean表示, 并且必须实现一个在第 7.3.2 节 “Spring里的通知类型”中描述的通知接口。 Advisor可以很好的利用AspectJ的切入点表达式。  

Spring 2.0通过<aop:advisor>元素来支持advisor概念。 你将会发现大多数情况下它会和transactional advice一起使用,在Spring 2.0中它有自己的命名空间。其格式如下:  

<aop:config>

  <aop:pointcut id="businessService"
        expression="execution(* com.xyz.myapp.service.*.*(..))"/>

  <aop:advisor 
      pointcut-ref="businessService"
      advice-ref="tx-advice"/>
      
</aop:config>

<tx:advice id="tx-advice">
  <tx:attributes>
    <tx:method name="*" propagation="REQUIRED"/>
  </tx:attributes>
</tx:advice>

和上面所使用的pointcut-ref属性一样,你还可以使用pointcut 属性来定义一个内联的切入点表达式。

为了定义一个advisor的优先级以便让通知具有次序,使用order属性来定义advisor中 Ordered的值 。

6.3.7. 例子

让我们看看第 6.2.7 节 “例子”中并发锁失败重试的例子, 当使用schema重写它时是什么样子。

因为并发的问题,有时候business services可能会失败(例如,死锁失败)。如果重试操作,下一次很可能就会成功。   对于business services来说,这种情况下重试是很正常的(Idempotent操作不需要用户参与,否则会得出矛盾的结论)    我们可能需要透明的重试操作以避免客户看到一个OptimisticLockingFailureException   异常。很明显,在一个横切多层的情况下,这是非常有必要的,因此通过切面来实现是很理想的。  

因为想要重试操作,我们需要使用环绕通知,这样就可以多次调用proceed()方法。 下面是简单的切面实现(只是一个schema支持的普通Java 类):

public class ConcurrentOperationExecutor implements Ordered {
   
   private static final int DEFAULT_MAX_RETRIES = 2;

   private int maxRetries = DEFAULT_MAX_RETRIES;
   private int order = 1;

   public void setMaxRetries(int maxRetries) {
      this.maxRetries = maxRetries;
   }
   
   public int getOrder() {
      return this.order;
   }
   
   public void setOrder(int order) {
      this.order = order;
   }
   
   public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { 
      int numAttempts = 0;
      PessimisticLockingFailureException lockFailureException;
      do {
         numAttempts++;
         try { 
            return pjp.proceed();
         }
         catch(PessimisticLockingFailureException ex) {
            lockFailureException = ex;
         }
      }
      while(numAttempts <= this.maxRetries);
      throw lockFailureException;
   }

}

请注意切面实现了Ordered接口,这样我们就可以把切面的优先级设定为 高于事务通知(我们每次重试的时候都想要在一个全新的事务中进行)。 maxRetriesorder 属性都可以在Spring中配置。 主要的动作在doConcurrentOperation 这个环绕通知方法中发生。 我们首先会尝试处理,如果得到一个OptimisticLockingFailureException 异常,我们仅仅重试直到耗尽所有预设的重试次数。

这个类跟我们在@AspectJ的例子中使用的是相同的,只是没有使用注解。

 

对应的Spring配置如下:

<aop:config>

  <aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor">

    <aop:pointcut id="idempotentOperation"
        expression="execution(* com.xyz.myapp.service.*.*(..))"/>
       
    <aop:around
       pointcut-ref="idempotentOperation"
       method="doConcurrentOperation"/>
  
  </aop:aspect>

</aop:config>

<bean id="concurrentOperationExecutor"
  class="com.xyz.myapp.service.impl.ConcurrentOperationExecutor">
     <property name="maxRetries" value="3"/>
     <property name="order" value="100"/>  
</bean>

请注意我们现在假设所有的bussiness services都是idempotent。如果不是这样,我们可以改写切面, 通过引入一个Idempotent注解,让它只调用idempotent:

@Retention(RetentionPolicy.RUNTIME)
public @interface Idempotent {
  // marker annotation
}

并且对service操作的实现进行注解。这时如果你只希望改变切面重试idempotent操作, 你只需要改写切入点表达式,让其只匹配@Idempotent操作:

  <aop:pointcut id="idempotentOperation"
        expression="execution(* com.xyz.myapp.service.*.*(..)) and
                    @annotation(com.xyz.myapp.service.Idempotent)"/>
上一篇:下一篇: