国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

目錄 搜尋
前言 簡(jiǎn)介 概覽 使用場(chǎng)景 Spring 2.0和 2.5的新特性 簡(jiǎn)介 控制反轉(zhuǎn)(IoC)容器 新的bean作用域 更簡(jiǎn)單的XML配置 可擴(kuò)展的XML編寫(xiě) Annotation(注解)驅(qū)動(dòng)配置 在classpath中自動(dòng)搜索組件 面向切面編程(AOP) 更加簡(jiǎn)單的AOP XML配置 對(duì)@AspectJ 切面的支持 對(duì)bean命名pointcut( bean name pointcut element)的支持 對(duì)AspectJ裝載時(shí)織入(AspectJ load-time weaving)的支持 中間層 在XML里更為簡(jiǎn)單的聲明性事務(wù)配置 對(duì)Websphere 事務(wù)管理的完整支持 JPA 異步的JMS JDBC Web層 Spring MVC合理的默認(rèn)值 Portlet 框架 基于Annotation的控制器 Spring MVC的表單標(biāo)簽庫(kù) 對(duì)Tiles 2 支持 對(duì)JSF 1.2支持 JAX-WS支持 其他 動(dòng)態(tài)語(yǔ)言支持 增強(qiáng)的測(cè)試支持 JMX 支持 將Spring 應(yīng)用程序上下文部署為JCA adapter 計(jì)劃任務(wù) 對(duì)Java 5 (Tiger) 支持 移植到Spring 2.5 改變 支持的JDK版本 Spring 2.5的Jar打包 XML配置 Deprecated(淘汰)的類(lèi)和方法 Apache OJB iBATIS Hibernate JDO UrlFilenameViewController 更新的樣例應(yīng)用 改進(jìn)的文檔 核心技術(shù) IoC(控制反轉(zhuǎn))容器 簡(jiǎn)介 基本原理 - 容器和bean 容器 配置元數(shù)據(jù) 實(shí)例化容器 XML配置元數(shù)據(jù)的結(jié)構(gòu) 多種bean bean的命名 bean的別名 實(shí)例化bean 用構(gòu)造器來(lái)實(shí)例化 使用靜態(tài)工廠方法實(shí)例化 使用實(shí)例工廠方法實(shí)例化 使用容器 依賴(lài) 注入依賴(lài) 構(gòu)造器注入 構(gòu)造器參數(shù)解析 構(gòu)造器參數(shù)類(lèi)型匹配 構(gòu)造參數(shù)索引 Setter注入 一些例子 依賴(lài)配置詳解 直接變量(基本類(lèi)型、Strings類(lèi)型等。) idref元素 引用其它的bean(協(xié)作者) 內(nèi)部bean 集合 集合的合并 強(qiáng)類(lèi)型集合(僅適用于Java5+) Nulls XML配置文件的簡(jiǎn)寫(xiě)及其他 XML-based configuration metadata shortcuts 使用p名稱(chēng)空間配置屬性 組合屬性名稱(chēng) 使用depends-on 延遲初始化bean 自動(dòng)裝配(autowire)協(xié)作者 將bean排除在自動(dòng)裝配之外 依賴(lài)檢查 方法注入 Lookup方法注入 自定義方法的替代方案 Bean的作用域 Singleton作用域 Prototype作用域 Singleton beans和prototype-bean的依賴(lài) 其他作用域 初始化web配置 Request作用域 Session作用域 global session作用域 作用域bean與依賴(lài) 選擇創(chuàng)建代理的類(lèi)型 自定義作用域 創(chuàng)建自定義作用域 使用自定義作用域 定制bean特性 生命周期回調(diào) 初始化回調(diào) 析構(gòu)回調(diào) 缺省的初始化和析構(gòu)方法 組合生命周期機(jī)制 在非web應(yīng)用中優(yōu)雅地關(guān)閉Spring IoC容器 了解自己 BeanFactoryAware BeanNameAware bean定義的繼承 容器擴(kuò)展點(diǎn) 用BeanPostProcessor定制bean 使用BeanPostProcessor的Hello World示例 RequiredAnnotationBeanPostProcessor示例 用BeanFactoryPostProcessor定制配置元數(shù)據(jù) PropertyPlaceholderConfigurer示例 PropertyOverrideConfigurer示例 使用FactoryBean定制實(shí)例化邏輯 The ApplicationContext BeanFactory 還是 ApplicationContext? 利用MessageSource實(shí)現(xiàn)國(guó)際化 事件 底層資源的訪問(wèn) ApplicationContext在WEB應(yīng)用中的實(shí)例化 粘合代碼和可怕的singleton 以J2EE RAR文件的形式部署Spring ApplicationContext 基于注解(Annotation-based)的配置 @Autowired 基于注解的自動(dòng)連接微調(diào) CustomAutowireConfigurer @Resource @PostConstruct 與 @PreDestroy 對(duì)受管組件的Classpath掃描 @Component和更多典型化注解 自動(dòng)檢測(cè)組件 使用過(guò)濾器自定義掃描 自動(dòng)檢測(cè)組件的命名 為自動(dòng)檢測(cè)的組件提供一個(gè)作用域 用注解提供限定符元數(shù)據(jù) 注冊(cè)一個(gè)LoadTimeWeaver 資源 簡(jiǎn)介 Resource接口 內(nèi)置 Resource 實(shí)現(xiàn) UrlResource ClassPathResource FileSystemResource ServletContextResource InputStreamResource ByteArrayResource ResourceLoader接口 ResourceLoaderAware 接口 把Resource作為屬性來(lái)配置 Application context 和Resource 路徑 構(gòu)造application context 創(chuàng)建 ClassPathXmlApplicationContext 實(shí)例 - 簡(jiǎn)介 Application context構(gòu)造器中資源路徑的通配符 Ant風(fēng)格的pattern 潛在的可移植性 classpath*: 前綴 其他關(guān)于通配符的說(shuō)明 FileSystemResource 說(shuō)明 校驗(yàn),數(shù)據(jù)綁定,BeanWrapper,與屬性編輯器 簡(jiǎn)介 使用Spring的Validator接口進(jìn)行校驗(yàn) 從錯(cuò)誤代碼到錯(cuò)誤信息 Bean處理和BeanWrapper 設(shè)置和獲取屬性值以及嵌套屬性 內(nèi)建的PropertyEditor實(shí)現(xiàn) 注冊(cè)用戶(hù)自定義的PropertyEditor 使用PropertyEditorRegistrars 使用Spring進(jìn)行面向切面編程(AOP) 簡(jiǎn)介 AOP概念 Spring AOP的功能和目標(biāo) AOP代理 @AspectJ支持 啟用@AspectJ支持 聲明一個(gè)切面 聲明一個(gè)切入點(diǎn)(pointcut) 切入點(diǎn)指示符(PCD)的支持 組合切入點(diǎn)表達(dá)式 共享通用切入點(diǎn)定義 示例 聲明通知 前置通知 后置通知(After returning advice) 異常通知(After throwing advice) 最終通知(After (finally) advice) 環(huán)繞通知 通知參數(shù)(Advice parameters) 訪問(wèn)當(dāng)前的連接點(diǎn) 傳遞參數(shù)給通知 確定參數(shù)名 處理參數(shù) 通知順序 引入(Introduction) 切面實(shí)例化模型 例子 基于Schema的AOP支持 聲明一個(gè)切面 聲明一個(gè)切入點(diǎn) 聲明通知 前置通知 后置通知 異常通知 最終通知 環(huán)繞通知 通知參數(shù) 通知順序 引入 切面實(shí)例化模型 Advisor 例子 AOP聲明風(fēng)格的選擇 Spring AOP還是完全用AspectJ? Spring AOP中使用@AspectJ還是XML? 混合切面類(lèi)型 代理機(jī)制 理解AOP代理 以編程方式創(chuàng)建@AspectJ代理 在Spring應(yīng)用中使用AspectJ 在Spring中使用AspectJ進(jìn)行domain object的依賴(lài)注入 @Configurable對(duì)象的單元測(cè)試 Working with multiple application contexts Spring中其他的AspectJ切面 使用Spring IoC來(lái)配置AspectJ的切面 在Spring應(yīng)用中使用AspectJ加載時(shí)織入(LTW) 第一個(gè)例子 切面 'META-INF/aop.xml' 相關(guān)類(lèi)庫(kù)(JARS) Spring配置 特定環(huán)境的配置 通用Java應(yīng)用 Tomcat WebLogic 更多資源 Spring AOP APIs 簡(jiǎn)介 Spring中的切入點(diǎn)API 概念 切入點(diǎn)運(yùn)算 AspectJ切入點(diǎn)表達(dá)式 便利的切入點(diǎn)實(shí)現(xiàn) 靜態(tài)切入點(diǎn) 正則表達(dá)式切入點(diǎn) 屬性驅(qū)動(dòng)的切入點(diǎn) 動(dòng)態(tài)切入點(diǎn) 控制流切入點(diǎn) 切入點(diǎn)的超類(lèi) 自定義切入點(diǎn) Spring的通知API 通知的生命周期 Spring里的通知類(lèi)型 攔截環(huán)繞通知 前置通知 異常通知 后置通知 引入通知 Spring里的Advisor API 使用ProxyFactoryBean創(chuàng)建AOP代理 基礎(chǔ) JavaBean屬性 基于JDK和CGLIB的代理 對(duì)接口進(jìn)行代理 對(duì)類(lèi)進(jìn)行代理 使用“全局”通知器 簡(jiǎn)化代理定義 使用ProxyFactory通過(guò)編程創(chuàng)建AOP代理 操作被通知對(duì)象 使用“自動(dòng)代理(autoproxy)”功能 自動(dòng)代理bean定義 BeanNameAutoProxyCreator DefaultAdvisorAutoProxyCreator AbstractAdvisorAutoProxyCreator 使用元數(shù)據(jù)驅(qū)動(dòng)的自動(dòng)代理 使用TargetSource 熱交換目標(biāo)源 池化目標(biāo)源 原型目標(biāo)源 ThreadLocal目標(biāo)源 定義新的Advice類(lèi)型 更多資源 測(cè)試 簡(jiǎn)介 單元測(cè)試 Mock對(duì)象 JNDI Servlet API Portlet API 單元測(cè)試支持類(lèi) 通用工具類(lèi) Spring MVC 集成測(cè)試 概覽 使用哪個(gè)支持框架 通用目標(biāo) 上下文管理及緩存 測(cè)試fixtures依賴(lài)注入 事務(wù)管理 集成測(cè)試支持類(lèi) JDBC測(cè)試支持 常用注解 JUnit 3.8遺留支持 上下文管理及緩存 測(cè)試fixture依賴(lài)注入 字段級(jí)別(Field Level)注入 事務(wù)管理 JUnit 3.8 遺留支持類(lèi) Java 5+ 專(zhuān)有支持 使用注解的事務(wù)相關(guān)測(cè)試 JPA支持類(lèi) Spring TestContext Framework 主要的抽象 上下文管理和緩存 測(cè)試fixture的依賴(lài)注入 事務(wù)管理 TestContext支持類(lèi) JUnit 3.8支持類(lèi) JUnit 4.4支持類(lèi) 定制JUnit 4.4運(yùn)行器 TestNG支持類(lèi) TestContext框架注解支持 PetClinic示例 更多資源 中間層數(shù)據(jù)訪問(wèn) 事務(wù)管理 簡(jiǎn)介 動(dòng)機(jī) 關(guān)鍵抽象 使用資源同步的事務(wù) 高層次方案 低層次方案 TransactionAwareDataSourceProxy 聲明式事務(wù)管理 理解Spring的聲明式事務(wù)管理實(shí)現(xiàn) 第一個(gè)例子 回滾 為不同的bean配置不同的事務(wù)語(yǔ)義 <tx:advice/> 有關(guān)的設(shè)置 使用 @Transactional @Transactional 有關(guān)的設(shè)置 事務(wù)傳播 required RequiresNew Nested 通知事務(wù)操作 結(jié)合AspectJ使用 @Transactional 編程式事務(wù)管理 使用TransactionTemplate 指定事務(wù)設(shè)置 使用PlatformTransactionManager 選擇編程式事務(wù)管理還是聲明式事務(wù)管理 與特定應(yīng)用服務(wù)器集成 IBM WebSphere BEA WebLogic Oracle OC4J 常見(jiàn)問(wèn)題的解決方法 對(duì)一個(gè)特定的 DataSource 使用了錯(cuò)誤的事務(wù)管理器 更多的資源 DAO支持 簡(jiǎn)介 一致的異常層次 一致的DAO支持抽象類(lèi) 使用JDBC進(jìn)行數(shù)據(jù)訪問(wèn) 簡(jiǎn)介 選擇一種工作模式 Spring JDBC包結(jié)構(gòu) 利用JDBC核心類(lèi)控制JDBC的基本操作和錯(cuò)誤處理 JdbcTemplate類(lèi) 一些示例 查詢(xún)(SELECT) 更新(INSERT/UPDATE/DELETE) 其他操作 JdbcTemplate 的最佳實(shí)踐 NamedParameterJdbcTemplate類(lèi) SimpleJdbcTemplate類(lèi) DataSource接口 SQLExceptionTranslator接口 執(zhí)行SQL語(yǔ)句 執(zhí)行查詢(xún) 更新數(shù)據(jù)庫(kù) 獲取自動(dòng)生成的主鍵 控制數(shù)據(jù)庫(kù)連接 DataSourceUtils類(lèi) SmartDataSource接口 AbstractDataSource類(lèi) SingleConnectionDataSource類(lèi) DriverManagerDataSource類(lèi) TransactionAwareDataSourceProxy類(lèi) DataSourceTransactionManager類(lèi) NativeJdbcExtractor JDBC批量操作 使用JdbcTemplate進(jìn)行批量操作 使用SimpleJdbcTemplate進(jìn)行批量操作 通過(guò)使用SimpleJdbc類(lèi)簡(jiǎn)化JDBC操作 使用SimpleJdbcInsert插入數(shù)據(jù) 使用SimpleJdbcInsert來(lái)獲取自動(dòng)生成的主鍵 指定SimpleJdbcInsert所使用的字段 使用SqlParameterSource提供參數(shù)值 使用SimpleJdbcCall調(diào)用存儲(chǔ)過(guò)程 聲明SimpleJdbcCall使用的參數(shù) 如何定義SqlParameters 使用SimpleJdbcCall調(diào)用內(nèi)置函數(shù) 使用SimpleJdbcCall返回的ResultSet/REF Cursor 用Java對(duì)象來(lái)表達(dá)JDBC操作 SqlQuery類(lèi) MappingSqlQuery類(lèi) SqlUpdate類(lèi) StoredProcedure類(lèi) SqlFunction類(lèi) 參數(shù)和數(shù)據(jù)處理的基本原則 為參數(shù)設(shè)置SQL類(lèi)型信息 處理BLOB 和 CLOB對(duì)象 在IN語(yǔ)句中傳入一組參數(shù)值 處理復(fù)雜類(lèi)型的存儲(chǔ)過(guò)程調(diào)用 使用ORM工具進(jìn)行數(shù)據(jù)訪問(wèn) 簡(jiǎn)介 Hibernate 資源管理 在Spring容器中創(chuàng)建 SessionFactory The HibernateTemplate 不使用回調(diào)的基于Spring的DAO實(shí)現(xiàn) 基于Hibernate3的原生API實(shí)現(xiàn)DAO 編程式的事務(wù)劃分 聲明式的事務(wù)劃分 事務(wù)管理策略 容器資源 vs 本地資源 在應(yīng)用服務(wù)器中使用Hibernate的注意事項(xiàng) JDO 建立PersistenceManagerFactory JdoTemplate和JdoDaoSupport 基于原生的JDO API實(shí)現(xiàn)DAO 事務(wù)管理 JdoDialect Oracle TopLink SessionFactory 抽象層 TopLinkTemplate and TopLinkDaoSupport 基于原生的TopLink API的DAO實(shí)現(xiàn) 事務(wù)管理 iBATIS SQL Maps 創(chuàng)建SqlMapClient 使用 SqlMapClientTemplate 和 SqlMapClientDaoSupport 基于原生的iBATIS API的DAO實(shí)現(xiàn) JPA 在Spring環(huán)境中建立JPA LocalEntityManagerFactoryBean 從JNDI中獲取 EntityManagerFactory LocalContainerEntityManagerFactoryBean Tomcat(5.0以上)加載時(shí)的織入配置 使用VM代理的全局加載時(shí)織入 上下文范圍內(nèi)的加載時(shí)織入配置 處理多持久化單元 JpaTemplate 和 JpaDaoSupport 基于原生的JPA實(shí)現(xiàn)DAO 異常轉(zhuǎn)化 事務(wù)管理 JpaDialect The Web Web MVC framework Web框架 概述 與其他MVC實(shí)現(xiàn)框架的集成 Spring Web MVC框架的特點(diǎn) DispatcherServlet 控制器 AbstractController 和 WebContentGenerator 其它的簡(jiǎn)單控制器 MultiActionController 命令控制器 處理器映射(handler mapping) BeanNameUrlHandlerMapping SimpleUrlHandlerMapping 攔截器(HandlerInterceptor) 視圖與視圖解析 視圖解析器(ViewResolver) 視圖解析鏈 重定向(Rediret)到另一個(gè)視圖 RedirectView redirect:前綴 forward:前綴 本地化解析器 AcceptHeaderLocaleResolver CookieLocaleResolver SessionLocaleResolver LocaleChangeInterceptor 使用主題 簡(jiǎn)介 如何定義主題 主題解析器 Spring對(duì)分段文件上傳(multipart file upload)的支持 介紹 使用MultipartResolver 在表單中處理分段文件上傳 使用Spring的表單標(biāo)簽庫(kù) 配置 form標(biāo)簽 input標(biāo)簽 checkbox標(biāo)簽 checkboxes標(biāo)簽 radiobutton標(biāo)簽 radiobuttons標(biāo)簽 password標(biāo)簽 select標(biāo)簽 option標(biāo)簽 options標(biāo)簽 textarea標(biāo)簽 hidden標(biāo)簽 errors標(biāo)簽 處理異常 慣例優(yōu)先原則(convention over configuration) 對(duì)控制器的支持:ControllerClassNameHandlerMapping 對(duì)模型的支持:ModelMap(ModelAndView) 對(duì)視圖的支持:RequestToViewNameTranslator 基于注解的控制器配置 建立dispatcher實(shí)現(xiàn)注解支持 使用@Controller定義一個(gè)控制器 使用@RequestMapping映射請(qǐng)求 使用@RequestParam綁定請(qǐng)求參數(shù)到方法參數(shù) 使用@ModelAttribute提供一個(gè)從模型到數(shù)據(jù)的鏈接 使用@SessionAttributes指定存儲(chǔ)在會(huì)話中的屬性 自定義WebDataBinder初始化 使用@InitBinder自定義數(shù)據(jù)綁定 配置一個(gè)定制的WebBindingInitializer 更多資源 集成視圖技術(shù) 簡(jiǎn)介 JSP和JSTL 視圖解析器 'Plain-old' JSPs versus JSTL 'Plain-old' JSP與JSTL 幫助簡(jiǎn)化開(kāi)發(fā)的額外的標(biāo)簽 Tiles 需要的資源 如何集成Tiles UrlBasedViewResolver類(lèi) ResourceBundleViewResolver類(lèi) SimpleSpringPreparerFactory 和 SpringBeanPreparerFactory Velocity和FreeMarker 需要的資源 Context 配置 創(chuàng)建模板 高級(jí)配置 velocity.properties FreeMarker 綁定支持和表單處理 用于綁定的宏 簡(jiǎn)單綁定 表單輸入生成宏 輸入域 選擇輸入域 重載HTML轉(zhuǎn)碼行為并使你的標(biāo)簽符合XHTML XSLT 寫(xiě)在段首 Bean 定義 標(biāo)準(zhǔn)MVC控制器代碼 把模型數(shù)據(jù)轉(zhuǎn)化為XML 定義視圖屬性 文檔轉(zhuǎn)換 小結(jié) 文檔視圖(PDF/Excel) 簡(jiǎn)介 配置和安裝 文檔視圖定義 Controller 代碼 Excel視圖子類(lèi) PDF視圖子類(lèi) JasperReports 依賴(lài)的資源 配置 配置ViewResolver 配置View 關(guān)于報(bào)表文件 使用 JasperReportsMultiFormatView 構(gòu)造ModelAndView 使用子報(bào)表 配置子報(bào)表文件 配置子報(bào)表數(shù)據(jù)源 配置Exporter的參數(shù) 集成其它Web框架 簡(jiǎn)介 通用配置 JavaServer Faces DelegatingVariableResolver FacesContextUtils Struts ContextLoaderPlugin DelegatingRequestProcessor DelegatingActionProxy ActionSupport Classes Tapestry 注入 Spring 托管的 beans 將 Spring Beans 注入到 Tapestry 頁(yè)面中 組件定義文件 添加抽象訪問(wèn)方法 將 Spring Beans 注入到 Tapestry 頁(yè)面中 - Tapestry 4.0+ 風(fēng)格 WebWork 更多資源 Portlet MVC框架 介紹 控制器 - MVC中的C 視圖 - MVC中的V Web作用范圍的Bean DispatcherPortlet ViewRendererServlet 控制器 AbstractController 和 PortletContentGenerator 其它簡(jiǎn)單的控制器 Command控制器 PortletWrappingController 處理器映射 PortletModeHandlerMapping ParameterHandlerMapping PortletModeParameterHandlerMapping 增加 HandlerInterceptors HandlerInterceptorAdapter ParameterMappingInterceptor 視圖和它們的解析 Multipart文件上傳支持 使用 PortletMultipartResolver 處理表單里的文件上傳 異常處理 Portlet應(yīng)用的部署 整合 使用Spring進(jìn)行遠(yuǎn)程訪問(wèn)與Web服務(wù) 簡(jiǎn)介 使用RMI暴露服務(wù) 使用RmiServiceExporter暴露服務(wù) 在客戶(hù)端鏈接服務(wù) 使用Hessian或者Burlap通過(guò)HTTP遠(yuǎn)程調(diào)用服務(wù) 為Hessian和co.配置DispatcherServlet 使用HessianServiceExporter暴露你的bean 在客戶(hù)端連接服務(wù) 使用Burlap 對(duì)通過(guò)Hessian或Burlap暴露的服務(wù)使用HTTP Basic認(rèn)證 使用HTTP調(diào)用器暴露服務(wù) Exposing the service object 在客戶(hù)端連接服務(wù) Web Services 使用JAX-RPC暴露基于servlet的web服務(wù) 使用JAX-RPC訪問(wèn)web服務(wù) 注冊(cè)JAX-RPC Bean映射 注冊(cè)自己的JAX-RPC 處理器 使用JAX-WS暴露基于servlet的web服務(wù) 使用JAX-WS暴露單獨(dú)web服務(wù) 使用Spring支持的JAX-WS RI來(lái)暴露服務(wù) 使用JAX-WS訪問(wèn)web服務(wù) 使用XFire來(lái)暴露Web服務(wù) JMS 服務(wù)端配置 客戶(hù)端配置 對(duì)遠(yuǎn)程接口不提供自動(dòng)探測(cè)實(shí)現(xiàn) 在選擇這些技術(shù)時(shí)的一些考慮 Enterprise Java Beans (EJB) 集成 簡(jiǎn)介 訪問(wèn)EJB 概念 訪問(wèn)本地的無(wú)狀態(tài)Session Bean(SLSB) 訪問(wèn)遠(yuǎn)程SLSB Accessing EJB 2.x SLSBs versus EJB 3 SLSBs 使用Spring提供的輔助類(lèi)實(shí)現(xiàn)EJB組件 EJB 2.x base classes EJB 3 注入攔截 JMS (Java Message Service) 簡(jiǎn)介 使用Spring JMS JmsTemplate 連接工廠 目的地管理 消息偵聽(tīng)容器 SimpleMessageListenerContainer DefaultMessageListenerContainer ServerSessionMessageListenerContainer 事務(wù)管理 發(fā)送消息 使用消息轉(zhuǎn)換器 SessionCallback 和 ProducerCallback 接收消息 同步接收 異步接收 - 消息驅(qū)動(dòng)的POJO SessionAwareMessageListener接口 MessageListenerAdapter 事務(wù)中的消息處理 JCA消息端點(diǎn)的支持 JMS命名空間支持 JMX 介紹 將Bean暴露為JMX 創(chuàng)建MBeanServer 重用原有的MBeanServer 延遲初始化的MBean MBean的自動(dòng)注冊(cè) 控制注冊(cè)行為 控制Bean的管理接口 MBeanInfoAssembler接口 使用源碼級(jí)元數(shù)據(jù) 使用JDK 5.0的注解 源代碼級(jí)的元數(shù)據(jù)類(lèi)型 AutodetectCapableMBeanInfoAssembler接口 用Java接口定義管理接口 使用MethodNameBasedMBeanInfoAssembler 控制Bean的ObjectName 從Properties讀取Properties 使用MetadataNamingStrategy <context:mbean-export/>元素 JSR-160連接器 服務(wù)器端連接器 客戶(hù)端連接器 基于Burlap/Hessian/SOAP的JMX 通過(guò)代理訪問(wèn)MBean 通知 為通知注冊(cè)監(jiān)聽(tīng)器 發(fā)布通知 更多資源 JCA CCI 簡(jiǎn)介 配置CCI 連接器配置 在Spring中配置ConnectionFactory 配置CCI連接 使用一個(gè) CCI 單連接 使用Spring的 CCI訪問(wèn)支持 記錄轉(zhuǎn)換 CciTemplate類(lèi) DAO支持 自動(dòng)輸出記錄生成 總結(jié) 直接使用一個(gè)CCI Connection接口和Interaction接口 CciTemplate 使用示例 建模CCI訪問(wèn)為操作對(duì)象 MappingRecordOperation MappingCommAreaOperation 自動(dòng)生成輸出記錄 總結(jié) MappingRecordOperation 使用示例 MappingCommAreaOperation 使用示例 事務(wù) Spring郵件抽象層 簡(jiǎn)介 使用Spring郵件抽象 MailSender 和 SimpleMailMessage 的基本用法 使用 JavaMailSender 和 MimeMessagePreparator 使用MimeMessageHelper 發(fā)送附件和嵌入式資源(inline resources) 附件 內(nèi)嵌資源 使用模板來(lái)創(chuàng)建郵件內(nèi)容 一個(gè)基于Velocity的示例 Spring中的定時(shí)調(diào)度(Scheduling)和線程池(Thread Pooling) 簡(jiǎn)介 使用OpenSymphony Quartz 調(diào)度器 使用JobDetailBean 使用 MethodInvokingJobDetailFactoryBean 使用triggers和SchedulerFactoryBean來(lái)包裝任務(wù) 使用JDK Timer支持類(lèi) 創(chuàng)建定制的timers 使用 MethodInvokingTimerTaskFactoryBean類(lèi) 最后:使用TimerFactoryBean來(lái)設(shè)置任務(wù) SpringTaskExecutor抽象 TaskExecutor接口 TaskExecutor類(lèi)型 使用TaskExecutor 動(dòng)態(tài)語(yǔ)言支持 介紹 第一個(gè)示例 定義動(dòng)態(tài)語(yǔ)言支持的bean 公共概念 <lang:language/> 元素 Refreshable bean 內(nèi)置動(dòng)態(tài)語(yǔ)言源文件 理解dynamic-language-backed bean上下文中的構(gòu)造器注入 JRuby beans Groovy beans 通過(guò)回調(diào)定制Groovy對(duì)象 BeanShell beans 場(chǎng)景 Spring MVC控制器的腳本化 Validator的腳本化 Bits and bobs AOP - 通知腳本化bean 作用域 更多的資源 注解和源代碼級(jí)的元數(shù)據(jù)支持 簡(jiǎn)介 Spring的元數(shù)據(jù)支持 注解 @Required Spring中的其它@Annotations Jakarta Commons Attributes集成 元數(shù)據(jù)和Spring AOP自動(dòng)代理 基本原理 聲明式事務(wù)管理 示例程序 演示案例 介紹 使用動(dòng)態(tài)語(yǔ)言實(shí)現(xiàn)的Spring MVC控制器 構(gòu)建與部署 使用SimpleJdbcTemplate和@Repository實(shí)現(xiàn)DAO 域?qū)ο?/a> Data Access Object 構(gòu)建 XML Schema-based configuration Introduction XML Schema-based configuration Referencing the schemas The 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 schema The jms schema The tx (transaction) schema The aop schema The context schema <property-placeholder/> <annotation-config/> <component-scan/> <load-time-weaver/> <spring-configured/> <mbean-export/> The tool schema The beans schema Setting up your IDE Setting up Eclipse Setting up IntelliJ IDEA Integration issues XML parsing errors in the Resin v.3 application server Extensible XML authoring Introduction Authoring the schema Coding a NamespaceHandler Coding a BeanDefinitionParser Registering the handler and the schema 'META-INF/spring.handlers' 'META-INF/spring.schemas' Using a custom extension in your Spring XML configuration Meatier examples Nesting custom tags within custom tags Custom attributes on 'normal' elements Further Resources spring-beans-2.0.dtd spring.tld Introduction The bind tag The escapeBody tag The hasBindErrors tag The htmlEscape tag The message tag The nestedPath tag The theme tag The transform tag spring-form.tld Introduction The checkbox tag The checkboxes tag The errors tag The form tag The hidden tag The input tag The label tag The option tag The options tag The password tag The radiobutton tag The radiobuttons tag The select tag The textarea tag Spring 2.5開(kāi)發(fā)手冊(cè)中文化項(xiàng)目 聲明 致謝 參與人員 項(xiàng)目歷程
文字

6.2.?@AspectJ支持

@AspectJ使用了Java 5的注解,可以將切面聲明為普通的Java類(lèi)。@AspectJ樣式在AspectJ 5發(fā)布的AspectJ project部分中被引入。Spring 2.0使用了和AspectJ 5一樣的注解,并使用AspectJ來(lái)做切入點(diǎn)解析和匹配。但是,AOP在運(yùn)行時(shí)仍舊是純的Spring AOP,并不依賴(lài)于AspectJ的編譯器或者織入器(weaver)。

使用AspectJ的編譯器或者織入器的話就可以使用完整的AspectJ語(yǔ)言,我們將在第?6.8?節(jié) “在Spring應(yīng)用中使用AspectJ”中討論這個(gè)問(wèn)題。

6.2.1.?啟用@AspectJ支持

為了在Spring配置中使用@AspectJ切面,你首先必須啟用Spring對(duì)@AspectJ切面配置的支持,并確保自動(dòng)代理(autoproxying)的bean是否能被這些切面通知。自動(dòng)代理是指Spring會(huì)判斷一個(gè)bean是否使用了一個(gè)或多個(gè)切面通知,并據(jù)此自動(dòng)生成相應(yīng)的代理以攔截其方法調(diào)用,并且確保通知在需要時(shí)執(zhí)行。

通過(guò)在你的Spring的配置中引入下列元素來(lái)啟用Spring對(duì)@AspectJ的支持:

<aop:aspectj-autoproxy/>

我們假定你正在使用附錄?A, XML Schema-based configuration 所描述的schema支持。關(guān)于如何在aop的命名空間中引入這些標(biāo)簽,請(qǐng)參見(jiàn)第?A.2.7?節(jié) “The aop schema”

如果你正在使用DTD,你仍然可以通過(guò)在你的application context中添加如下定義來(lái)啟用@AspectJ支持:

<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />

你需要在你的應(yīng)用程序的classpath中引入兩個(gè)AspectJ庫(kù):aspectjweaver.jaraspectjrt.jar。這些庫(kù)可以在AspectJ的安裝包(1.5.1或者之后的版本)的'lib'目錄里找到,或者也可以在Spring-with-dependencies發(fā)布包的'lib/aspectj'目錄下找到。

6.2.2.?聲明一個(gè)切面

啟用@AspectJ支持后,在application context中定義的任意帶有一個(gè)@Aspect切面(擁有@Aspect注解)的bean都將被Spring自動(dòng)識(shí)別并用于配置Spring AOP。以下例子展示了為完成一個(gè)不是非常有用的切面所需要的最小定義:

application context中一個(gè)常見(jiàn)的bean定義,它指向一個(gè)使用了@Aspect注解的bean類(lèi):

<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect">
         <!-- configure properties of aspect here as normal -->
      </bean>
      

以及NotVeryUsefulAspect類(lèi)的定義,使用了 org.aspectj.lang.annotation.Aspect注解。

package org.xyz;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class NotVeryUsefulAspect {

}

切面(用@Aspect注解的類(lèi))和其他類(lèi)一樣有方法和字段定義。他們也可能包括切入點(diǎn),通知和引入(inter-type)聲明。

通知切面

在Spring AOP中,擁有切面的類(lèi)本身可能是其它切面中通知的目標(biāo)。一個(gè)類(lèi)上面的@Aspect注解標(biāo)識(shí)它為一個(gè)切面,并且從自動(dòng)代理中排除它。

6.2.3.?聲明一個(gè)切入點(diǎn)(pointcut)

在前面我們提到,切入點(diǎn)決定了連接點(diǎn)關(guān)注的內(nèi)容,使得我們可以控制通知什么時(shí)候執(zhí)行。Spring AOP只支持Spring bean的方法執(zhí)行連接點(diǎn)。所以你可以把切入點(diǎn)看做是Spring bean上方法執(zhí)行的匹配。一個(gè)切入點(diǎn)聲明有兩個(gè)部分:一個(gè)包含名字和任意參數(shù)的簽名,還有一個(gè)切入點(diǎn)表達(dá)式,該表達(dá)式?jīng)Q定了我們關(guān)注那個(gè)方法的執(zhí)行。在@AspectJ注解風(fēng)格的AOP中,一個(gè)切入點(diǎn)簽名通過(guò)一個(gè)普通的方法定義來(lái)提供,并且切入點(diǎn)表達(dá)式使用@Pointcut注解來(lái)表示(作為切入點(diǎn)簽名的方法必須返回void 類(lèi)型)。

用一個(gè)例子能幫我們清楚的區(qū)分切入點(diǎn)簽名和切入點(diǎn)表達(dá)式之間的差別,下面的例子定義了一個(gè)切入點(diǎn)'anyOldTransfer',這個(gè)切入點(diǎn)將匹配任何名為 "transfer" 的方法的執(zhí)行:

@Pointcut("execution(* transfer(..))")// the pointcut expression
private void anyOldTransfer() {}// the pointcut signature

切入點(diǎn)表達(dá)式,也就是組成@Pointcut注解的值,是正規(guī)的AspectJ 5切入點(diǎn)表達(dá)式。如果你想要更多了解AspectJ的切入點(diǎn)語(yǔ)言,請(qǐng)參見(jiàn)AspectJ編程指南(如果要了解基于Java 5的擴(kuò)展請(qǐng)參閱AspectJ 5 開(kāi)發(fā)手冊(cè))或者其他人寫(xiě)的關(guān)于AspectJ的書(shū),例如Colyer et. al.著的“Eclipse AspectJ”或者Ramnivas Laddad著的“AspectJ in Action”。

6.2.3.1.?切入點(diǎn)指示符(PCD)的支持

Spring AOP支持在切入點(diǎn)表達(dá)式中使用如下的AspectJ切入點(diǎn)指示符:

  • execution - 匹配方法執(zhí)行的連接點(diǎn),這是你將會(huì)用到的Spring的最主要的切入點(diǎn)指示符。

  • within - 限定匹配特定類(lèi)型的連接點(diǎn)(在使用Spring AOP的時(shí)候,在匹配的類(lèi)型中定義的方法的執(zhí)行)。

  • this - 限定匹配特定的連接點(diǎn)(使用Spring AOP的時(shí)候方法的執(zhí)行),其中bean reference(Spring AOP 代理)是指定類(lèi)型的實(shí)例。

  • target - 限定匹配特定的連接點(diǎn)(使用Spring AOP的時(shí)候方法的執(zhí)行),其中目標(biāo)對(duì)象(被代理的應(yīng)用對(duì)象)是指定類(lèi)型的實(shí)例。

  • args - 限定匹配特定的連接點(diǎn)(使用Spring AOP的時(shí)候方法的執(zhí)行),其中參數(shù)是指定類(lèi)型的實(shí)例。

  • @target - 限定匹配特定的連接點(diǎn)(使用Spring AOP的時(shí)候方法的執(zhí)行),其中正執(zhí)行對(duì)象的類(lèi)持有指定類(lèi)型的注解。

  • @args - 限定匹配特定的連接點(diǎn)(使用Spring AOP的時(shí)候方法的執(zhí)行),其中實(shí)際傳入?yún)?shù)的運(yùn)行時(shí)類(lèi)型持有指定類(lèi)型的注解。

  • @within - 限定匹配特定的連接點(diǎn),其中連接點(diǎn)所在類(lèi)型已指定注解(在使用Spring AOP的時(shí)候,所執(zhí)行的方法所在類(lèi)型已指定注解)。

  • @annotation - 限定匹配特定的連接點(diǎn)(使用Spring AOP的時(shí)候方法的執(zhí)行),其中連接點(diǎn)的主題持有指定的注解。

另外,Spring AOP還提供了一個(gè)名為'bean'的PCD。這個(gè)PCD允許你限定匹配連接點(diǎn)到一個(gè)特定名稱(chēng)的Spring bean,或者到一個(gè)特定名稱(chēng)Spring bean的集合(當(dāng)使用通配符時(shí))。'bean' PCD具有下列的格式:

bean(idOrNameOfBean)

'idOrNameOfBean'標(biāo)記可以是任何Spring bean的名字:限定通配符使用'*'來(lái)提供,如果你為Spring bean制定一些命名約定,你可以非常容易地編寫(xiě)一個(gè)'bean' PCD表達(dá)式將它們選出來(lái)。和其它連接點(diǎn)指示符一樣,'bean' PCD也支持&&, ||和 !邏輯操作符。

注意

請(qǐng)注意'bean' PCD僅僅 被Spring AOP支持而不是AspectJ. 這是Spring對(duì)AspectJ中定義的標(biāo)準(zhǔn)PCD的一個(gè)特定擴(kuò)展。

'bean' PCD不僅僅可以在類(lèi)型級(jí)別(被限制在基于織入AOP上)上操作而還可以在實(shí)例級(jí)別(基于Spring bean的概念)上操作。

因?yàn)镾pring AOP限制了連接點(diǎn)必須是方法執(zhí)行級(jí)別的,上文pointcut指示符中的討論也給出了一個(gè)定義,這個(gè)定義和AspectJ的編程指南中的定義相比顯得更加狹窄。除此之外,AspectJ它本身有基于類(lèi)型的語(yǔ)義,在執(zhí)行的連接點(diǎn)'this'和'target'都是指同一個(gè)對(duì)象,也就是執(zhí)行方法的對(duì)象。Spring AOP是一個(gè)基于代理的系統(tǒng),并且嚴(yán)格區(qū)分代理對(duì)象本身(對(duì)應(yīng)于'this')和背后的目標(biāo)對(duì)象(對(duì)應(yīng)于'target')

6.2.3.2.?組合切入點(diǎn)表達(dá)式

切入點(diǎn)表達(dá)式可以使用'&', '||' 和 '!'來(lái)組合。還可以通過(guò)名字來(lái)指向切入點(diǎn)表達(dá)式。以下的例子展示了三種切入點(diǎn)表達(dá)式: anyPublicOperation(在一個(gè)方法執(zhí)行連接點(diǎn)代表了任意public方法的執(zhí)行時(shí)匹配);inTrading(在一個(gè)代表了在交易模塊中的任意的方法執(zhí)行時(shí)匹配)和 tradingOperation(在一個(gè)代表了在交易模塊中的任意的公共方法執(zhí)行時(shí)匹配)。

@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}

@Pointcut("within(com.xyz.someapp.trading..*")
private void inTrading() {}

@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {}
        

如上所示,用更少的命名組件來(lái)構(gòu)建更加復(fù)雜的切入點(diǎn)表達(dá)式是一種最佳實(shí)踐。當(dāng)用名字來(lái)指定切入點(diǎn)時(shí)使用的是常見(jiàn)的Java成員可視性訪問(wèn)規(guī)則。(比如說(shuō),你可以在同一類(lèi)型中訪問(wèn)私有的切入點(diǎn),在繼承關(guān)系中訪問(wèn)受保護(hù)的切入點(diǎn),可以在任意地方訪問(wèn)公共切入點(diǎn))。成員可視性訪問(wèn)規(guī)則不影響到切入點(diǎn)的匹配。

6.2.3.3.?共享通用切入點(diǎn)定義

當(dāng)開(kāi)發(fā)企業(yè)級(jí)應(yīng)用的時(shí)候,你通常會(huì)想要從幾個(gè)切面來(lái)引用模塊化的應(yīng)用和特定操作的集合。我們推薦定義一個(gè)“SystemArchitecture”切面來(lái)捕捉通用的切入點(diǎn)表達(dá)式。一個(gè)典型的通用切面看起來(lái)可能像下面這樣:

package com.xyz.someapp;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class SystemArchitecture {

  
  @Pointcut("within(com.xyz.someapp.web..*)")
  public void inWebLayer() {}

  
  @Pointcut("within(com.xyz.someapp.service..*)")
  public void inServiceLayer() {}

  
  @Pointcut("within(com.xyz.someapp.dao..*)")
  public void inDataAccessLayer() {}

  
  @Pointcut("execution(* com.xyz.someapp.service.*.*(..))")
  public void businessService() {}
  
  
  @Pointcut("execution(* com.xyz.someapp.dao.*.*(..))")
  public void dataAccessOperation() {}

}

示例中的切入點(diǎn)定義了一個(gè)你可以在任何需要切入點(diǎn)表達(dá)式的地方可引用的切面。比如,為了使service層事務(wù)化,你可以寫(xiě)成:

<aop:config>
  <aop:advisor 
      pointcut="com.xyz.someapp.SystemArchitecture.businessService()"
      advice-ref="tx-advice"/>
</aop:config>

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

我們將在第?6.3?節(jié) “基于Schema的AOP支持”中討論 <aop:config><aop:advisor>標(biāo)簽。在第?9?章 事務(wù)管理 中討論事務(wù)標(biāo)簽。

6.2.3.4.?示例

Spring AOP 用戶(hù)可能會(huì)經(jīng)常使用 execution切入點(diǎn)指示符。執(zhí)行表達(dá)式的格式如下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)
          throws-pattern?)

除了返回類(lèi)型模式(上面代碼片斷中的ret-type-pattern),名字模式和參數(shù)模式以外, 所有的部分都是可選的。返回類(lèi)型模式?jīng)Q定了方法的返回類(lèi)型必須依次匹配一個(gè)連接點(diǎn)。 你會(huì)使用的最頻繁的返回類(lèi)型模式是*,它代表了匹配任意的返回類(lèi)型。 一個(gè)全限定的類(lèi)型名將只會(huì)匹配返回給定類(lèi)型的方法。名字模式匹配的是方法名。 你可以使用*通配符作為所有或者部分命名模式。 參數(shù)模式稍微有點(diǎn)復(fù)雜:()匹配了一個(gè)不接受任何參數(shù)的方法, 而(..)匹配了一個(gè)接受任意數(shù)量參數(shù)的方法(零或者更多)。 模式(*)匹配了一個(gè)接受一個(gè)任何類(lèi)型的參數(shù)的方法。 模式(*,String)匹配了一個(gè)接受兩個(gè)參數(shù)的方法,第一個(gè)可以是任意類(lèi)型, 第二個(gè)則必須是String類(lèi)型。更多的信息請(qǐng)參閱AspectJ編程指南中 語(yǔ)言語(yǔ)義的部分。

下面給出一些通用切入點(diǎn)表達(dá)式的例子。

  • 任意公共方法的執(zhí)行:

    execution(public * *(..))
  • 任何一個(gè)名字以“set”開(kāi)始的方法的執(zhí)行:

    execution(* set*(..))
  • AccountService接口定義的任意方法的執(zhí)行:

    execution(* com.xyz.service.AccountService.*(..))
  • 在service包中定義的任意方法的執(zhí)行:

    execution(* com.xyz.service.*.*(..))
  • 在service包或其子包中定義的任意方法的執(zhí)行:

    execution(* com.xyz.service..*.*(..))
  • 在service包中的任意連接點(diǎn)(在Spring AOP中只是方法執(zhí)行):

    within(com.xyz.service.*)
  • 在service包或其子包中的任意連接點(diǎn)(在Spring AOP中只是方法執(zhí)行):

    within(com.xyz.service..*)
  • 實(shí)現(xiàn)了AccountService接口的代理對(duì)象的任意連接點(diǎn) (在Spring AOP中只是方法執(zhí)行):

    this(com.xyz.service.AccountService)

    'this'在綁定表單中更加常用:- 請(qǐng)參見(jiàn)后面的通知一節(jié)中了解如何使得代理對(duì)象在通知體內(nèi)可用。

  • 實(shí)現(xiàn)AccountService接口的目標(biāo)對(duì)象的任意連接點(diǎn) (在Spring AOP中只是方法執(zhí)行):

    target(com.xyz.service.AccountService)

    'target'在綁定表單中更加常用:- 請(qǐng)參見(jiàn)后面的通知一節(jié)中了解如何使得目標(biāo)對(duì)象在通知體內(nèi)可用。

  • 任何一個(gè)只接受一個(gè)參數(shù),并且運(yùn)行時(shí)所傳入的參數(shù)是Serializable 接口的連接點(diǎn)(在Spring AOP中只是方法執(zhí)行)

    args(java.io.Serializable)

    'args'在綁定表單中更加常用:- 請(qǐng)參見(jiàn)后面的通知一節(jié)中了解如何使得方法參數(shù)在通知體內(nèi)可用。

    請(qǐng)注意在例子中給出的切入點(diǎn)不同于 execution(* *(java.io.Serializable)): args版本只有在動(dòng)態(tài)運(yùn)行時(shí)候傳入?yún)?shù)是Serializable時(shí)才匹配,而execution版本在方法簽名中聲明只有一個(gè) Serializable類(lèi)型的參數(shù)時(shí)候匹配。

  • 目標(biāo)對(duì)象中有一個(gè) @Transactional 注解的任意連接點(diǎn) (在Spring AOP中只是方法執(zhí)行)

    @target(org.springframework.transaction.annotation.Transactional)

    '@target'在綁定表單中更加常用:- 請(qǐng)參見(jiàn)后面的通知一節(jié)中了解如何使得注解對(duì)象在通知體內(nèi)可用。

  • 任何一個(gè)目標(biāo)對(duì)象聲明的類(lèi)型有一個(gè) @Transactional 注解的連接點(diǎn) (在Spring AOP中只是方法執(zhí)行):

    @within(org.springframework.transaction.annotation.Transactional)

    '@within'在綁定表單中更加常用:- 請(qǐng)參見(jiàn)后面的通知一節(jié)中了解如何使得注解對(duì)象在通知體內(nèi)可用。

  • 任何一個(gè)執(zhí)行的方法有一個(gè) @Transactional 注解的連接點(diǎn) (在Spring AOP中只是方法執(zhí)行)

    @annotation(org.springframework.transaction.annotation.Transactional)

    '@annotation'在綁定表單中更加常用:- 請(qǐng)參見(jiàn)后面的通知一節(jié)中了解如何使得注解對(duì)象在通知體內(nèi)可用。

  • 任何一個(gè)只接受一個(gè)參數(shù),并且運(yùn)行時(shí)所傳入的參數(shù)類(lèi)型具有@Classified 注解的連接點(diǎn)(在Spring AOP中只是方法執(zhí)行)

    @args(com.xyz.security.Classified)

    '@args'在綁定表單中更加常用:- 請(qǐng)參見(jiàn)后面的通知一節(jié)中了解如何使得注解對(duì)象在通知體內(nèi)可用。

  • 任何一個(gè)在名為'tradeService'的Spring bean之上的連接點(diǎn) (在Spring AOP中只是方法執(zhí)行):

    bean(tradeService)
  • 任何一個(gè)在名字匹配通配符表達(dá)式'*Service'的Spring bean之上的連接點(diǎn) (在Spring AOP中只是方法執(zhí)行):

    bean(*Service)

6.2.4.?聲明通知

通知是跟一個(gè)切入點(diǎn)表達(dá)式關(guān)聯(lián)起來(lái)的,并且在切入點(diǎn)匹配的方法執(zhí)行之前或者之后或者前后運(yùn)行。 切入點(diǎn)表達(dá)式可能是指向已命名的切入點(diǎn)的簡(jiǎn)單引用或者是一個(gè)已經(jīng)聲明過(guò)的切入點(diǎn)表達(dá)式。

6.2.4.1.?前置通知

一個(gè)切面里使用 @Before 注解聲明前置通知:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BeforeExample {

  @Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
  public void doAccessCheck() {
    // ...
  }

}

如果使用一個(gè)in-place 的切入點(diǎn)表達(dá)式,我們可以把上面的例子換個(gè)寫(xiě)法:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class BeforeExample {

  @Before("execution(* com.xyz.myapp.dao.*.*(..))")
  public void doAccessCheck() {
    // ...
  }

}

6.2.4.2.?后置通知(After returning advice)

返回后通知通常在一個(gè)匹配的方法返回的時(shí)候執(zhí)行。使用 @AfterReturning 注解來(lái)聲明:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

  @AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
  public void doAccessCheck() {
    // ...
  }

}

說(shuō)明:你可以在相同的切面里定義多個(gè)通知,或者其他成員。 我們只是在展示如何定義一個(gè)簡(jiǎn)單的通知。這些例子主要的側(cè)重點(diǎn)是正在討論的問(wèn)題。

有時(shí)候你需要在通知體內(nèi)得到返回的值。你可以使用@AfterReturning 接口的形式來(lái)綁定返回值:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturningExample {

  @AfterReturning(
    pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
    returning="retVal")
  public void doAccessCheck(Object retVal) {
    // ...
  }
  
}

returning屬性中使用的名字必須對(duì)應(yīng)于通知方法內(nèi)的一個(gè)參數(shù)名。 當(dāng)一個(gè)方法執(zhí)行返回后,返回值作為相應(yīng)的參數(shù)值傳入通知方法。 一個(gè)returning子句也限制了只能匹配到返回指定類(lèi)型值的方法。 (在本例子中,返回值是Object類(lèi),也就是說(shuō)返回任意類(lèi)型都會(huì)匹配)

請(qǐng)注意當(dāng)使用后置通知時(shí)允許返回一個(gè)完全不同的引用。

6.2.4.3.?異常通知(After throwing advice)

拋出異常通知在一個(gè)方法拋出異常后執(zhí)行。使用@AfterThrowing注解來(lái)聲明:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

  @AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
  public void doRecoveryActions() {
    // ...
  }

}

你通常會(huì)想要限制通知只在某種特殊的異常被拋出的時(shí)候匹配,你還希望可以在通知體內(nèi)得到被拋出的異常。 使用throwing屬性不僅可以限制匹配的異常類(lèi)型(如果你不想限制,請(qǐng)使用 Throwable作為異常類(lèi)型),還可以將拋出的異常綁定到通知的一個(gè)參數(shù)上。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class AfterThrowingExample {

  @AfterThrowing(
    pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
    throwing="ex")
  public void doRecoveryActions(DataAccessException ex) {
    // ...
  }

}

throwing屬性中使用的名字必須與通知方法內(nèi)的一個(gè)參數(shù)對(duì)應(yīng)。 當(dāng)一個(gè)方法因拋出一個(gè)異常而中止后,這個(gè)異常將會(huì)作為那個(gè)對(duì)應(yīng)的參數(shù)送至通知方法。 throwing 子句也限制了只能匹配到拋出指定異常類(lèi)型的方法 (上面的示例為DataAccessException)。

6.2.4.4.?最終通知(After (finally) advice)

不論一個(gè)方法是如何結(jié)束的,最終通知都會(huì)運(yùn)行。使用@After 注解來(lái)聲明。最終通知必須準(zhǔn)備處理正常返回和異常返回兩種情況。通常用它來(lái)釋放資源。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;

@Aspect
public class AfterFinallyExample {

  @After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
  public void doReleaseLock() {
    // ...
  }

}

6.2.4.5.?環(huán)繞通知

最后一種通知是環(huán)繞通知。環(huán)繞通知在一個(gè)方法執(zhí)行之前和之后執(zhí)行。它使得通知有機(jī)會(huì) 在一個(gè)方法執(zhí)行之前和執(zhí)行之后運(yùn)行。而且它可以決定這個(gè)方法在什么時(shí)候執(zhí)行,如何執(zhí)行,甚至是否執(zhí)行。 環(huán)繞通知經(jīng)常在某線程安全的環(huán)境下,你需要在一個(gè)方法執(zhí)行之前和之后共享某種狀態(tài)的時(shí)候使用。 請(qǐng)盡量使用最簡(jiǎn)單的滿足你需求的通知。(比如如果簡(jiǎn)單的前置通知也可以適用的情況下不要使用環(huán)繞通知)。

環(huán)繞通知使用@Around注解來(lái)聲明。通知的第一個(gè)參數(shù)必須是 ProceedingJoinPoint類(lèi)型。在通知體內(nèi),調(diào)用 ProceedingJoinPointproceed()方法會(huì)導(dǎo)致 后臺(tái)的連接點(diǎn)方法執(zhí)行。proceed 方法也可能會(huì)被調(diào)用并且傳入一個(gè) Object[]對(duì)象-該數(shù)組中的值將被作為方法執(zhí)行時(shí)的參數(shù)。

當(dāng)傳入一個(gè)Object[]對(duì)象的時(shí)候,處理的方法與通過(guò)AspectJ編譯器處理環(huán)繞通知略有不同。 對(duì)于使用傳統(tǒng)AspectJ語(yǔ)言寫(xiě)的環(huán)繞通知來(lái)說(shuō),傳入?yún)?shù)的數(shù)量必須和傳遞給環(huán)繞通知的參數(shù)數(shù)量匹配 (不是后臺(tái)的連接點(diǎn)接受的參數(shù)數(shù)量),并且特定順序的傳入?yún)?shù)代替了將要綁定給連接點(diǎn)的原始值 (如果你看不懂不用擔(dān)心)。Spring采用的方法更加簡(jiǎn)單并且能更好匹配它基于代理(proxy-based)的執(zhí)行語(yǔ)法, 如果你使用AspectJ的編譯器和編織器來(lái)編譯為Spring而寫(xiě)的@AspectJ切面和處理參數(shù),你只需要知道這一區(qū)別即可。 有一種方法可以讓你寫(xiě)出100%兼容Spring AOP和AspectJ的表達(dá)式,我們將會(huì)在后續(xù)的通知參數(shù)的章節(jié)中討論它。

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;

@Aspect
public class AroundExample {

  @Around("com.xyz.myapp.SystemArchitecture.businessService()")
  public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    // start stopwatch
    Object retVal = pjp.proceed();
    // stop stopwatch
    return retVal;
  }

}

方法的調(diào)用者得到的返回值就是環(huán)繞通知返回的值。 例如:一個(gè)簡(jiǎn)單的緩存切面,如果緩存中有值,就返回該值,否則調(diào)用proceed()方法。 請(qǐng)注意proceed可能在通知體內(nèi)部被調(diào)用一次,許多次,或者根本不被調(diào)用,所有這些都是合法的。

6.2.4.6.?通知參數(shù)(Advice parameters)

Spring 2.0 提供了完整的通知類(lèi)型 - 這意味著你可以在通知簽名中聲明所需的參數(shù), (就像我們?cè)谇懊婵吹降暮笾煤彤惓Mㄖ粯樱┒豢偸鞘褂肙bject[]。 我們將會(huì)看到如何使得參數(shù)和其他上下文值對(duì)通知體可用。 首先讓我們看以下如何編寫(xiě)普通的通知以找出正在被通知的方法。

6.2.4.6.1.?訪問(wèn)當(dāng)前的連接點(diǎn)

任何通知方法可以將第一個(gè)參數(shù)定義為org.aspectj.lang.JoinPoint類(lèi)型 (環(huán)繞通知需要定義第一個(gè)參數(shù)為ProceedingJoinPoint類(lèi)型, 它是 JoinPoint 的一個(gè)子類(lèi))。JoinPoint 接口提供了一系列有用的方法,比如 getArgs()(返回方法參數(shù))、 getThis()(返回代理對(duì)象)、getTarget()(返回目標(biāo))、 getSignature()(返回正在被通知的方法相關(guān)信息)和 toString() (打印出正在被通知的方法的有用信息)。詳細(xì)的內(nèi)容請(qǐng)參考JavaDoc。

6.2.4.6.2.?傳遞參數(shù)給通知

我們已經(jīng)看到了如何綁定返回值或者異常(使用后置通知和異常通知)。為了可以在通知體內(nèi)訪問(wèn)參數(shù), 你可以使用args來(lái)綁定。如果在一個(gè)args表達(dá)式中應(yīng)該使用類(lèi)型名字的地方 使用一個(gè)參數(shù)名字,那么當(dāng)通知執(zhí)行的時(shí)候?qū)?yīng)的參數(shù)值將會(huì)被傳遞進(jìn)來(lái)。用一個(gè)例子應(yīng)該會(huì)使它變得清晰。 假使你想要通知以一個(gè)Account對(duì)象作為第一個(gè)參數(shù)的DAO操作的執(zhí)行, 你想要在通知體內(nèi)也能訪問(wèn)account對(duì)象,可以編寫(xiě)如下的代碼:

@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation() &&" + 
        "args(account,..)")
public void validateAccount(Account account) {
  // ...
}

切入點(diǎn)表達(dá)式的 args(account,..) 部分有兩個(gè)目的:首先它保證了 只會(huì)匹配那些接受至少一個(gè)參數(shù)的方法的執(zhí)行,而且傳入的參數(shù)必須是Account類(lèi)型的實(shí)例, 其次它使得在通知體內(nèi)可以通過(guò)account 參數(shù)訪問(wèn)實(shí)際的Account對(duì)象。

另外一個(gè)辦法是定義一個(gè)切入點(diǎn),這個(gè)切入點(diǎn)在匹配某個(gè)連接點(diǎn)的時(shí)候“提供”了 Account對(duì)象的值,然后直接從通知中訪問(wèn)那個(gè)命名切入點(diǎn)??雌饋?lái)和下面的示例一樣:

@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() &&" + 
          "args(account,..)")
private void accountDataAccessOperation(Account account) {}

@Before("accountDataAccessOperation(account)")
public void validateAccount(Account account) {
  // ...
}

有興趣的讀者請(qǐng)參閱 AspectJ 編程指南了解更詳細(xì)的內(nèi)容。

代理對(duì)象(this)、目標(biāo)對(duì)象(target) 和注解(@within, @target, @annotation, @args)都可以用一種類(lèi)似的格式來(lái)綁定。 以下的例子展示了如何使用 @Auditable注解來(lái)匹配方法執(zhí)行,并提取Audit代碼。

首先是@Auditable注解的定義:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auditable {
	AuditCode value();
}

然后是匹配@Auditable方法執(zhí)行的通知:

@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && " + 
        "@annotation(auditable)")
public void audit(Auditable auditable) {
  AuditCode code = auditable.value();
  // ...
}
6.2.4.6.3.?確定參數(shù)名

綁定在通知上的參數(shù)依賴(lài)切入點(diǎn)表達(dá)式的匹配名,并借此在(通知和切入點(diǎn))的方法簽名中聲明參數(shù)名。 參數(shù)名無(wú)法 通過(guò)Java反射來(lái)獲取,所以Spring AOP使用如下的策略來(lái)確定參數(shù)名字:

  1. 如果參數(shù)名字已經(jīng)被用戶(hù)明確指定,則使用指定的參數(shù)名: 通知和切入點(diǎn)注解有一個(gè)額外的"argNames"屬性,該屬性用來(lái)指定所注解的方法的參數(shù)名 - 這些參數(shù)名在運(yùn)行時(shí)是可以 訪問(wèn)的。例子如下:

    @Before(
       value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
       argNames="bean,auditable")
    public void audit(Object bean, Auditable auditable) {
      AuditCode code = auditable.value();
      // ... use code and bean
    }

    如果第一個(gè)參數(shù)是JoinPoint, ProceedingJoinPoint, 或者JoinPoint.StaticPart類(lèi)型, 你可以在“argNames”屬性的值中省去參數(shù)的名字。例如,如果你修改前面的通知來(lái)獲取連接點(diǎn)對(duì)象, "argNames"屬性就不必包含它:

    @Before(
       value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
       argNames="bean,auditable")
    public void audit(JoinPoint jp, Object bean, Auditable auditable) {
      AuditCode code = auditable.value();
      // ... use code, bean, and jp
    }

    對(duì)于第一個(gè)JoinPoint, ProceedingJoinPoint,和 JoinPoint.StaticPart類(lèi)型的參數(shù)特殊處理特別適合 沒(méi)有集合其它連接上下文的通知。在這種情部下,你可以簡(jiǎn)單的省略“argNames”屬性。 例如,下面的通知不需要聲明“argNames”屬性:

    @Before(
       "com.xyz.lib.Pointcuts.anyPublicMethod()")
    public void audit(JoinPoint jp) {
      // ... use jp
    }
  2. 使用'argNames'屬性有一點(diǎn)笨拙,所以如果'argNames' 屬性沒(méi)有被指定,Spring AOP將查看類(lèi)的debug信息并嘗試從本地的變量表確定參數(shù)名。只要類(lèi)編譯時(shí)有debug信息, (最少要有'-g:vars')這個(gè)信息將會(huì)出現(xiàn)。打開(kāi)這個(gè)標(biāo)志編譯的結(jié)果是: (1)你的代碼稍微容易理解(反向工程), (2)class文件的大小稍微有些大(通常不重要), (3)你的編譯器將不會(huì)應(yīng)用優(yōu)化去移除未使用的本地變量。換句話說(shuō),打開(kāi)這個(gè)標(biāo)志創(chuàng)建時(shí)你應(yīng)當(dāng)不會(huì)遇到困難。

    如果一個(gè)@AspectJ切面已經(jīng)被AspectJ編譯器(ajc)編譯過(guò),即使沒(méi)有debug信息, 也不需要添加argNames參數(shù),因?yàn)榫幾g器會(huì)保留必需的信息。

  3. 如果不加上必要的debug信息來(lái)編譯的話,Spring AOP將會(huì)嘗試推斷綁定變量到參數(shù)的配對(duì)。 (例如,要是只有一個(gè)變量被綁定到切入點(diǎn)表達(dá)式,通知方法只接受一個(gè)參數(shù), 配對(duì)是顯而易見(jiàn)的)。 如果變量的綁定不明確,將會(huì)拋出一個(gè)AmbiguousBindingException異常。

  4. 如果以上所有策略都失敗了,將會(huì)拋出一個(gè)IllegalArgumentException異常。

6.2.4.6.4.?處理參數(shù)

我們之前提過(guò)我們將會(huì)討論如何編寫(xiě)一個(gè)帶參數(shù)的的proceed()調(diào)用, 使得在Spring AOP和AspectJ中都能正常工作。解決方法是僅僅確保通知簽名按順序綁定方法參數(shù)。例如:

@Around("execution(List<Account> find*(..)) &&" +
        "com.xyz.myapp.SystemArchitecture.inDataAccessLayer() && " +
        "args(accountHolderNamePattern)")		
public Object preProcessQueryPattern(ProceedingJoinPoint pjp, String accountHolderNamePattern)
throws Throwable {
  String newPattern = preProcess(accountHolderNamePattern);
  return pjp.proceed(new Object[] {newPattern});
}        

大多數(shù)情況下你都會(huì)這樣綁定(就像上面的例子那樣)。

6.2.4.7.?通知順序

如果有多個(gè)通知想要在同一連接點(diǎn)運(yùn)行會(huì)發(fā)生什么?Spring AOP遵循跟AspectJ一樣的優(yōu)先規(guī)則來(lái)確定通知執(zhí)行的順序。 在“進(jìn)入”連接點(diǎn)的情況下,最高優(yōu)先級(jí)的通知會(huì)先執(zhí)行(所以給定的兩個(gè)前置通知中,優(yōu)先級(jí)高的那個(gè)會(huì)先執(zhí)行)。 在“退出”連接點(diǎn)的情況下,最高優(yōu)先級(jí)的通知會(huì)最后執(zhí)行。(所以給定的兩個(gè)后置通知中, 優(yōu)先級(jí)高的那個(gè)會(huì)第二個(gè)執(zhí)行)。

當(dāng)定義在不同的切面里的兩個(gè)通知都需要在一個(gè)相同的連接點(diǎn)中運(yùn)行, 那么除非你指定,否則執(zhí)行的順序是未知的。你可以通過(guò)指定優(yōu)先級(jí)來(lái)控制執(zhí)行順序。 在標(biāo)準(zhǔn)的Spring方法中可以在切面類(lèi)中實(shí)現(xiàn)org.springframework.core.Ordered 接口或者用Order注解做到這一點(diǎn)。在兩個(gè)切面中, Ordered.getValue()方法返回值(或者注解值)較低的那個(gè)有更高的優(yōu)先級(jí)。

當(dāng)定義在相同的切面里的兩個(gè)通知都需要在一個(gè)相同的連接點(diǎn)中運(yùn)行, 執(zhí)行的順序是未知的(因?yàn)檫@里沒(méi)有方法通過(guò)反射javac編譯的類(lèi)來(lái)獲取聲明順序)。 考慮在每個(gè)切面類(lèi)中按連接點(diǎn)壓縮這些通知方法到一個(gè)通知方法,或者重構(gòu)通知的片段到各自的切面類(lèi)中 - 它能在切面級(jí)別進(jìn)行排序。

6.2.5.?引入(Introduction)

引入(在AspectJ中被稱(chēng)為inter-type聲明)使得一個(gè)切面可以定義被通知對(duì)象實(shí)現(xiàn)給定的接口, 并且可以為那些對(duì)象提供具體的實(shí)現(xiàn)。

使用@DeclareParents注解來(lái)定義引入。這個(gè)注解用來(lái)定義匹配的類(lèi)型 擁有一個(gè)新的父類(lèi)(所以有了這個(gè)名字)。比如,給定一個(gè)接口UsageTracked, 和接口的具體實(shí)現(xiàn)DefaultUsageTracked類(lèi), 接下來(lái)的切面聲明了所有的service接口的實(shí)現(xiàn)都實(shí)現(xiàn)了UsageTracked接口。 (比如為了通過(guò)JMX輸出統(tǒng)計(jì)信息)。

@Aspect
public class UsageTracking {

  @DeclareParents(value="com.xzy.myapp.service.*+",
                  defaultImpl=DefaultUsageTracked.class)
  public static UsageTracked mixin;
  
  @Before("com.xyz.myapp.SystemArchitecture.businessService() &&" +
          "this(usageTracked)")
  public void recordUsage(UsageTracked usageTracked) {
    usageTracked.incrementUseCount();
  }
  
}

實(shí)現(xiàn)的接口通過(guò)被注解的字段類(lèi)型來(lái)決定。@DeclareParents注解的 value屬性是一個(gè)AspectJ的類(lèi)型模式:- 任何匹配類(lèi)型的bean都會(huì)實(shí)現(xiàn) UsageTracked接口。請(qǐng)注意,在上面的前置通知的例子中,service beans 可以直接用作UsageTracked接口的實(shí)現(xiàn)。如果需要編程式的來(lái)訪問(wèn)一個(gè)bean, 你可以這樣寫(xiě):

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

6.2.6.?切面實(shí)例化模型

(這是一個(gè)高級(jí)主題,所以如果你剛開(kāi)始學(xué)習(xí)AOP你可以跳過(guò)它到后面的章節(jié))

默認(rèn)情況下,在application context中每一個(gè)切面都會(huì)有一個(gè)實(shí)例。AspectJ把這個(gè)叫做單例化模型。 也可以用其他的生命周期來(lái)定義切面:Spring支持AspectJ的 perthispertarget實(shí)例化模型(現(xiàn)在還不支持percflow、percflowbelowpertypewithin)。

一個(gè)"perthis" 切面通過(guò)在@Aspect注解中指定perthis 子句來(lái)聲明。讓我們先來(lái)看一個(gè)例子,然后解釋它是如何運(yùn)作的:

@Aspect("perthis(com.xyz.myapp.SystemArchitecture.businessService())")
public class MyAspect {

  private int someState;
	
  @Before(com.xyz.myapp.SystemArchitecture.businessService())
  public void recordServiceUsage() {
    // ...
  }
  	
}

這個(gè)'perthis'子句的效果是每個(gè)獨(dú)立的service對(duì)象執(zhí)行一個(gè)業(yè)務(wù)時(shí)都會(huì) 創(chuàng)建一個(gè)切面實(shí)例(切入點(diǎn)表達(dá)式所匹配的連接點(diǎn)上的每一個(gè)獨(dú)立的對(duì)象都會(huì)綁定到'this'上)。 在service對(duì)象上第一次調(diào)用方法的時(shí)候,切面實(shí)例將被創(chuàng)建。切面在service對(duì)象失效的同時(shí)失效。 在切面實(shí)例被創(chuàng)建前,所有的通知都不會(huì)被執(zhí)行,一旦切面對(duì)象創(chuàng)建完成, 定義的通知將會(huì)在匹配的連接點(diǎn)上執(zhí)行,但是只有當(dāng)service對(duì)象是和切面關(guān)聯(lián)的才可以。 請(qǐng)參閱 AspectJ 編程指南了解更多關(guān)于per-clauses的信息。

'pertarget'實(shí)例模型的跟“perthis”完全一樣,只不過(guò)是為每個(gè)匹配于連接點(diǎn) 的獨(dú)立目標(biāo)對(duì)象創(chuàng)建一個(gè)切面實(shí)例。

6.2.7.?例子

現(xiàn)在你已經(jīng)看到了每個(gè)獨(dú)立的部分是如何運(yùn)作的了,是時(shí)候把他們放到一起做一些有用的事情了!

因?yàn)椴l(fā)的問(wèn)題,有時(shí)候業(yè)務(wù)服務(wù)(business services)可能會(huì)失?。ɡ?,死鎖失?。H绻匦聡L試一下, 很有可能就會(huì)成功。對(duì)于業(yè)務(wù)服務(wù)來(lái)說(shuō),重試幾次是很正常的(Idempotent操作不需要用戶(hù)參與,否則會(huì)得出矛盾的結(jié)論) 我們可能需要透明的重試操作以避免客戶(hù)看到一個(gè)PessimisticLockingFailureException異常。 很明顯,在一個(gè)橫切多層的情況下,這是非常有必要的,因此通過(guò)切面來(lái)實(shí)現(xiàn)是很理想的。

因?yàn)槲覀兿胍卦嚥僮鳎覀儠?huì)需要使用到環(huán)繞通知,這樣我們就可以多次調(diào)用proceed()方法。 下面是簡(jiǎn)單的切面實(shí)現(xiàn):

@Aspect
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;
   }
   
   @Around("com.xyz.myapp.SystemArchitecture.businessService()")
   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;
   }

}

請(qǐng)注意切面實(shí)現(xiàn)了 Ordered 接口,這樣我們就可以把切面的優(yōu)先級(jí)設(shè)定為高于事務(wù)通知 (我們每次重試的時(shí)候都想要在一個(gè)全新的事務(wù)中進(jìn)行)。maxRetriesorder 屬性都可以在Spring中配置。主要的動(dòng)作在doConcurrentOperation這個(gè)環(huán)繞通知方法中發(fā)生。 請(qǐng)注意這個(gè)時(shí)候我們所有的businessService()方法都會(huì)使用這個(gè)重試策略。 我們首先會(huì)嘗試處理,如果得到一個(gè)PessimisticLockingFailureException異常, 我們僅僅重試直到耗盡所有預(yù)設(shè)的重試次數(shù)。

對(duì)應(yīng)的Spring配置如下:

<aop:aspectj-autoproxy/>

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

為改進(jìn)切面,使之僅僅重試idempotent操作,我們可以定義一個(gè) Idempotent注解:

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

并且對(duì)service操作的實(shí)現(xiàn)進(jìn)行注解。為了只重試idempotent操作,切面的修改只需要改寫(xiě)切入點(diǎn)表達(dá)式, 使得只匹配@Idempotent操作:

@Around("com.xyz.myapp.SystemArchitecture.businessService() && " + 
        "@annotation(com.xyz.myapp.service.Idempotent)")
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable { 
  ...	
}
上一篇: 下一篇: